Allow DO/PO to be installed with certain preconfigured accounts. am: 5e7e0670c7
am: 9d46e5e044
Change-Id: I6a55f788b12c93b7be0503300c171f7f5a40fe9d
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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) {
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -5863,6 +5871,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
} finally {
|
||||
mInjector.binderRestoreCallingIdentity(ident);
|
||||
}
|
||||
Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5900,7 +5909,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)) {
|
||||
@@ -5909,6 +5918,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;
|
||||
}
|
||||
}
|
||||
@@ -5933,6 +5943,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
} finally {
|
||||
mInjector.binderRestoreCallingIdentity(ident);
|
||||
}
|
||||
Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6209,9 +6220,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.
|
||||
@@ -6231,8 +6242,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");
|
||||
}
|
||||
@@ -6249,14 +6260,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;
|
||||
@@ -8474,7 +8485,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;
|
||||
}
|
||||
@@ -8491,7 +8502,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 {
|
||||
@@ -8517,7 +8528,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() {
|
||||
@@ -9050,6 +9062,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.
|
||||
@@ -9074,4 +9088,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user