User Manager service to manage users and query user details
Moved a bunch of methods from PackageManager to UserManager.
Fix launching of activities from recents to correct user.
Guest creation APIs
Change-Id: I0733405e6eb2829675665e225c759d6baa2b708f
diff --git a/Android.mk b/Android.mk
index 381989c..c2331f2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -139,6 +139,7 @@
core/java/android/os/IRemoteCallback.aidl \
core/java/android/os/ISchedulingPolicyService.aidl \
core/java/android/os/IUpdateLock.aidl \
+ core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
core/java/android/service/dreams/IDreamService.aidl \
diff --git a/api/current.txt b/api/current.txt
index d2f1e4e..9c6c437 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5386,6 +5386,7 @@
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String UI_MODE_SERVICE = "uimode";
field public static final java.lang.String USB_SERVICE = "usb";
+ field public static final java.lang.String USER_SERVICE = "user";
field public static final java.lang.String VIBRATOR_SERVICE = "vibrator";
field public static final java.lang.String WALLPAPER_SERVICE = "wallpaper";
field public static final java.lang.String WIFI_P2P_SERVICE = "wifip2p";
@@ -16448,6 +16449,11 @@
ctor public TransactionTooLargeException();
}
+ public class UserManager {
+ method public java.lang.String getUserName();
+ method public boolean supportsMultipleUsers();
+ }
+
public abstract class Vibrator {
method public abstract void cancel();
method public abstract boolean hasVibrator();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4cb5270..eb1f9a2 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -20,6 +20,7 @@
import android.app.ActivityManagerNative;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ContainerEncryptionParams;
import android.content.pm.FeatureInfo;
@@ -39,9 +40,11 @@
import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
+import android.os.IUserManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserManager;
import java.io.File;
import java.lang.reflect.Field;
@@ -59,6 +62,7 @@
public final class Pm {
IPackageManager mPm;
+ IUserManager mUm;
private WeakHashMap<String, Resources> mResourceCache
= new WeakHashMap<String, Resources>();
@@ -82,6 +86,7 @@
return;
}
+ mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
@@ -985,7 +990,7 @@
}
name = arg;
try {
- if (mPm.createUser(name, 0) == null) {
+ if (mUm.createUser(name, 0) == null) {
System.err.println("Error: couldn't create User.");
showUsage();
}
@@ -1017,7 +1022,7 @@
return;
}
try {
- if (!mPm.removeUser(userId)) {
+ if (!mUm.removeUser(userId)) {
System.err.println("Error: couldn't remove user.");
showUsage();
}
@@ -1034,7 +1039,7 @@
return;
}
try {
- List<UserInfo> users = mPm.getUsers();
+ List<UserInfo> users = mUm.getUsers();
if (users == null) {
System.err.println("Error: couldn't get users");
} else {
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index ba05ee7..935d647 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -51,6 +51,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserId;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -92,6 +93,7 @@
private final Context mContext;
private final PackageManager mPackageManager;
+ private UserManager mUserManager;
private HandlerThread mMessageThread;
private final MessageHandler mMessageHandler;
@@ -245,6 +247,13 @@
initUser(0);
}
+ private UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+ return mUserManager;
+ }
+
private UserAccounts initUser(int userId) {
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
@@ -382,12 +391,7 @@
}
private List<UserInfo> getAllUsers() {
- try {
- return AppGlobals.getPackageManager().getUsers();
- } catch (RemoteException re) {
- // Local to system process, shouldn't happen
- }
- return null;
+ return getUserManager().getUsers();
}
public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9a50a41..2face4c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -41,8 +41,8 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.content.pm.ManifestDigest;
+import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -1182,80 +1182,6 @@
return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
- // Multi-user support
-
- /**
- * @hide
- */
- @Override
- public UserInfo createUser(String name, int flags) {
- try {
- return mPM.createUser(name, flags);
- } catch (RemoteException e) {
- // Should never happen!
- }
- return null;
- }
-
- /**
- * @hide
- */
- @Override
- public List<UserInfo> getUsers() {
- try {
- return mPM.getUsers();
- } catch (RemoteException re) {
- ArrayList<UserInfo> users = new ArrayList<UserInfo>();
- UserInfo primary = new UserInfo(0, "Root!", null,
- UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
- users.add(primary);
- return users;
- }
- }
-
- /**
- * @hide
- */
- @Override
- public UserInfo getUser(int userId) {
- try {
- return mPM.getUser(userId);
- } catch (RemoteException re) {
- return null;
- }
- }
-
- /**
- * @hide
- */
- @Override
- public boolean removeUser(int id) {
- try {
- return mPM.removeUser(id);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * @hide
- */
- @Override
- public void setUserName(int id, String name) {
- try {
- mPM.setUserName(id, name);
- } catch (RemoteException re) {
- }
- }
-
- /**
- * @hide
- */
- @Override
- public void updateUserFlags(int id, int flags) {
- // TODO:
- }
-
/**
* @hide
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d886278..4496ce8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -80,6 +80,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IUserManager;
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
@@ -87,6 +88,7 @@
import android.os.ServiceManager;
import android.os.UserId;
import android.os.SystemVibrator;
+import android.os.UserManager;
import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
@@ -498,6 +500,13 @@
return WindowManagerImpl.getDefault().makeCompatible(
ctx.mPackageInfo.mCompatibilityInfo);
}});
+
+ registerService(USER_SERVICE, new ServiceFetcher() {
+ public Object getService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(USER_SERVICE);
+ IUserManager service = IUserManager.Stub.asInterface(b);
+ return new UserManager(ctx, service);
+ }});
}
static ContextImpl getImpl(Context context) {
@@ -918,6 +927,18 @@
(Activity)null, intent, -1, options);
}
+ /** @hide */
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, int userId) {
+ try {
+ ActivityManagerNative.getDefault().startActivityAsUser(
+ mMainThread.getApplicationThread(), intent,
+ intent.resolveTypeIfNeeded(getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, options, userId);
+ } catch (RemoteException re) {
+ }
+ }
+
@Override
public void startActivities(Intent[] intents) {
startActivities(intents, null);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5a7a989..bf60a96 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -890,6 +890,22 @@
public abstract void startActivity(Intent intent, Bundle options);
/**
+ * Same as {@link #startActivity(Intent, Bundle)}, but for a specific user. It requires holding
+ * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ * @param intent The description of the activity to start.
+ * @param options Additional options for how the Activity should be started.
+ * May be null if there are no options. See {@link android.app.ActivityOptions}
+ * for how to build the Bundle supplied here; there are no supported definitions
+ * for building it manually.
+ * @param userId The user id of the user to start this activity for.
+ * @throws ActivityNotFoundException
+ * @hide
+ */
+ public void startActivityAsUser(Intent intent, Bundle options, int userId) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Same as {@link #startActivities(Intent[], Bundle)} with no options
* specified.
*
@@ -2018,6 +2034,15 @@
public static final String SCHEDULING_POLICY_SERVICE = "scheduling_policy";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.os.UserManager} for managing users on devices that support multiple users.
+ *
+ * @see #getSystemService
+ * @see android.os.UserManager
+ */
+ public static final String USER_SERVICE = "user";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f007720..ff4c9a1 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -287,6 +287,12 @@
mBase.startActivity(intent, options);
}
+ /** @hide */
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, int userId) {
+ mBase.startActivityAsUser(intent, options, userId);
+ }
+
@Override
public void startActivities(Intent[] intents) {
mBase.startActivities(intents);
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index badcb03..e6303b9 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -53,6 +53,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserId;
+import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
@@ -206,16 +207,20 @@
// Use this as a random offset to seed all periodic syncs
private int mSyncRandomOffsetMillis;
+ private UserManager mUserManager;
+
private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
- private List<UserInfo> getAllUsers() {
- try {
- return AppGlobals.getPackageManager().getUsers();
- } catch (RemoteException re) {
- // Local to system process, shouldn't happen
+ private UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
- return null;
+ return mUserManager;
+ }
+
+ private List<UserInfo> getAllUsers() {
+ return getUserManager().getUsers();
}
private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0d87df5..22807a4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -358,11 +358,6 @@
boolean setInstallLocation(int loc);
int getInstallLocation();
- UserInfo createUser(in String name, int flags);
- boolean removeUser(int userId);
- void setUserName(int userId, String name);
- ParcelFileDescriptor setUserIcon(int userId);
-
void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
int flags, in String installerPackageName, in Uri verificationURI,
in ManifestDigest manifestDigest, in ContainerEncryptionParams encryptionParams);
@@ -373,9 +368,6 @@
boolean isFirstBoot();
- List<UserInfo> getUsers();
- UserInfo getUser(int userId);
-
void setPermissionEnforced(String permission, boolean enforced);
boolean isPermissionEnforced(String permission);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cd7ef0e..f287ca5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2616,56 +2616,6 @@
String packageName, IPackageMoveObserver observer, int flags);
/**
- * Creates a user with the specified name and options.
- *
- * @param name the user's name
- * @param flags flags that identify the type of user and other properties.
- * @see UserInfo
- *
- * @return the UserInfo object for the created user, or null if the user could not be created.
- * @hide
- */
- public abstract UserInfo createUser(String name, int flags);
-
- /**
- * @return the list of users that were created
- * @hide
- */
- public abstract List<UserInfo> getUsers();
-
- /**
- * @param id the ID of the user, where 0 is the primary user.
- * @hide
- */
- public abstract boolean removeUser(int id);
-
- /**
- * Updates the user's name.
- *
- * @param id the user's id
- * @param name the new name for the user
- * @hide
- */
- public abstract void setUserName(int id, String name);
-
- /**
- * Changes the user's properties specified by the flags.
- *
- * @param id the user's id
- * @param flags the new flags for the user
- * @hide
- */
- public abstract void updateUserFlags(int id, int flags);
-
- /**
- * Returns the details for the user specified by userId.
- * @param userId the user id of the user
- * @return UserInfo for the specified user, or null if no such user exists.
- * @hide
- */
- public abstract UserInfo getUser(int userId);
-
- /**
* Returns the device identity that verifiers can use to associate their scheme to a particular
* device. This should not be used by anything other than a package verifier.
*
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 68a7257..638e273 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -18,12 +18,17 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Parcelable.Creator;
/**
* Per-user information.
* @hide
*/
public class UserInfo implements Parcelable {
+
+ /** 6 bits for user type */
+ public static final int FLAG_MASK_USER_TYPE = 0x0000003F;
+
/**
* Primary user. Only one user can have this flag set. Meaning of this
* flag TBD.
@@ -41,6 +46,12 @@
*/
public static final int FLAG_GUEST = 0x00000004;
+ /**
+ * Indicates the user has restrictions in privileges, in addition to those for normal users.
+ * Exact meaning TBD. For instance, maybe they can't install apps or administer WiFi access pts.
+ */
+ public static final int FLAG_RESTRICTED = 0x00000008;
+
public int id;
public String name;
public String iconPath;
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
new file mode 100644
index 0000000..cb1b962
--- /dev/null
+++ b/core/java/android/os/IUserManager.aidl
@@ -0,0 +1,36 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+import android.os.ParcelFileDescriptor;
+import android.content.pm.UserInfo;
+
+/**
+ * {@hide}
+ */
+interface IUserManager {
+ UserInfo createUser(in String name, int flags);
+ boolean removeUser(int userHandle);
+ void setUserName(int userHandle, String name);
+ ParcelFileDescriptor setUserIcon(int userHandle);
+ List<UserInfo> getUsers();
+ UserInfo getUserInfo(int userHandle);
+ void setGuestEnabled(boolean enable);
+ boolean isGuestEnabled();
+ void wipeUser(int userHandle);
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
new file mode 100644
index 0000000..9c73392
--- /dev/null
+++ b/core/java/android/os/UserManager.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import com.android.internal.R;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Manages users and user details on a multi-user system.
+ */
+public class UserManager {
+
+ private static String TAG = "UserManager";
+ private final IUserManager mService;
+ private final Context mContext;
+
+ /** @hide */
+ public UserManager(Context context, IUserManager service) {
+ mService = service;
+ mContext = context;
+ }
+
+ /**
+ * Returns whether the system supports multiple users.
+ * @return true if multiple users can be created, false if it is a single user device.
+ */
+ public boolean supportsMultipleUsers() {
+ return getMaxSupportedUsers() > 1;
+ }
+
+ /**
+ * Returns the user handle for the user that this application is running for.
+ * @return the user handle of the user making this call.
+ * @hide
+ * */
+ public int getUserHandle() {
+ return Process.myUserHandle();
+ }
+
+ /**
+ * Returns the user name of the user making this call.
+ * @return the user name
+ */
+ public String getUserName() {
+ try {
+ return mService.getUserInfo(getUserHandle()).name;
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user name", re);
+ return "";
+ }
+ }
+
+ /**
+ * Returns the UserInfo object describing a specific user.
+ * @param userHandle the user handle of the user whose information is being requested.
+ * @return the UserInfo object for a specific user.
+ * @hide
+ * */
+ public UserInfo getUserInfo(int userHandle) {
+ try {
+ return mService.getUserInfo(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user info", re);
+ return null;
+ }
+ }
+
+ /**
+ * Creates a user with the specified name and options.
+ *
+ * @param name the user's name
+ * @param flags flags that identify the type of user and other properties.
+ * @see UserInfo
+ *
+ * @return the UserInfo object for the created user, or null if the user could not be created.
+ * @hide
+ */
+ public UserInfo createUser(String name, int flags) {
+ try {
+ return mService.createUser(name, flags);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not create a user", re);
+ return null;
+ }
+ }
+
+ /**
+ * Returns information for all users on this device.
+ * @return the list of users that were created.
+ * @hide
+ */
+ public List<UserInfo> getUsers() {
+ try {
+ return mService.getUsers();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user list", re);
+ return null;
+ }
+ }
+
+ /**
+ * Removes a user and all associated data.
+ * @param userHandle the integer handle of the user, where 0 is the primary user.
+ * @hide
+ */
+ public boolean removeUser(int userHandle) {
+ try {
+ return mService.removeUser(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not remove user ", re);
+ return false;
+ }
+ }
+
+ /**
+ * Updates the user's name.
+ *
+ * @param userHandle the user's integer handle
+ * @param name the new name for the user
+ * @hide
+ */
+ public void setUserName(int userHandle, String name) {
+ try {
+ mService.setUserName(userHandle, name);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set the user name ", re);
+ }
+ }
+
+ /**
+ * Returns a file descriptor for the user's photo. PNG data can be written into this file.
+ * @param userHandle the user for whom to change the photo.
+ * @return a {@link ParcelFileDescriptor} to which to write the photo.
+ * @hide
+ */
+ public ParcelFileDescriptor setUserIcon(int userHandle) {
+ try {
+ return mService.setUserIcon(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set the user icon ", re);
+ return null;
+ }
+ }
+
+ /**
+ * Enable or disable the use of a guest account. If disabled, the existing guest account
+ * will be wiped.
+ * @param enable whether to enable a guest account.
+ * @hide
+ */
+ public void setGuestEnabled(boolean enable) {
+ try {
+ mService.setGuestEnabled(enable);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not change guest account availability to " + enable);
+ }
+ }
+
+ /**
+ * Checks if a guest user is enabled for this device.
+ * @return whether a guest user is enabled
+ * @hide
+ */
+ public boolean isGuestEnabled() {
+ try {
+ return mService.isGuestEnabled();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not retrieve guest enabled state");
+ return false;
+ }
+ }
+
+ /**
+ * Wipes all the data for a user, but doesn't remove the user.
+ * @param userHandle
+ * @hide
+ */
+ public void wipeUser(int userHandle) {
+ try {
+ mService.wipeUser(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not wipe user " + userHandle);
+ }
+ }
+
+ /**
+ * Returns the maximum number of users that can be created on this device. A return value
+ * of 1 means that it is a single user device.
+ * @hide
+ * @return a value greater than or equal to 1
+ */
+ public int getMaxSupportedUsers() {
+ return mContext.getResources().getInteger(R.integer.config_multiuserMaximumUsers);
+ }
+}
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 2411cec..a1f6735 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -32,6 +32,7 @@
import android.os.Binder;
import android.os.Process;
import android.os.UserId;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
@@ -77,7 +78,8 @@
Searchables searchables = mSearchables.get(userId);
long origId = Binder.clearCallingIdentity();
- boolean userExists = mContext.getPackageManager().getUser(userId) != null;
+ boolean userExists = ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
+ .getUserInfo(userId) != null;
Binder.restoreCallingIdentity(origId);
if (searchables == null && userExists) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6ab323e..ee0ff8e 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -890,4 +890,7 @@
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
<bool name="config_cellBroadcastAppLinks">false</bool>
+
+ <!-- Maximum number of supported users -->
+ <integer name="config_multiuserMaximumUsers">10</integer>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5b86db5..38a431f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -280,6 +280,7 @@
<java-symbol type="integer" name="config_wifi_driver_stop_delay" />
<java-symbol type="integer" name="config_soundEffectVolumeDb" />
<java-symbol type="integer" name="config_lockSoundVolumeDb" />
+ <java-symbol type="integer" name="config_multiuserMaximumUsers" />
<java-symbol type="color" name="tab_indicator_text_v4" />
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index cb69660..bb647c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -36,6 +36,7 @@
import android.net.Uri;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserId;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
@@ -789,7 +790,7 @@
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
| Intent.FLAG_ACTIVITY_NEW_TASK);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
- context.startActivity(intent, opts.toBundle());
+ context.startActivityAsUser(intent, opts.toBundle(), UserId.USER_CURRENT);
}
if (usingDrawingCache) {
holder.thumbnailViewImage.setDrawingCacheEnabled(false);
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 6c50f1c..a0e3e3c 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
@@ -278,7 +279,8 @@
mItems.add(mSilentModeAction);
}
- List<UserInfo> users = mContext.getPackageManager().getUsers();
+ List<UserInfo> users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
+ .getUsers();
if (users.size() > 1) {
UserInfo currentUser;
try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 96cf3d3..e8350c1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -55,6 +55,7 @@
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.UserManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.usb.UsbService;
@@ -192,6 +193,11 @@
}
ActivityManagerService.setSystemProcess();
+
+ Slog.i(TAG, "User Service");
+ ServiceManager.addService(Context.USER_SERVICE,
+ UserManagerService.getInstance(context));
+
mContentResolver = context.getContentResolver();
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index df6c51e..533c2cd 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -1835,7 +1835,7 @@
pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ List<UserInfo> users = mAm.getUserManager().getUsers();
for (UserInfo user : users) {
if (mServiceMap.getAllServices(user.id).size() > 0) {
boolean printed = false;
@@ -1913,8 +1913,8 @@
needSep = printed;
}
}
- } catch (RemoteException re) {
-
+ } catch (Exception e) {
+ Log.w(TAG, "Exception in dumpServicesLocked: " + e);
}
if (mPendingServices.size() > 0) {
@@ -2028,16 +2028,13 @@
int opti, boolean dumpAll) {
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ List<UserInfo> users = mAm.getUserManager().getUsers();
if ("all".equals(name)) {
synchronized (this) {
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
- services.add(r1);
- }
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ services.add(r1);
}
- } catch (RemoteException re) {
}
}
} else {
@@ -2055,24 +2052,20 @@
}
synchronized (this) {
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- services.add(r1);
- }
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- services.add(r1);
- }
- } else if (System.identityHashCode(r1) == objectId) {
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
services.add(r1);
}
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ services.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ services.add(r1);
}
}
- } catch (RemoteException re) {
}
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5bed015..d3ec9f7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -27,6 +27,7 @@
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
import dalvik.system.Zygote;
@@ -76,12 +77,12 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -111,6 +112,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserId;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.format.Time;
import android.util.EventLog;
@@ -778,6 +780,10 @@
static ActivityManagerService mSelf;
static ActivityThread mSystemThread;
+ private int mCurrentUserId;
+ private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+ private UserManager mUserManager;
+
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
@@ -4197,19 +4203,14 @@
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
- null, null, 0, null, null,
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID, user.id);
- }
- } catch (RemoteException re) {
- // Won't happen, in same process
+ List<UserInfo> users = getUserManager().getUsers();
+ for (UserInfo user : users) {
+ broadcastIntentLocked(null, null,
+ new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, user.id);
}
-
}
}
}
@@ -13358,9 +13359,6 @@
// Multi-user methods
- private int mCurrentUserId;
- private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
-
public boolean switchUser(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.myUid()) {
@@ -13403,7 +13401,7 @@
Slog.e(TAG, "Trying to get user from unauthorized app");
return null;
}
- return AppGlobals.getPackageManager().getUser(mCurrentUserId);
+ return getUserManager().getUserInfo(mCurrentUserId);
}
private void onUserRemoved(Intent intent) {
@@ -13431,14 +13429,15 @@
}
private boolean userExists(int userId) {
- try {
- UserInfo user = AppGlobals.getPackageManager().getUser(userId);
- return user != null;
- } catch (RemoteException re) {
- // Won't happen, in same process
- }
+ UserInfo user = getUserManager().getUserInfo(userId);
+ return user != null;
+ }
- return false;
+ UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+ return mUserManager;
}
private void checkValidCaller(int uid, int userId) {
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 7e5f8cf..46c24b0 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -113,6 +113,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserId;
+import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
@@ -1707,7 +1708,8 @@
}
private void updateRulesForAppLocked(int appId) {
- for (UserInfo user : mContext.getPackageManager().getUsers()) {
+ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ for (UserInfo user : um.getUsers()) {
final int uid = UserId.getUid(user.id, appId);
updateRulesForUidLocked(uid);
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 180081b..f257203 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -68,6 +68,7 @@
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.UserInfo;
import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
@@ -77,7 +78,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
-import android.content.pm.UserInfo;
import android.content.pm.ManifestDigest;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
@@ -101,6 +101,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserId;
+import android.os.UserManager;
import android.provider.Settings.Secure;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -420,7 +421,7 @@
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
- static UserManager sUserManager;
+ static UserManagerService sUserManager;
// Stores a list of users whose package restrictions file needs to be updated
private HashSet<Integer> mDirtyUsers = new HashSet<Integer>();
@@ -899,7 +900,7 @@
mOnlyCore = onlyCore;
mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
- mSettings = new Settings();
+ mSettings = new Settings(context);
mSettings.addSharedUserLPw("android.uid.system",
Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM);
@@ -942,11 +943,12 @@
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- sUserManager = new UserManager(mInstaller, mUserAppDataDir);
+ sUserManager = UserManagerService.getInstance(context);
+ sUserManager.setInstaller(this, mInstaller);
readPermissions();
- mRestoredSettings = mSettings.readLPw(getUsers());
+ mRestoredSettings = mSettings.readLPw(sUserManager.getUsers());
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -9459,48 +9461,16 @@
PackageHelper.APP_INSTALL_AUTO);
}
- public UserInfo createUser(String name, int flags) {
- // TODO(kroot): Add a real permission for creating users
- enforceSystemOrRoot("Only the system can create users");
-
- UserInfo userInfo = sUserManager.createUser(name, flags);
- if (userInfo != null) {
- Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
- }
- return userInfo;
- }
-
- public boolean removeUser(int userId) {
- // TODO(kroot): Add a real permission for removing users
- enforceSystemOrRoot("Only the system can remove users");
-
- if (userId == 0 || !sUserManager.exists(userId)) {
- return false;
- }
-
- cleanUpUser(userId);
-
- if (sUserManager.removeUser(userId)) {
- // Let other services shutdown any activity
- Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
- addedIntent.putExtra(Intent.EXTRA_USERID, userId);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
- }
- sUserManager.removePackageFolders(userId);
- return true;
- }
-
- private void cleanUpUser(int userId) {
+ /** Called by UserManagerService */
+ void cleanUpUser(int userHandle) {
// Disable all the packages for the user first
synchronized (mPackages) {
Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
for (Entry<String, PackageSetting> entry : entries) {
- entry.getValue().removeUser(userId);
+ entry.getValue().removeUser(userHandle);
}
- if (mDirtyUsers.remove(userId));
- mSettings.removeUserLPr(userId);
+ if (mDirtyUsers.remove(userHandle));
+ mSettings.removeUserLPr(userHandle);
}
}
@@ -9516,30 +9486,6 @@
}
@Override
- public List<UserInfo> getUsers() {
- enforceSystemOrRoot("Only the system can query users");
- return sUserManager.getUsers();
- }
-
- @Override
- public UserInfo getUser(int userId) {
- enforceSystemOrRoot("Only the system can query user");
- return sUserManager.getUser(userId);
- }
-
- @Override
- public void setUserName(int userId, String name) {
- enforceSystemOrRoot("Only the system can rename users");
- sUserManager.setUserName(userId, name);
- }
-
- @Override
- public ParcelFileDescriptor setUserIcon(int userId) {
- enforceSystemOrRoot("Only the system can update users");
- return sUserManager.setUserIcon(userId);
- }
-
- @Override
public void setPermissionEnforced(String permission, boolean enforced) {
mContext.enforceCallingOrSelfPermission(GRANT_REVOKE_PERMISSIONS, null);
if (READ_EXTERNAL_STORAGE.equals(permission)) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index def1696..add91d3 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -34,6 +34,7 @@
import android.app.AppGlobals;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
@@ -79,6 +80,7 @@
private static final String TAG = "PackageSettings";
private static final boolean DEBUG_STOPPED = false;
+ private static final boolean DEBUG_MU = false;
private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
private static final String ATTR_ENFORCEMENT = "enforcement";
@@ -173,12 +175,15 @@
*/
private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>();
+ private final Context mContext;
+
private final File mSystemDir;
- Settings() {
- this(Environment.getDataDirectory());
+ Settings(Context context) {
+ this(context, Environment.getDataDirectory());
}
- Settings(File dataDir) {
+ Settings(Context context, File dataDir) {
+ mContext = context;
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
@@ -739,6 +744,9 @@
}
void readPackageRestrictionsLPr(int userId) {
+ if (DEBUG_MU) {
+ Log.i(TAG, "Reading package restrictions for user=" + userId);
+ }
FileInputStream str = null;
File userPackagesStateFile = getUserPackagesStateFile(userId);
File backupFile = getUserPackagesStateBackupFile(userId);
@@ -891,6 +899,9 @@
}
void writePackageRestrictionsLPr(int userId) {
+ if (DEBUG_MU) {
+ Log.i(TAG, "Writing package restrictions for user=" + userId);
+ }
// Keep the old stopped packages around until we know the new ones have
// been successfully written.
File userPackagesStateFile = getUserPackagesStateFile(userId);
@@ -935,6 +946,7 @@
boolean stopped = pkg.getStopped(userId);
boolean notLaunched = pkg.getNotLaunched(userId);
int enabled = pkg.getEnabled(userId);
+ if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + enabled);
HashSet<String> enabledComponents = pkg.getEnabledComponents(userId);
HashSet<String> disabledComponents = pkg.getDisabledComponents(userId);
@@ -1584,7 +1596,24 @@
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+ }
+ if (mBackupStoppedPackagesFilename.exists()
+ || mStoppedPackagesFilename.exists()) {
+ // Read old file
+ readStoppedLPw();
+ mBackupStoppedPackagesFilename.delete();
+ mStoppedPackagesFilename.delete();
+ // Migrate to new file format
+ writePackageRestrictionsLPr(0);
+ } else {
+ if (users == null) {
+ readPackageRestrictionsLPr(0);
+ } else {
+ for (UserInfo user : users) {
+ readPackageRestrictionsLPr(user.id);
+ }
+ }
}
final int N = mPendingPackages.size();
@@ -1628,23 +1657,6 @@
}
}
- if (mBackupStoppedPackagesFilename.exists()
- || mStoppedPackagesFilename.exists()) {
- // Read old file
- readStoppedLPw();
- mBackupStoppedPackagesFilename.delete();
- mStoppedPackagesFilename.delete();
- // Migrate to new file format
- writePackageRestrictionsLPr(0);
- } else {
- if (users == null) {
- readPackageRestrictionsLPr(0);
- } else {
- for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
- }
- }
- }
mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
+ mSharedUsers.size() + " shared uids\n");
@@ -2378,9 +2390,7 @@
private List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
- return AppGlobals.getPackageManager().getUsers();
- } catch (RemoteException re) {
- // Local to system process, shouldn't happen
+ return UserManagerService.getInstance(mContext).getUsers();
} catch (NullPointerException npe) {
// packagemanager not yet initialized
} finally {
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManagerService.java
similarity index 78%
rename from services/java/com/android/server/pm/UserManager.java
rename to services/java/com/android/server/pm/UserManagerService.java
index 738ab08..b55dd24 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -22,12 +22,17 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.SystemClock;
import android.os.UserId;
import android.util.Log;
@@ -48,9 +53,9 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-public class UserManager {
+public class UserManagerService extends IUserManager.Stub {
- private static final String TAG = "UserManager";
+ private static final String TAG = "UserManagerService";
private static final String TAG_NAME = "name";
@@ -75,14 +80,25 @@
private final File mUsersDir;
private final File mUserListFile;
private int[] mUserIds;
+ private boolean mGuestEnabled;
private Installer mInstaller;
private File mBaseUserPath;
+ private Context mContext;
+ private static UserManagerService sInstance;
+ private PackageManagerService mPm;
+
+ public synchronized static UserManagerService getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new UserManagerService(context);
+ }
+ return sInstance;
+ }
/**
* Available for testing purposes.
*/
- UserManager(File dataDir, File baseUserPath) {
+ UserManagerService(File dataDir, File baseUserPath) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
// Make zeroth user directory, for services to migrate their files to that location
@@ -97,12 +113,19 @@
readUserList();
}
- public UserManager(Installer installer, File baseUserPath) {
- this(Environment.getDataDirectory(), baseUserPath);
- mInstaller = installer;
+ public UserManagerService(Context context) {
+ this(Environment.getDataDirectory(), new File(Environment.getDataDirectory(), "user"));
+ mContext = context;
}
+ void setInstaller(PackageManagerService pm, Installer installer) {
+ mInstaller = installer;
+ mPm = pm;
+ }
+
+ @Override
public List<UserInfo> getUsers() {
+ enforceSystemOrRoot("Only the system can query users");
synchronized (mUsers) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
for (int i = 0; i < mUsers.size(); i++) {
@@ -112,7 +135,9 @@
}
}
- public UserInfo getUser(int userId) {
+ @Override
+ public UserInfo getUserInfo(int userId) {
+ enforceSystemOrRoot("Only the system can query user");
synchronized (mUsers) {
UserInfo info = mUsers.get(userId);
return info;
@@ -125,7 +150,9 @@
}
}
+ @Override
public void setUserName(int userId, String name) {
+ enforceSystemOrRoot("Only the system can rename users");
synchronized (mUsers) {
UserInfo info = mUsers.get(userId);
if (name != null && !name.equals(info.name)) {
@@ -135,7 +162,9 @@
}
}
+ @Override
public ParcelFileDescriptor setUserIcon(int userId) {
+ enforceSystemOrRoot("Only the system can update users");
synchronized (mUsers) {
UserInfo info = mUsers.get(userId);
if (info == null) return null;
@@ -147,6 +176,57 @@
}
}
+ @Override
+ public void setGuestEnabled(boolean enable) {
+ enforceSystemOrRoot("Only the system can enable guest users");
+ synchronized (mUsers) {
+ if (mGuestEnabled != enable) {
+ mGuestEnabled = enable;
+ // Erase any guest user that currently exists
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ if (user.isGuest()) {
+ if (!enable) {
+ removeUser(user.id);
+ }
+ return;
+ }
+ }
+ // No guest was found
+ if (enable) {
+ createUser("Guest", UserInfo.FLAG_GUEST);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isGuestEnabled() {
+ synchronized (mUsers) {
+ return mGuestEnabled;
+ }
+ }
+
+ @Override
+ public void wipeUser(int userHandle) {
+ enforceSystemOrRoot("Only the system can wipe users");
+ // TODO:
+ }
+
+ /**
+ * Enforces that only the system UID or root's UID can call a method exposed
+ * via Binder.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ */
+ private static final void enforceSystemOrRoot(String message) {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID && uid != 0) {
+ throw new SecurityException(message);
+ }
+ }
+
private ParcelFileDescriptor updateIconBitmapLocked(UserInfo info) {
try {
File dir = new File(mUsersDir, Integer.toString(info.id));
@@ -184,6 +264,7 @@
}
private void readUserListLocked() {
+ mGuestEnabled = false;
if (!mUserListFile.exists()) {
fallbackToSingleUserLocked();
return;
@@ -212,6 +293,9 @@
if (user != null) {
mUsers.put(user.id, user);
}
+ if (user.isGuest()) {
+ mGuestEnabled = true;
+ }
}
}
updateUserIdsLocked();
@@ -389,7 +473,9 @@
return null;
}
+ @Override
public UserInfo createUser(String name, int flags) {
+ enforceSystemOrRoot("Only the system can create users");
int userId = getNextAvailableId();
UserInfo userInfo = new UserInfo(userId, name, null, flags);
File userPath = new File(mBaseUserPath, Integer.toString(userId));
@@ -402,6 +488,11 @@
writeUserLocked(userInfo);
updateUserIdsLocked();
}
+ if (userInfo != null) {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ }
return userInfo;
}
@@ -410,27 +501,37 @@
* after the user's processes have been terminated.
* @param id the user's id
*/
- public boolean removeUser(int id) {
+ public boolean removeUser(int userHandle) {
+ enforceSystemOrRoot("Only the system can remove users");
synchronized (mUsers) {
- return removeUserLocked(id);
+ return removeUserLocked(userHandle);
}
}
- private boolean removeUserLocked(int id) {
- // Remove from the list
- UserInfo userInfo = mUsers.get(id);
- if (userInfo != null) {
- // Remove this user from the list
- mUsers.remove(id);
- // Remove user file
- File userFile = new File(mUsersDir, id + ".xml");
- userFile.delete();
- // Update the user list
- writeUserListLocked();
- updateUserIdsLocked();
- return true;
+ private boolean removeUserLocked(int userHandle) {
+ final UserInfo user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
}
- return false;
+
+ mPm.cleanUpUser(userHandle);
+
+ // Remove this user from the list
+ mUsers.remove(userHandle);
+ // Remove user file
+ File userFile = new File(mUsersDir, userHandle + ".xml");
+ userFile.delete();
+ // Update the user list
+ writeUserListLocked();
+ updateUserIdsLocked();
+
+ // Let other services shutdown any activity
+ Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userHandle);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+
+ removePackageFolders(userHandle);
+ return true;
}
public void installPackageForAllUsers(String packageName, int uid) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 416900f8..f6f9aa0 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -176,13 +176,6 @@
return info;
}
- @Override
- public List<UserInfo> getUsers() {
- final ArrayList<UserInfo> users = new ArrayList<UserInfo>();
- users.add(new UserInfo(USER_ID, "Primary", null, UserInfo.FLAG_PRIMARY));
- users.add(new UserInfo(USER_ID_GUEST, "Guest", 0));
- return users;
- }
};
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 796372d..5f93e6f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -138,7 +138,7 @@
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir());
assertEquals(true, settings.readLPw(null));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -156,11 +156,11 @@
public void testNewPackageRestrictionsFile() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir());
assertEquals(true, settings.readLPw(null));
// Create Settings again to make it read from the new files
- settings = new Settings(getContext().getFilesDir());
+ settings = new Settings(getContext(), getContext().getFilesDir());
assertEquals(true, settings.readLPw(null));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
@@ -171,7 +171,7 @@
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir());
assertEquals(true, settings.readLPw(null));
// Enable/Disable a package
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index d736ac1..bc3649c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,7 +16,7 @@
package com.android.server.pm;
-import com.android.server.pm.UserManager;
+import com.android.server.pm.UserManagerService;
import android.content.pm.UserInfo;
import android.os.Debug;
@@ -25,14 +25,14 @@
import java.util.List;
-/** Test {@link UserManager} functionality. */
+/** Test {@link UserManagerService} functionality. */
public class UserManagerTest extends AndroidTestCase {
- UserManager mUserManager = null;
+ UserManagerService mUserManager = null;
@Override
public void setUp() throws Exception {
- mUserManager = new UserManager(Environment.getExternalStorageDirectory(),
+ mUserManager = new UserManagerService(Environment.getExternalStorageDirectory(),
Environment.getExternalStorageDirectory());
}
@@ -52,7 +52,7 @@
}
public void testAddUser() throws Exception {
- final UserManager details = mUserManager;
+ final UserManagerService details = mUserManager;
UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
assertTrue(userInfo != null);
@@ -71,7 +71,7 @@
}
public void testAdd2Users() throws Exception {
- final UserManager details = mUserManager;
+ final UserManagerService details = mUserManager;
UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
@@ -85,7 +85,7 @@
}
public void testRemoveUser() throws Exception {
- final UserManager details = mUserManager;
+ final UserManagerService details = mUserManager;
UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 0399b3b..ef14404 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -513,54 +513,6 @@
* @hide
*/
@Override
- public UserInfo createUser(String name, int flags) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
- public List<UserInfo> getUsers() {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
- public UserInfo getUser(int userId) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
- public boolean removeUser(int id) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
- public void setUserName(int id, String name) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
- public void updateUserFlags(int id, int flags) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @hide
- */
- @Override
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {