Merge "Stop LockSettingsService from calling DevicePolicyManager directly"
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8765760..b873be3 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -153,4 +153,11 @@
* Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
*/
protected abstract DevicePolicyCache getDevicePolicyCache();
+
+ /**
+ * @return cached version of device state related to DPM that can be accessed without risking
+ * deadlocks.
+ * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
+ */
+ protected abstract DeviceStateCache getDeviceStateCache();
}
diff --git a/core/java/android/app/admin/DeviceStateCache.java b/core/java/android/app/admin/DeviceStateCache.java
new file mode 100644
index 0000000..7619aa2
--- /dev/null
+++ b/core/java/android/app/admin/DeviceStateCache.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import com.android.server.LocalServices;
+
+/**
+ * Stores a copy of the set of device state maintained by {@link DevicePolicyManager} which
+ * is not directly related to admin policies. This lives in its own class so that the state
+ * can be accessed from any place without risking dead locks.
+ *
+ * @hide
+ */
+public abstract class DeviceStateCache {
+ protected DeviceStateCache() {
+ }
+
+ /**
+ * @return the instance.
+ */
+ public static DeviceStateCache getInstance() {
+ final DevicePolicyManagerInternal dpmi =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ return (dpmi != null) ? dpmi.getDeviceStateCache() : EmptyDeviceStateCache.INSTANCE;
+ }
+
+ /**
+ * See {@link DevicePolicyManager#isDeviceProvisioned}
+ */
+ public abstract boolean isDeviceProvisioned();
+
+ /**
+ * Empty implementation.
+ */
+ private static class EmptyDeviceStateCache extends DeviceStateCache {
+ private static final EmptyDeviceStateCache INSTANCE = new EmptyDeviceStateCache();
+
+ @Override
+ public boolean isDeviceProvisioned() {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index a5b71f6..59fb3d9 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -86,12 +86,22 @@
public abstract void setDeviceManaged(boolean isManaged);
/**
+ * Returns whether the device is managed by device owner.
+ */
+ public abstract boolean isDeviceManaged();
+
+ /**
* Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to update
* whether the user is managed by profile owner.
*/
public abstract void setUserManaged(int userId, boolean isManaged);
/**
+ * whether a profile owner manages this user.
+ */
+ public abstract boolean isUserManaged(int userId);
+
+ /**
* Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to omit
* restriction check, because DevicePolicyManager must always be able to set user icon
* regardless of any restriction.
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b7eca29..378d9eb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -44,6 +44,7 @@
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
import android.app.admin.PasswordMetrics;
import android.app.backup.BackupManager;
import android.app.trust.IStrongAuthTracker;
@@ -76,6 +77,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
@@ -429,6 +431,10 @@
return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
+ public UserManagerInternal getUserManagerInternal() {
+ return LocalServices.getService(UserManagerInternal.class);
+ }
+
/**
* Return the {@link DevicePolicyManager} object.
*
@@ -440,6 +446,10 @@
return (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
}
+ public DeviceStateCache getDeviceStateCache() {
+ return DeviceStateCache.getInstance();
+ }
+
public KeyStore getKeyStore() {
return KeyStore.getInstance();
}
@@ -1023,11 +1033,16 @@
}
private void notifySeparateProfileChallengeChanged(int userId) {
- final DevicePolicyManagerInternal dpmi = LocalServices.getService(
- DevicePolicyManagerInternal.class);
- if (dpmi != null) {
- dpmi.reportSeparateProfileChallengeChanged(userId);
- }
+ // LSS cannot call into DPM directly, otherwise it will cause deadlock.
+ // In this case, calling DPM on a handler thread is OK since DPM doesn't
+ // expect reportSeparateProfileChallengeChanged() to happen synchronously.
+ mHandler.post(() -> {
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ if (dpmi != null) {
+ dpmi.reportSeparateProfileChallengeChanged(userId);
+ }
+ });
}
@Override
@@ -2038,7 +2053,6 @@
* reporting the password changed.
*/
private void notifyPasswordChanged(@UserIdInt int userId) {
- // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
mHandler.post(() -> {
mInjector.getDevicePolicyManager().reportPasswordChanged(userId);
LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
@@ -3026,45 +3040,43 @@
pw.decreaseIndent();
}
+ /**
+ * Cryptographically disable escrow token support for the current user, if the user is not
+ * managed (either user has a profile owner, or if device is managed). Do not disable
+ * if we are running an automotive build.
+ */
private void disableEscrowTokenOnNonManagedDevicesIfNeeded(int userId) {
- long ident = Binder.clearCallingIdentity();
- try {
- // Managed profile should have escrow enabled
- if (mUserManager.getUserInfo(userId).isManagedProfile()) {
- Slog.i(TAG, "Managed profile can have escrow token");
- return;
- }
- DevicePolicyManager dpm = mInjector.getDevicePolicyManager();
- // Devices with Device Owner should have escrow enabled on all users.
- if (dpm.getDeviceOwnerComponentOnAnyUser() != null) {
- Slog.i(TAG, "Corp-owned device can have escrow token");
- return;
- }
- // We could also have a profile owner on the given (non-managed) user for unicorn cases
- if (dpm.getProfileOwnerAsUser(userId) != null) {
- Slog.i(TAG, "User with profile owner can have escrow token");
- return;
- }
- // If the device is yet to be provisioned (still in SUW), there is still
- // a chance that Device Owner will be set on the device later, so postpone
- // disabling escrow token for now.
- if (!dpm.isDeviceProvisioned()) {
- Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
- return;
- }
+ final UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
- // Escrow tokens are enabled on automotive builds.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- return;
- }
+ // Managed profile should have escrow enabled
+ if (userManagerInternal.isUserManaged(userId)) {
+ Slog.i(TAG, "Managed profile can have escrow token");
+ return;
+ }
- // Disable escrow token permanently on all other device/user types.
- Slog.i(TAG, "Disabling escrow token on user " + userId);
- if (isSyntheticPasswordBasedCredentialLocked(userId)) {
- mSpManager.destroyEscrowData(userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
+ // Devices with Device Owner should have escrow enabled on all users.
+ if (userManagerInternal.isDeviceManaged()) {
+ Slog.i(TAG, "Corp-owned device can have escrow token");
+ return;
+ }
+
+ // If the device is yet to be provisioned (still in SUW), there is still
+ // a chance that Device Owner will be set on the device later, so postpone
+ // disabling escrow token for now.
+ if (!mInjector.getDeviceStateCache().isDeviceProvisioned()) {
+ Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
+ return;
+ }
+
+ // Escrow tokens are enabled on automotive builds.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ return;
+ }
+
+ // Disable escrow token permanently on all other device/user types.
+ Slog.i(TAG, "Disabling escrow token on user " + userId);
+ if (isSyntheticPasswordBasedCredentialLocked(userId)) {
+ mSpManager.destroyEscrowData(userId);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5f86708..dace598 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3979,6 +3979,13 @@
}
@Override
+ public boolean isDeviceManaged() {
+ synchronized (mUsersLock) {
+ return mIsDeviceManaged;
+ }
+ }
+
+ @Override
public void setUserManaged(@UserIdInt int userId, boolean isManaged) {
synchronized (mUsersLock) {
mIsUserManaged.put(userId, isManaged);
@@ -3986,6 +3993,13 @@
}
@Override
+ public boolean isUserManaged(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ return mIsUserManaged.get(userId);
+ }
+ }
+
+ @Override
public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
long ident = Binder.clearCallingIdentity();
try {
@@ -4205,6 +4219,7 @@
return restrictions != null && restrictions.getBoolean(restrictionKey);
}
+ @Override
public @Nullable UserInfo getUserInfo(@UserIdInt int userId) {
UserData userData;
synchronized (mUsersLock) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 479dd1e..c3ff285 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -126,6 +126,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
import android.app.admin.NetworkEvent;
import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog;
@@ -507,6 +508,7 @@
private final OverlayPackagesProvider mOverlayPackagesProvider;
private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
+ private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
/**
* Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
@@ -2295,6 +2297,9 @@
policy = new DevicePolicyData(userHandle);
mUserData.append(userHandle, policy);
loadSettingsLocked(policy, userHandle);
+ if (userHandle == UserHandle.USER_SYSTEM) {
+ mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
+ }
}
return policy;
}
@@ -8958,6 +8963,8 @@
pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
pw.println();
mPolicyCache.dump(pw);
+ pw.println();
+ mStateCache.dump(pw);
}
}
@@ -11169,6 +11176,9 @@
DevicePolicyData policy = getUserData(userHandle);
if (!policy.mUserSetupComplete) {
policy.mUserSetupComplete = true;
+ if (userHandle == UserHandle.USER_SYSTEM) {
+ mStateCache.setDeviceProvisioned(true);
+ }
synchronized (getLockObject()) {
saveSettingsLocked(userHandle);
}
@@ -11481,6 +11491,12 @@
protected DevicePolicyCache getDevicePolicyCache() {
return mPolicyCache;
}
+
+ @Override
+ protected DeviceStateCache getDeviceStateCache() {
+ return mStateCache;
+ }
+
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
@@ -13021,6 +13037,7 @@
Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
DevicePolicyData policy = getUserData(userId);
policy.mUserSetupComplete = isUserCompleted;
+ mStateCache.setDeviceProvisioned(isUserCompleted);
synchronized (getLockObject()) {
saveSettingsLocked(userId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java
new file mode 100644
index 0000000..c3cb9b0
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.app.admin.DeviceStateCache;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * Implementation of {@link DeviceStateCache}, to which {@link DevicePolicyManagerService} pushes
+ * device state.
+ *
+ */
+public class DeviceStateCacheImpl extends DeviceStateCache {
+ /**
+ * Lock object. For simplicity we just always use this as the lock. We could use each object
+ * as a lock object to make it more fine-grained, but that'd make copy-paste error-prone.
+ */
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mIsDeviceProvisioned = false;
+
+ @Override
+ public boolean isDeviceProvisioned() {
+ return mIsDeviceProvisioned;
+ }
+
+ /** Update the device provisioned flag for USER_SYSTEM */
+ public void setDeviceProvisioned(boolean provisioned) {
+ synchronized (mLock) {
+ mIsDeviceProvisioned = provisioned;
+ }
+ }
+
+ /** Dump content */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Device state cache:");
+ pw.increaseIndent();
+ pw.println("Device provisioned: " + mIsDeviceProvisioned);
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 1f5ebe4..7cece1f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -29,6 +29,7 @@
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.pm.UserInfo;
@@ -37,6 +38,7 @@
import android.os.IProgressListener;
import android.os.RemoteException;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.security.KeyStore;
@@ -91,6 +93,8 @@
FakeGsiService mGsiService;
PasswordSlotManagerTestable mPasswordSlotManager;
RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+ UserManagerInternal mUserManagerInternal;
+ DeviceStateCache mDeviceStateCache;
protected boolean mHasSecureLockScreen;
@Override
@@ -108,6 +112,8 @@
mGsiService = new FakeGsiService();
mPasswordSlotManager = new PasswordSlotManagerTestable();
mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);
+ mUserManagerInternal = mock(UserManagerInternal.class);
+ mDeviceStateCache = mock(DeviceStateCache.class);
LocalServices.removeServiceForTest(LockSettingsInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
@@ -144,7 +150,8 @@
mAuthSecretService = mock(IAuthSecret.class);
mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
- mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager);
+ mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager,
+ mUserManagerInternal, mDeviceStateCache);
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
installChildProfile(MANAGED_PROFILE_USER_ID);
@@ -172,6 +179,9 @@
// Adding a fake Device Owner app which will enable escrow token support in LSS.
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
+ when(mUserManagerInternal.isDeviceManaged()).thenReturn(true);
+ when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
+
mLocalService = LocalServices.getService(LockSettingsInternal.class);
}
@@ -184,6 +194,7 @@
when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
when(mUserManager.isUserRunning(eq(profileId))).thenReturn(true);
when(mUserManager.isUserUnlocked(eq(profileId))).thenReturn(true);
+ when(mUserManagerInternal.isUserManaged(eq(profileId))).thenReturn(true);
return userInfo;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 5c67d04..65d6f45 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -19,12 +19,14 @@
import static org.mockito.Mockito.mock;
import android.app.IActivityManager;
+import android.app.admin.DeviceStateCache;
import android.content.Context;
import android.hardware.authsecret.V1_0.IAuthSecret;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
@@ -44,15 +46,16 @@
private LockPatternUtils mLockPatternUtils;
private IStorageManager mStorageManager;
private SyntheticPasswordManager mSpManager;
- private IAuthSecret mAuthSecretService;
private FakeGsiService mGsiService;
private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+ private UserManagerInternal mUserManagerInternal;
+ private DeviceStateCache mDeviceStateCache;
public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
IActivityManager activityManager, LockPatternUtils lockPatternUtils,
IStorageManager storageManager, SyntheticPasswordManager spManager,
- IAuthSecret authSecretService, FakeGsiService gsiService,
- RecoverableKeyStoreManager recoverableKeyStoreManager) {
+ FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
super(context);
mLockSettingsStorage = storage;
mKeyStore = keyStore;
@@ -62,6 +65,8 @@
mSpManager = spManager;
mGsiService = gsiService;
mRecoverableKeyStoreManager = recoverableKeyStoreManager;
+ mUserManagerInternal = userManagerInternal;
+ mDeviceStateCache = deviceStateCache;
}
@Override
@@ -93,6 +98,10 @@
public LockPatternUtils getLockPatternUtils() {
return mLockPatternUtils;
}
+ @Override
+ public DeviceStateCache getDeviceStateCache() {
+ return mDeviceStateCache;
+ }
@Override
public KeyStore getKeyStore() {
@@ -110,6 +119,11 @@
}
@Override
+ public UserManagerInternal getUserManagerInternal() {
+ return mUserManagerInternal;
+ }
+
+ @Override
public boolean hasEnrolledBiometrics(int userId) {
return false;
}
@@ -134,10 +148,11 @@
LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
IStorageManager storageManager, IActivityManager mActivityManager,
SyntheticPasswordManager spManager, IAuthSecret authSecretService,
- FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager) {
+ FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
- storageManager, spManager, authSecretService, gsiService,
- recoverableKeyStoreManager));
+ storageManager, spManager, gsiService,
+ recoverableKeyStoreManager, userManagerInternal, deviceStateCache));
mGateKeeperService = gatekeeper;
mAuthSecretService = authSecretService;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 127cf49..0776589 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.admin.PasswordMetrics;
import android.os.RemoteException;
@@ -468,6 +469,18 @@
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
}
+ public void testEscrowTokenCannotBeActivatedOnUnmanagedUser() {
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ when(mUserManagerInternal.isDeviceManaged()).thenReturn(false);
+ when(mUserManagerInternal.isUserManaged(PRIMARY_USER_ID)).thenReturn(false);
+ when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
+
+ try {
+ mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
+ fail("Escrow token should not be possible on unmanaged device");
+ } catch (SecurityException expected) { }
+ }
+
public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
final byte[] password = "password".getBytes();
final byte[] pattern = "123654".getBytes();