static class ActiveAdmin {
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
+ private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
boolean encryptionRequested = false;
+ boolean testOnlyAdmin = false;
boolean disableCamera = false;
boolean disableCallerId = false;
boolean disableContactsSearch = false;
out.attribute(null, ATTR_VALUE, Boolean.toString(encryptionRequested));
out.endTag(null, TAG_ENCRYPTION_REQUESTED);
}
+ if (testOnlyAdmin) {
+ out.startTag(null, TAG_TEST_ONLY_ADMIN);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(testOnlyAdmin));
+ out.endTag(null, TAG_TEST_ONLY_ADMIN);
+ }
if (disableCamera) {
out.startTag(null, TAG_DISABLE_CAMERA);
out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
} else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) {
encryptionRequested = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) {
+ testOnlyAdmin = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_DISABLE_CAMERA.equals(tag)) {
disableCamera = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("uid="); pw.println(getUid());
+ pw.print(prefix); pw.print("testOnlyAdmin=");
+ pw.println(testOnlyAdmin);
pw.print(prefix); pw.println("policies:");
ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
if (pols != null) {
synchronized (this) {
long ident = mInjector.binderClearCallingIdentity();
try {
- if (!refreshing
- && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
+ final ActiveAdmin existingAdmin
+ = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (!refreshing && existingAdmin != null) {
throw new IllegalArgumentException("Admin is already added");
}
if (policy.mRemovingAdmins.contains(adminReceiver)) {
"Trying to set an admin which is being removed");
}
ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
+ newAdmin.testOnlyAdmin =
+ (existingAdmin != null) ? existingAdmin.testOnlyAdmin
+ : isPackageTestOnly(adminReceiver.getPackageName(), userHandle);
policy.mAdminMap.put(adminReceiver, newAdmin);
int replaceIndex = -1;
final int N = policy.mAdminList.size();
enforceShell("forceRemoveActiveAdmin");
long ident = mInjector.binderClearCallingIdentity();
try {
- 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.
synchronized (this) {
+ if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
+ throw new SecurityException("Attempt to remove non-test admin "
+ + adminReceiver + " " + userHandle);
+ }
+
+ // If admin is a device or profile owner tidy that up first.
if (isDeviceOwner(adminReceiver, userHandle)) {
clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
}
}
}
+ /**
+ * Return if a given package has testOnly="true", in which case we'll relax certain rules
+ * for CTS.
+ *
+ * DO NOT use this method except in {@link #setActiveAdmin}. Use {@link #isAdminTestOnlyLocked}
+ * to check wehter an active admin is test-only or not.
+ *
+ * The system allows this flag to be changed when an app is updated, which is not good
+ * for us. So we persist the flag in {@link ActiveAdmin} when an admin is first installed,
+ * and used the persisted version in actual checks. (See b/31382361 and b/28928996)
+ */
private boolean isPackageTestOnly(String packageName, int userHandle) {
final ApplicationInfo ai;
try {
return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
}
+ /**
+ * See {@link #isPackageTestOnly}.
+ */
+ private boolean isAdminTestOnlyLocked(ComponentName who, int userHandle) {
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return (admin != null) && admin.testOnlyAdmin;
+ }
+
private void enforceShell(String method) {
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
* The profile owner can only be set before the user setup phase has completed,
* except for:
* - SYSTEM_UID
- * - adb if there are no accounts. (But see {@link #hasIncompatibleAccounts})
+ * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked})
*/
private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) {
UserInfo info = getUserInfo(userHandle);
int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
if (hasUserSetupCompleted(userHandle)
- && hasIncompatibleAccounts(userHandle, owner)) {
+ && hasIncompatibleAccountsLocked(userHandle, owner)) {
throw new IllegalStateException("Not allowed to set the profile owner because "
+ "there are already some accounts on the profile");
}
enforceCanManageProfileAndDeviceOwners();
}
- final int code = checkSetDeviceOwnerPreCondition(owner, userId, isAdb);
+ final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb);
switch (code) {
case CODE_OK:
return;
* The device owner can only be set before the setup phase of the primary user has completed,
* except for adb command if no accounts or additional users are present on the device.
*/
- private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition(
+ private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreConditionLocked(
@Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb) {
if (mOwners.hasDeviceOwner()) {
return CODE_HAS_DEVICE_OWNER;
if (mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
- if (hasIncompatibleAccounts(UserHandle.USER_SYSTEM, owner)) {
+ if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
return CODE_ACCOUNTS_NOT_EMPTY;
}
} else {
}
private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
- return CODE_OK == checkSetDeviceOwnerPreCondition(
- /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false);
+ synchronized (this) {
+ return CODE_OK == checkSetDeviceOwnerPreConditionLocked(
+ /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false);
+ }
}
private boolean hasFeatureManagedUsers() {
* ..._DISALLOWED, return true.
* - Otherwise return false.
*/
- private boolean hasIncompatibleAccounts(int userId, @Nullable ComponentName owner) {
+ private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) {
final long token = mInjector.binderClearCallingIdentity();
try {
final AccountManager am = AccountManager.get(mContext);
// 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)) {
+ } else if (isAdminTestOnlyLocked(owner, userId)) {
if (compatible) {
log = "Installing test-only owner " + owner;
} else {