Don't call DPM from UserManager to avoid lock inversion
- Also make sure DPMS.mOwners is always guarded with DPMS.this.
(and remove synchronization from Owners.)
Bug 25796840
Change-Id: I83f7b78e7b437d9c2a2b1d6e714346cd15f95330
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ff829ff..424b902 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -24,7 +24,6 @@
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -216,14 +215,15 @@
private final SparseArray<Bundle> mAppliedUserRestrictions = new SparseArray<>();
/**
- * User restrictions set by {@link DevicePolicyManager} that should be applied to all users,
- * including guests.
+ * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
+ * that should be applied to all users, including guests.
*/
@GuardedBy("mRestrictionsLock")
private Bundle mDevicePolicyGlobalUserRestrictions;
/**
- * User restrictions set by {@link DevicePolicyManager} for each user.
+ * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
+ * for each user.
*/
@GuardedBy("mRestrictionsLock")
private final SparseArray<Bundle> mDevicePolicyLocalUserRestrictions = new SparseArray<>();
@@ -248,6 +248,12 @@
private final LocalService mLocalService;
+ @GuardedBy("mUsersLock")
+ private boolean mIsDeviceManaged;
+
+ @GuardedBy("mUsersLock")
+ private final SparseBooleanArray mIsUserManaged = new SparseBooleanArray();
+
@GuardedBy("mUserRestrictionsListeners")
private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners =
new ArrayList<>();
@@ -509,11 +515,9 @@
if (!userInfo.isAdmin()) {
return false;
}
+ // restricted profile can be created if there is no DO set and the admin user has no PO;
+ return !mIsDeviceManaged && !mIsUserManaged.get(userId);
}
- DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- // restricted profile can be created if there is no DO set and the admin user has no PO
- return !dpm.isDeviceManaged() && dpm.getProfileOwnerAsUser(userId) == null;
}
/*
@@ -1596,11 +1600,10 @@
if (UserManager.isSplitSystemUser()
&& !isGuest && !isManagedProfile && getPrimaryUser() == null) {
flags |= UserInfo.FLAG_PRIMARY;
- DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
- mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (devicePolicyManager == null
- || !devicePolicyManager.isDeviceManaged()) {
- flags |= UserInfo.FLAG_ADMIN;
+ synchronized (mUsersLock) {
+ if (!mIsDeviceManaged) {
+ flags |= UserInfo.FLAG_ADMIN;
+ }
}
}
userId = getNextAvailableId();
@@ -1865,6 +1868,13 @@
// Remove this user from the list
synchronized (mUsersLock) {
mUsers.remove(userHandle);
+ mIsUserManaged.delete(userHandle);
+ }
+ synchronized (mRestrictionsLock) {
+ mBaseUserRestrictions.remove(userHandle);
+ mAppliedUserRestrictions.remove(userHandle);
+ mCachedEffectiveUserRestrictions.remove(userHandle);
+ mDevicePolicyLocalUserRestrictions.remove(userHandle);
}
// Remove user file
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
@@ -2376,9 +2386,10 @@
if (user == null) {
continue;
}
+ final int userId = user.id;
pw.print(" "); pw.print(user);
pw.print(" serialNo="); pw.print(user.serialNumber);
- if (mRemovingUserIds.get(mUsers.keyAt(i))) {
+ if (mRemovingUserIds.get(userId)) {
pw.print(" <removing> ");
}
if (user.partial) {
@@ -2403,6 +2414,8 @@
sb.append(" ago");
pw.println(sb);
}
+ pw.print(" Has profile owner: ");
+ pw.println(mIsUserManaged.get(userId));
pw.println(" Restrictions:");
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.dumpRestrictions(
@@ -2427,6 +2440,10 @@
synchronized (mGuestRestrictions) {
UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions);
}
+ synchronized (mUsersLock) {
+ pw.println();
+ pw.println(" Device managed: " + mIsDeviceManaged);
+ }
}
}
@@ -2507,6 +2524,20 @@
mUserRestrictionsListeners.remove(listener);
}
}
+
+ @Override
+ public void setDeviceManaged(boolean isManaged) {
+ synchronized (mUsersLock) {
+ mIsDeviceManaged = isManaged;
+ }
+ }
+
+ @Override
+ public void setUserManaged(int userId, boolean isManaged) {
+ synchronized (mUsersLock) {
+ mIsUserManaged.put(userId, isManaged);
+ }
+ }
}
private class Shell extends ShellCommand {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f80a611..8199250 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1071,7 +1071,7 @@
}
Owners newOwners() {
- return new Owners(mContext);
+ return new Owners(mContext, getUserManager(), getUserManagerInternal());
}
UserManager getUserManager() {
@@ -2150,20 +2150,24 @@
}
private void ensureDeviceOwnerUserStarted() {
- if (mOwners.hasDeviceOwner()) {
- final int userId = mOwners.getDeviceOwnerUserId();
- if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
+ final int userId;
+ synchronized (this) {
+ if (!mOwners.hasDeviceOwner()) {
+ return;
}
- if (userId != UserHandle.USER_SYSTEM) {
- try {
- mInjector.getIActivityManager().startUserInBackground(userId);
+ userId = mOwners.getDeviceOwnerUserId();
+ }
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
+ }
+ if (userId != UserHandle.USER_SYSTEM) {
+ try {
+ mInjector.getIActivityManager().startUserInBackground(userId);
- // STOPSHIP Prevent the DO user from being killed.
+ // STOPSHIP Prevent the DO user from being killed.
- } catch (RemoteException e) {
- Slog.w(LOG_TAG, "Exception starting user", e);
- }
+ } catch (RemoteException e) {
+ Slog.w(LOG_TAG, "Exception starting user", e);
}
}
}
@@ -4584,7 +4588,7 @@
+ " for device owner");
}
synchronized (this) {
- enforceCanSetDeviceOwner(userId);
+ enforceCanSetDeviceOwnerLocked(userId);
// Shutting down backup manager service permanently.
long ident = mInjector.binderClearCallingIdentity();
@@ -4751,7 +4755,7 @@
+ " not installed for userId:" + userHandle);
}
synchronized (this) {
- enforceCanSetProfileOwner(userHandle);
+ enforceCanSetProfileOwnerLocked(userHandle);
mOwners.setProfileOwner(who, ownerName, userHandle);
mOwners.writeProfileOwner(userHandle);
return true;
@@ -4953,7 +4957,7 @@
* - SYSTEM_UID
* - adb if there are not accounts.
*/
- private void enforceCanSetProfileOwner(int userHandle) {
+ private void enforceCanSetProfileOwnerLocked(int userHandle) {
UserInfo info = mUserManager.getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
@@ -4995,7 +4999,7 @@
* The device owner can only be set before the setup phase of the primary user has completed,
* except for adb if no accounts or additional users are present on the device.
*/
- private void enforceCanSetDeviceOwner(int userId) {
+ private void enforceCanSetDeviceOwnerLocked(int userId) {
if (mOwners.hasDeviceOwner()) {
throw new IllegalStateException("Trying to set the device owner, but device owner "
+ "is already set.");
@@ -6819,20 +6823,22 @@
public boolean isProvisioningAllowed(String action) {
final int callingUserId = mInjector.userHandleGetCallingUserId();
if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
- if (mOwners.hasDeviceOwner()) {
- if (!mInjector.userManagerIsSplitSystemUser()) {
- // Only split-system-user systems support managed-profiles in combination with
- // device-owner.
- return false;
- }
- if (mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM) {
- // Only system device-owner supports managed-profiles. Non-system device-owner
- // doesn't.
- return false;
- }
- if (callingUserId == UserHandle.USER_SYSTEM) {
- // Managed-profiles cannot be setup on the system user, only regular users.
- return false;
+ synchronized (this) {
+ if (mOwners.hasDeviceOwner()) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ // Only split-system-user systems support managed-profiles in combination with
+ // device-owner.
+ return false;
+ }
+ if (mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM) {
+ // Only system device-owner supports managed-profiles. Non-system device-owner
+ // doesn't.
+ return false;
+ }
+ if (callingUserId == UserHandle.USER_SYSTEM) {
+ // Managed-profiles cannot be setup on the system user, only regular users.
+ return false;
+ }
}
}
if (getProfileOwner(callingUserId) != null) {
@@ -6877,8 +6883,10 @@
}
private boolean isDeviceOwnerProvisioningAllowed(int callingUserId) {
- if (mOwners.hasDeviceOwner()) {
- return false;
+ synchronized (this) {
+ if (mOwners.hasDeviceOwner()) {
+ return false;
+ }
}
if (getProfileOwner(callingUserId) != null) {
return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 435de7a..f7de0b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -23,6 +23,7 @@
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
@@ -50,6 +51,9 @@
/**
* Stores and restores state for the Device and Profile owners. By definition there can be
* only one device owner, but there may be a profile owner for each user.
+ *
+ * <p>This class is not thread safe. (i.e. access to this class must always be synchronized
+ * in the caller side.)
*/
class Owners {
private static final String TAG = "DevicePolicyManagerService";
@@ -78,8 +82,8 @@
private static final String TAG_SYSTEM_UPDATE_POLICY = "system-update-policy";
- private final Context mContext;
private final UserManager mUserManager;
+ private final UserManagerInternal mUserManagerInternal;
// Internal state for the device owner package.
private OwnerInfo mDeviceOwner;
@@ -92,44 +96,48 @@
// Local system update policy controllable by device owner.
private SystemUpdatePolicy mSystemUpdatePolicy;
- public Owners(Context context) {
- mContext = context;
- mUserManager = context.getSystemService(UserManager.class);
+ public Owners(Context context, UserManager userManager,
+ UserManagerInternal userManagerInternal) {
+ mUserManager = userManager;
+ mUserManagerInternal = userManagerInternal;
}
/**
* Load configuration from the disk.
*/
void load() {
- synchronized (this) {
- // First, try to read from the legacy file.
- final File legacy = getLegacyConfigFileWithTestOverride();
+ // First, try to read from the legacy file.
+ final File legacy = getLegacyConfigFileWithTestOverride();
- if (readLegacyOwnerFile(legacy)) {
- if (DEBUG) {
- Log.d(TAG, "Legacy config file found.");
- }
+ final List<UserInfo> users = mUserManager.getUsers();
- // Legacy file exists, write to new files and remove the legacy one.
- writeDeviceOwner();
- for (int userId : getProfileOwnerKeys()) {
- writeProfileOwner(userId);
- }
- if (DEBUG) {
- Log.d(TAG, "Deleting legacy config file");
- }
- if (!legacy.delete()) {
- Slog.e(TAG, "Failed to remove the legacy setting file");
- }
- } else {
- // No legacy file, read from the new format files.
- new DeviceOwnerReadWriter().readFromFileLocked();
-
- final List<UserInfo> users = mUserManager.getUsers();
- for (UserInfo ui : users) {
- new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
- }
+ if (readLegacyOwnerFile(legacy)) {
+ if (DEBUG) {
+ Log.d(TAG, "Legacy config file found.");
}
+
+ // Legacy file exists, write to new files and remove the legacy one.
+ writeDeviceOwner();
+ for (int userId : getProfileOwnerKeys()) {
+ writeProfileOwner(userId);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Deleting legacy config file");
+ }
+ if (!legacy.delete()) {
+ Slog.e(TAG, "Failed to remove the legacy setting file");
+ }
+ } else {
+ // No legacy file, read from the new format files.
+ new DeviceOwnerReadWriter().readFromFileLocked();
+
+ for (UserInfo ui : users) {
+ new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
+ }
+ }
+ mUserManagerInternal.setDeviceManaged(hasDeviceOwner());
+ for (UserInfo ui : users) {
+ mUserManagerInternal.setUserManaged(ui.id, hasProfileOwner(ui.id));
}
if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) {
Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported",
@@ -169,21 +177,27 @@
boolean userRestrictionsMigrated) {
mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated);
mDeviceOwnerUserId = userId;
+
+ mUserManagerInternal.setDeviceManaged(true);
}
void clearDeviceOwner() {
mDeviceOwner = null;
mDeviceOwnerUserId = UserHandle.USER_NULL;
+
+ mUserManagerInternal.setDeviceManaged(false);
}
void setProfileOwner(ComponentName admin, String ownerName, int userId) {
// For a newly set PO, there's no need for migration.
mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
/* userRestrictionsMigrated =*/ true));
+ mUserManagerInternal.setUserManaged(userId, true);
}
void removeProfileOwner(int userId) {
mProfileOwners.remove(userId);
+ mUserManagerInternal.setUserManaged(userId, false);
}
ComponentName getProfileOwnerComponent(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 56d6fc0..435b602 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -52,7 +52,7 @@
private final File mProfileOwnerBase;
public OwnersTestable(DpmMockContext context) {
- super(context);
+ super(context, context.userManager, context.userManagerInternal);
mLegacyFile = new File(context.dataDir, LEGACY_FILE);
mDeviceOwnerFile = new File(context.dataDir, DEVICE_OWNER_FILE);
mProfileOwnerBase = new File(context.dataDir, PROFILE_OWNER_FILE_BASE);