Loading core/java/android/app/admin/DevicePolicyManager.java +27 −0 Original line number Diff line number Diff line Loading @@ -1275,6 +1275,33 @@ public class DevicePolicyManager { */ public static final int PASSWORD_QUALITY_MANAGED = 0x80000; /** * @hide * * adb shell dpm set-{device,profile}-owner will normally not allow installing an owner to * a user with accounts. {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} * and {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED} are the account features * used by authenticator to exempt their accounts from this: * * <ul> * <li>Non-test-only DO/PO still can't be installed when there are accounts. * <p>In order to make an apk test-only, add android:testOnly="true" to the * <application> tag in the manifest. * * <li>Test-only DO/PO can be installed even when there are accounts, as long as all the * accounts have the {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} feature. * Some authenticators claim to have any features, so to detect it, we also check * {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED} and disallow installing * if any of the accounts have it. * </ul> */ public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; /** @hide See {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} */ public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; /** * Called by an application that is administering the device to set the password restrictions it * is imposing. After setting this, the user will not be able to enter a new password that is Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +116 −25 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static org.xmlpull.v1.XmlPullParser.TEXT; import android.Manifest.permission; import android.accessibilityservice.AccessibilityServiceInfo; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.IntDef; import android.annotation.NonNull; Loading Loading @@ -2943,19 +2944,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceShell("forceRemoveActiveAdmin"); long ident = mInjector.binderClearCallingIdentity(); try { final ApplicationInfo ai; try { ai = mIPackageManager.getApplicationInfo(adminReceiver.getPackageName(), 0, userHandle); } catch (RemoteException e) { throw new IllegalStateException(e); } if (ai == null) { throw new IllegalStateException("Couldn't find package to remove admin " + adminReceiver.getPackageName() + " " + userHandle); } if ((ai.flags & ApplicationInfo.FLAG_TEST_ONLY) == 0) { throw new SecurityException("Attempt to remove non-test admin " + adminReceiver if (!isPackageTestOnly(adminReceiver.getPackageName(), userHandle)) { throw new SecurityException("Attempt to remove non-test admin " + adminReceiver + " " + userHandle); } // If admin is a device or profile owner tidy that up first. Loading @@ -2971,11 +2961,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Remove the admin skipping sending the broadcast. removeAdminArtifacts(adminReceiver, userHandle); Slog.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle); } finally { mInjector.binderRestoreCallingIdentity(ident); } } private boolean isPackageTestOnly(String packageName, int userHandle) { final ApplicationInfo ai; try { ai = mIPackageManager.getApplicationInfo(packageName, (PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE), userHandle); } catch (RemoteException e) { throw new IllegalStateException(e); } if (ai == null) { throw new IllegalStateException("Couldn't find package to remove admin " + packageName + " " + userHandle); } return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0; } private void enforceShell(String method) { final int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) { Loading Loading @@ -4514,7 +4521,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * not installed and therefore not available. * * @throws SecurityException if the caller is not a profile or device owner. * @throws UnsupportedException if the package does not support being set as always-on. * @throws UnsupportedOperationException if the package does not support being set as always-on. */ @Override public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown) Loading Loading @@ -5710,7 +5717,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " for device owner"); } synchronized (this) { enforceCanSetDeviceOwnerLocked(userId); enforceCanSetDeviceOwnerLocked(admin, userId); if (getActiveAdminUncheckedLocked(admin, userId) == null || getUserData(userId).mRemovingAdmins.contains(admin)) { throw new IllegalArgumentException("Not active admin: " + admin); Loading Loading @@ -5742,6 +5749,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); return true; } } Loading Loading @@ -5863,6 +5871,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent); } } Loading Loading @@ -5898,7 +5907,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " not installed for userId:" + userHandle); } synchronized (this) { enforceCanSetProfileOwnerLocked(userHandle); enforceCanSetProfileOwnerLocked(who, userHandle); if (getActiveAdminUncheckedLocked(who, userHandle) == null || getUserData(userHandle).mRemovingAdmins.contains(who)) { Loading @@ -5907,6 +5916,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mOwners.setProfileOwner(who, ownerName, userHandle); mOwners.writeProfileOwner(userHandle); Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle); return true; } } Loading @@ -5931,6 +5941,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId); } } Loading Loading @@ -6207,9 +6218,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * The profile owner can only be set before the user setup phase has completed, * except for: * - SYSTEM_UID * - adb if there are not accounts. * - adb if there are no accounts. (But see {@link #hasIncompatibleAccounts}) */ private void enforceCanSetProfileOwnerLocked(int userHandle) { private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) { UserInfo info = getUserInfo(userHandle); if (info == null) { // User doesn't exist. Loading @@ -6229,8 +6240,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } int callingUid = mInjector.binderGetCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { if (hasUserSetupCompleted(userHandle) && AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) { if (hasUserSetupCompleted(userHandle) && hasIncompatibleAccounts(userHandle, owner)) { throw new IllegalStateException("Not allowed to set the profile owner because " + "there are already some accounts on the profile"); } Loading @@ -6247,14 +6258,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS * permission. */ private void enforceCanSetDeviceOwnerLocked(int userId) { private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) { int callingUid = mInjector.binderGetCallingUid(); boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; if (!isAdb) { enforceCanManageProfileAndDeviceOwners(); } final int code = checkSetDeviceOwnerPreCondition(userId, isAdb); final int code = checkSetDeviceOwnerPreCondition(owner, userId, isAdb); switch (code) { case CODE_OK: return; Loading Loading @@ -8472,7 +8483,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * except for adb command if no accounts or additional users are present on the device. */ private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition( int deviceOwnerUserId, boolean isAdb) { @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb) { if (mOwners.hasDeviceOwner()) { return CODE_HAS_DEVICE_OWNER; } Loading @@ -8489,7 +8500,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (mUserManager.getUserCount() > 1) { return CODE_NONSYSTEM_USER_EXISTS; } if (AccountManager.get(mContext).getAccounts().length > 0) { if (hasIncompatibleAccounts(UserHandle.USER_SYSTEM, owner)) { return CODE_ACCOUNTS_NOT_EMPTY; } } else { Loading @@ -8515,7 +8526,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) { return CODE_OK == checkSetDeviceOwnerPreCondition(deviceOwnerUserId, /* isAdb */ false); return CODE_OK == checkSetDeviceOwnerPreCondition( /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false); } private boolean hasFeatureManagedUsers() { Loading Loading @@ -9048,6 +9060,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { saveSettingsLocked(userHandle); updateMaximumTimeToLockLocked(userHandle); policy.mRemovingAdmins.remove(adminReceiver); Slog.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle); } // The removed admin might have disabled camera, so update user // restrictions. Loading @@ -9072,4 +9086,81 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return policy.mDeviceProvisioningConfigApplied; } } /** * Return true if a given user has any accounts that'll prevent installing a device or profile * owner {@code owner}. * - If the user has no accounts, then return false. * - Otherwise, if the owner is unknown (== null), or is not test-only, then return true. * - Otherwise, if there's any account that does not have ..._ALLOWED, or does have * ..._DISALLOWED, return true. * - Otherwise return false. */ private boolean hasIncompatibleAccounts(int userId, @Nullable ComponentName owner) { final long token = mInjector.binderClearCallingIdentity(); try { final AccountManager am = AccountManager.get(mContext); final Account accounts[] = am.getAccountsAsUser(userId); if (accounts.length == 0) { return false; } final String[] feature_allow = { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED }; final String[] feature_disallow = { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED }; // Even if we find incompatible accounts along the way, we still check all accounts // for logging. boolean compatible = true; for (Account account : accounts) { if (hasAccountFeatures(am, account, feature_disallow)) { Log.e(LOG_TAG, account + " has " + feature_disallow[0]); compatible = false; } if (!hasAccountFeatures(am, account, feature_allow)) { Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]); compatible = false; } } if (compatible) { Log.w(LOG_TAG, "All accounts are compatible"); } else { Log.e(LOG_TAG, "Found incompatible accounts"); } // Then check if the owner is test-only. String log; if (owner == null) { // Owner is unknown. Suppose it's not test-only compatible = false; log = "Only test-only device/profile owner can be installed with accounts"; } else if (isPackageTestOnly(owner.getPackageName(), userId)) { if (compatible) { log = "Installing test-only owner " + owner; } else { log = "Can't install test-only owner " + owner + " with incompatible accounts"; } } else { compatible = false; log = "Can't install non test-only owner " + owner + " with accounts"; } if (compatible) { Log.w(LOG_TAG, log); } else { Log.e(LOG_TAG, log); } return !compatible; } finally { mInjector.binderRestoreCallingIdentity(token); } } private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) { try { return am.hasFeatures(account, features, null, null).getResult(); } catch (Exception e) { Log.w(LOG_TAG, "Failed to get account feature", e); return false; } } } Loading
core/java/android/app/admin/DevicePolicyManager.java +27 −0 Original line number Diff line number Diff line Loading @@ -1275,6 +1275,33 @@ public class DevicePolicyManager { */ public static final int PASSWORD_QUALITY_MANAGED = 0x80000; /** * @hide * * adb shell dpm set-{device,profile}-owner will normally not allow installing an owner to * a user with accounts. {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} * and {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED} are the account features * used by authenticator to exempt their accounts from this: * * <ul> * <li>Non-test-only DO/PO still can't be installed when there are accounts. * <p>In order to make an apk test-only, add android:testOnly="true" to the * <application> tag in the manifest. * * <li>Test-only DO/PO can be installed even when there are accounts, as long as all the * accounts have the {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} feature. * Some authenticators claim to have any features, so to detect it, we also check * {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED} and disallow installing * if any of the accounts have it. * </ul> */ public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; /** @hide See {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} */ public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; /** * Called by an application that is administering the device to set the password restrictions it * is imposing. After setting this, the user will not be able to enter a new password that is Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +116 −25 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static org.xmlpull.v1.XmlPullParser.TEXT; import android.Manifest.permission; import android.accessibilityservice.AccessibilityServiceInfo; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.IntDef; import android.annotation.NonNull; Loading Loading @@ -2943,19 +2944,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { enforceShell("forceRemoveActiveAdmin"); long ident = mInjector.binderClearCallingIdentity(); try { final ApplicationInfo ai; try { ai = mIPackageManager.getApplicationInfo(adminReceiver.getPackageName(), 0, userHandle); } catch (RemoteException e) { throw new IllegalStateException(e); } if (ai == null) { throw new IllegalStateException("Couldn't find package to remove admin " + adminReceiver.getPackageName() + " " + userHandle); } if ((ai.flags & ApplicationInfo.FLAG_TEST_ONLY) == 0) { throw new SecurityException("Attempt to remove non-test admin " + adminReceiver if (!isPackageTestOnly(adminReceiver.getPackageName(), userHandle)) { throw new SecurityException("Attempt to remove non-test admin " + adminReceiver + " " + userHandle); } // If admin is a device or profile owner tidy that up first. Loading @@ -2971,11 +2961,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } // Remove the admin skipping sending the broadcast. removeAdminArtifacts(adminReceiver, userHandle); Slog.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle); } finally { mInjector.binderRestoreCallingIdentity(ident); } } private boolean isPackageTestOnly(String packageName, int userHandle) { final ApplicationInfo ai; try { ai = mIPackageManager.getApplicationInfo(packageName, (PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE), userHandle); } catch (RemoteException e) { throw new IllegalStateException(e); } if (ai == null) { throw new IllegalStateException("Couldn't find package to remove admin " + packageName + " " + userHandle); } return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0; } private void enforceShell(String method) { final int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) { Loading Loading @@ -4514,7 +4521,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * not installed and therefore not available. * * @throws SecurityException if the caller is not a profile or device owner. * @throws UnsupportedException if the package does not support being set as always-on. * @throws UnsupportedOperationException if the package does not support being set as always-on. */ @Override public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown) Loading Loading @@ -5710,7 +5717,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " for device owner"); } synchronized (this) { enforceCanSetDeviceOwnerLocked(userId); enforceCanSetDeviceOwnerLocked(admin, userId); if (getActiveAdminUncheckedLocked(admin, userId) == null || getUserData(userId).mRemovingAdmins.contains(admin)) { throw new IllegalArgumentException("Not active admin: " + admin); Loading Loading @@ -5742,6 +5749,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); return true; } } Loading Loading @@ -5863,6 +5871,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent); } } Loading Loading @@ -5898,7 +5907,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " not installed for userId:" + userHandle); } synchronized (this) { enforceCanSetProfileOwnerLocked(userHandle); enforceCanSetProfileOwnerLocked(who, userHandle); if (getActiveAdminUncheckedLocked(who, userHandle) == null || getUserData(userHandle).mRemovingAdmins.contains(who)) { Loading @@ -5907,6 +5916,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mOwners.setProfileOwner(who, ownerName, userHandle); mOwners.writeProfileOwner(userHandle); Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle); return true; } } Loading @@ -5931,6 +5941,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId); } } Loading Loading @@ -6207,9 +6218,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * The profile owner can only be set before the user setup phase has completed, * except for: * - SYSTEM_UID * - adb if there are not accounts. * - adb if there are no accounts. (But see {@link #hasIncompatibleAccounts}) */ private void enforceCanSetProfileOwnerLocked(int userHandle) { private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) { UserInfo info = getUserInfo(userHandle); if (info == null) { // User doesn't exist. Loading @@ -6229,8 +6240,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } int callingUid = mInjector.binderGetCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { if (hasUserSetupCompleted(userHandle) && AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) { if (hasUserSetupCompleted(userHandle) && hasIncompatibleAccounts(userHandle, owner)) { throw new IllegalStateException("Not allowed to set the profile owner because " + "there are already some accounts on the profile"); } Loading @@ -6247,14 +6258,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS * permission. */ private void enforceCanSetDeviceOwnerLocked(int userId) { private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) { int callingUid = mInjector.binderGetCallingUid(); boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; if (!isAdb) { enforceCanManageProfileAndDeviceOwners(); } final int code = checkSetDeviceOwnerPreCondition(userId, isAdb); final int code = checkSetDeviceOwnerPreCondition(owner, userId, isAdb); switch (code) { case CODE_OK: return; Loading Loading @@ -8472,7 +8483,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * except for adb command if no accounts or additional users are present on the device. */ private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition( int deviceOwnerUserId, boolean isAdb) { @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb) { if (mOwners.hasDeviceOwner()) { return CODE_HAS_DEVICE_OWNER; } Loading @@ -8489,7 +8500,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (mUserManager.getUserCount() > 1) { return CODE_NONSYSTEM_USER_EXISTS; } if (AccountManager.get(mContext).getAccounts().length > 0) { if (hasIncompatibleAccounts(UserHandle.USER_SYSTEM, owner)) { return CODE_ACCOUNTS_NOT_EMPTY; } } else { Loading @@ -8515,7 +8526,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) { return CODE_OK == checkSetDeviceOwnerPreCondition(deviceOwnerUserId, /* isAdb */ false); return CODE_OK == checkSetDeviceOwnerPreCondition( /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false); } private boolean hasFeatureManagedUsers() { Loading Loading @@ -9048,6 +9060,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { saveSettingsLocked(userHandle); updateMaximumTimeToLockLocked(userHandle); policy.mRemovingAdmins.remove(adminReceiver); Slog.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle); } // The removed admin might have disabled camera, so update user // restrictions. Loading @@ -9072,4 +9086,81 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return policy.mDeviceProvisioningConfigApplied; } } /** * Return true if a given user has any accounts that'll prevent installing a device or profile * owner {@code owner}. * - If the user has no accounts, then return false. * - Otherwise, if the owner is unknown (== null), or is not test-only, then return true. * - Otherwise, if there's any account that does not have ..._ALLOWED, or does have * ..._DISALLOWED, return true. * - Otherwise return false. */ private boolean hasIncompatibleAccounts(int userId, @Nullable ComponentName owner) { final long token = mInjector.binderClearCallingIdentity(); try { final AccountManager am = AccountManager.get(mContext); final Account accounts[] = am.getAccountsAsUser(userId); if (accounts.length == 0) { return false; } final String[] feature_allow = { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED }; final String[] feature_disallow = { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED }; // Even if we find incompatible accounts along the way, we still check all accounts // for logging. boolean compatible = true; for (Account account : accounts) { if (hasAccountFeatures(am, account, feature_disallow)) { Log.e(LOG_TAG, account + " has " + feature_disallow[0]); compatible = false; } if (!hasAccountFeatures(am, account, feature_allow)) { Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]); compatible = false; } } if (compatible) { Log.w(LOG_TAG, "All accounts are compatible"); } else { Log.e(LOG_TAG, "Found incompatible accounts"); } // Then check if the owner is test-only. String log; if (owner == null) { // Owner is unknown. Suppose it's not test-only compatible = false; log = "Only test-only device/profile owner can be installed with accounts"; } else if (isPackageTestOnly(owner.getPackageName(), userId)) { if (compatible) { log = "Installing test-only owner " + owner; } else { log = "Can't install test-only owner " + owner + " with incompatible accounts"; } } else { compatible = false; log = "Can't install non test-only owner " + owner + " with accounts"; } if (compatible) { Log.w(LOG_TAG, log); } else { Log.e(LOG_TAG, log); } return !compatible; } finally { mInjector.binderRestoreCallingIdentity(token); } } private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) { try { return am.hasFeatures(account, features, null, null).getResult(); } catch (Exception e) { Log.w(LOG_TAG, "Failed to get account feature", e); return false; } } }