From 07387fedfafa72bcb68defd801eef82f1f494d7c Mon Sep 17 00:00:00 2001 From: Nicolas Prevot Date: Fri, 30 Oct 2015 17:53:30 +0000 Subject: [PATCH] Add method to tell the dpc if provisioning is allowed. The DPC can use it to tell if provisioning a managed profile or for device owner would work or not. BUG:25338478 Change-Id: I09ea6a9f23a8e88e4ed37c048170b2a68213086e --- api/current.txt | 1 + api/system-current.txt | 1 + .../app/admin/DevicePolicyManager.java | 19 ++++++++ .../app/admin/IDevicePolicyManager.aidl | 1 + core/java/android/os/IUserManager.aidl | 2 +- core/java/android/os/UserManager.java | 6 ++- .../android/server/pm/UserManagerService.java | 15 ++++--- .../DevicePolicyManagerService.java | 43 +++++++++++++++++++ 8 files changed, 79 insertions(+), 9 deletions(-) diff --git a/api/current.txt b/api/current.txt index 62f0113a7d1cb..6c64e287a6c6d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5741,6 +5741,7 @@ package android.app.admin { method public boolean isLockTaskPermitted(java.lang.String); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isProfileOwnerApp(java.lang.String); + method public boolean isProvisioningAllowed(java.lang.String); method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String); method public void lockNow(); method public void removeActiveAdmin(android.content.ComponentName); diff --git a/api/system-current.txt b/api/system-current.txt index 4118f338ed641..df63ecf318c2f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5870,6 +5870,7 @@ package android.app.admin { method public boolean isLockTaskPermitted(java.lang.String); method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isProfileOwnerApp(java.lang.String); + method public boolean isProvisioningAllowed(java.lang.String); method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String); method public void lockNow(); method public void notifyPendingSystemUpdate(long); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0fdf3d3693c36..6a3f8e61e4585 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4299,4 +4299,23 @@ public class DevicePolicyManager { return PERMISSION_GRANT_STATE_DEFAULT; } } + + /** + * Returns if provisioning a managed profile or device is possible or not. + * @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE}, + * {@link #ACTION_PROVISION_MANAGED_PROFILE}. + * Note that even if this method returns true, there is a slight possibility that the + * provisioning will not be allowed when it is actually initiated because some event has + * happened in between. + * @return if provisioning a managed profile or device is possible or not. + * @throws IllegalArgumentException if the supplied action is not valid. + */ + public boolean isProvisioningAllowed(String action) { + try { + return mService.isProvisioningAllowed(action); + } catch (RemoteException re) { + Log.w(TAG, "Failed talking with device policy service", re); + return false; + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index ccaa8cb82170d..15e88378f8c42 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -227,4 +227,5 @@ interface IDevicePolicyManager { boolean setPermissionGrantState(in ComponentName admin, String packageName, String permission, int grantState); int getPermissionGrantState(in ComponentName admin, String packageName, String permission); + boolean isProvisioningAllowed(String action); } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 12cac85308c59..b5bbbbb7717f8 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -44,7 +44,7 @@ interface IUserManager { UserInfo getPrimaryUser(); List getUsers(boolean excludeDying); List getProfiles(int userHandle, boolean enabledOnly); - boolean canAddMoreManagedProfiles(int userId); + boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne); UserInfo getProfileParent(int userHandle); boolean isSameProfileGroup(int userId, int otherUserId); UserInfo getUserInfo(int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e892349c34f64..c191b23750958 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1077,13 +1077,15 @@ public class UserManager { /** * Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS * permission. + * if allowedToRemoveOne is true and if the user already has a managed profile, then return if + * we could add a new managed profile to this user after removing the existing one. * * @return true if more managed profiles can be added, false if limit has been reached. * @hide */ - public boolean canAddMoreManagedProfiles(int userId) { + public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) { try { - return mService.canAddMoreManagedProfiles(userId); + return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne); } catch (RemoteException re) { Log.w(TAG, "Could not check if we can add more managed profiles", re); return false; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c41d49359a665..ebb69bf7d7ca4 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -853,7 +853,7 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean canAddMoreManagedProfiles(int userId) { + public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) { checkManageUsersPermission("check if more managed profiles can be added."); if (ActivityManager.isLowRamDeviceStatic()) { return false; @@ -863,8 +863,9 @@ public class UserManagerService extends IUserManager.Stub { return false; } // Limit number of managed profiles that can be created - int managedProfilesCount = getProfiles(userId, true).size() - 1; - if (managedProfilesCount >= MAX_MANAGED_PROFILES) { + final int managedProfilesCount = getProfiles(userId, true).size() - 1; + final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0; + if (managedProfilesCount - profilesRemovedCount >= MAX_MANAGED_PROFILES) { return false; } synchronized(mUsersLock) { @@ -872,9 +873,11 @@ public class UserManagerService extends IUserManager.Stub { if (!userInfo.canHaveProfile()) { return false; } - int usersCount = getAliveUsersExcludingGuestsCountLU(); + int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU() + - profilesRemovedCount; // We allow creating a managed profile in the special case where there is only one user. - return usersCount == 1 || usersCount < UserManager.getMaxSupportedUsers(); + return usersCountAfterRemoving == 1 + || usersCountAfterRemoving < UserManager.getMaxSupportedUsers(); } } @@ -1450,7 +1453,7 @@ public class UserManagerService extends IUserManager.Stub { } if (parent == null) return null; } - if (isManagedProfile && !canAddMoreManagedProfiles(parentId)) { + if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) { Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId); return null; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index aea2ecf68858d..cabebe796cce5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1220,6 +1220,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Settings.Secure.putInt(mContext.getContentResolver(), name, value); } + int settingsGlobalGetInt(String name, int def) { + return Settings.Global.getInt(mContext.getContentResolver(), name, def); + } + void settingsGlobalPutInt(String name, int value) { Settings.Global.putInt(mContext.getContentResolver(), name, value); } @@ -6622,4 +6626,43 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new RuntimeException("Package manager has died", re); } } + + @Override + public boolean isProvisioningAllowed(String action) { + if (mOwners.hasDeviceOwner()) { + return false; + } + final int callingUserId = mInjector.userHandleGetCallingUserId(); + if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) { + try { + if (!mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) { + return false; + } + } catch (RemoteException e) { + return false; + } + final long ident = mInjector.binderClearCallingIdentity(); + try { + if (!mUserManager.canAddMoreManagedProfiles(callingUserId, true)) { + return false; + } + } finally { + mInjector.binderRestoreCallingIdentity(ident); + } + return true; + } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE.equals(action)) { + if (getProfileOwner(callingUserId) != null) { + return false; + } + if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) { + return false; + } + if (callingUserId != UserHandle.USER_SYSTEM) { + // Device owner provisioning can only be initiated from system user. + return false; + } + return true; + } + throw new IllegalArgumentException("Unknown provisioning action " + action); + } }