[Android Software Development] ramdroid77:
Like we all know Google has decided that multi user support is only needed on tablet devices. I don't agree with Google here so right after I've got Android 4.2 on my Galaxy Nexus I started to do some research to get multi user support enabled. My goal was to get a generic app published (probably it needs root access) that would allow to enable multi user support on any Android 4.2 phone.

After some research I figured out this is not possible. Multi user support is deeply integrated into the OS and the only way to get it enabled is by modding a custom ROM. In my investigation I started looking at the AOSP source code. There are two entries points that are relevant: the new UserManager API checks if multi users are supported on the device and KeyguardHostView allows to switch users on the lock screen.

UserManager

If you don't already have the whole AOSP available then you can only download this repository:

git clone https://android.googlesource.com/platform/frameworks/base

base/core/java/android/os --> UserManager.java --> function getMaxSupportedUser():

SystemProperties.getInt("fw.max_users",

Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers))

This can be achieved quite easily. Open a shell and type:

su

setprop fw.max_users 5 (actually any value > 1 works)

Now the user menu is visible in Android settings. Here you can choose "Create new user" just like you do on your Nexus 7. A new user account is created and your phone is running in the new user account. In the QuickSettings notification bar you go back to your lock screen. However the icons to switch users are missing. So let's look at the lock screen....

KeyGuard

Download the repository:

git clone https://android.googlesource.com/platform/frameworks/policies/base

base/policy/src/com/android/internal/policy/impl/keyguard --> KeyguardHostView.java

Actually what happens here is that KeyboardHostView is trying to add the KeyguardMultiUserSelectorView layout into the lock screen if the number of users is greater than one. Unfortunately we will see an exception in logcat because layout R.id.keyguard_user_selector is simply not included in phone layouts. Probably we could change build.prop to enable tablet mode and get it enabled, but that's not really what we want to do.

Now the idea is to develop a lock screen widget that would simulate the KeyboardHostView. The app probably needs to be installed as system app so it's available for all users. So let's go ahead and look what exactly is happening...

First the list of users is retrieved:

UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);

um.getUserList()

This user list is added to KeyguardMultiUserSelectorView:

base/policy/src/com/android/internal/policy/impl/keyguard --> KeyguardMultiUserSelectorView.java

In order to finally switch users it just calls:

ActivityManagerNative.getDefault().switchUser(avatar.getUserInfo().id);

Doesn't really look like rocket science! Let's try to reproduce this in our own app....

Implementation

The UserInfo class (which is returned in a List in getUserList) is not included in Android SDK. Copy into your project from AOSP sources:

base/core/java/android/content/pm/UserInfo.java

Some little hack for a hidden constructor:

public UserHandle getUserHandle() {

// Constructor UserHandle(int) is hidden, use public constructor instead

Parcel pId = Parcel.obtain();

pId.writeInt(id);

return new UserHandle(pId);

}

Oh well, the UserManager.getUserList() is hidden. Use reflection to retrieve the user list:

List<UserInfo> users = new ArrayList<UserInfo>();

final UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);

Method getUsers = userManager.getClass().getMethod("getUsers", null);

users = (List<UserInfo>) getUsers.invoke(userManager, null);

ActivityManagerNative is nowhere available. Use reflection to get an instance:

Class activityManagerNative = classLoader.loadClass("android.app.ActivityManagerNative");

Method getDefault = activityManagerNative.getMethod("getDefault", null);

Object instanceActivityManagerNative = getDefault.invoke(null, null);

Too lazy to figure out the proper call to getMethod, so just search for switchUser method:

Method switchUser = null;

Method methods[] = instanceActivityManagerNative.getClass().getDeclaredMethods();

for (Method m : methods) {

if (m.getName().equals("switchUser")) {

switchUser = m;

}

}

Call switch user with some ID that we retrieved from the user list:

switchUser.invoke(instanceActivityManagerNative, new Object[] { users.get(1).id } );

Executing this code will crash because we are missing some permissions....

Permissions

android.permission.MANAGE_USERS is needed for getUserList()

This permission has android:protectionLevel="signature|system"

--> Install app in system partition (we'd like to do this anyway) and the permission is granted

android.permission.INTERACT_ACROSS_USERS_FULL is needed for switchUser(). However this permission has android:protectionLevel="signature".

Here we can already stop because the app needs to be signed with platform key in order to get this permission. Sadly there's no way we can do this on devices with a retail firmware. Only way to get this permission is by building our own ROM from AOSP.

Besides, the alternative permission android.permission.INTERACT_ACROSS_USERS only has android:protectionLevel="signature|system" but it seems this is not good enough for switchUser()....

Implementation in Custom ROMs

As we can directly modify the framework files this shouldn't be too much effort:

--> set the fw.max_users system property to a value > 1

--> copy the KeyguardMultiUserSelectorView layout from a tablet device (R.id.keyguard_user_selector)

I don't really have the time and motivation to maintain any custom ROMs so I'm stopping here.