Add a feature flag for secure lock screen.
Disable certain APIs which require secure lock screen if the device
doesn't have the feature.
Make sure one cannot set the password/PIN if there is no secure lock
screen, because the password/PIN wouldn't be really used afterwards
while the password strength checks would succeed, creating a false
sense of security.
Allow setting password strength requirements in DPM - test if the
current password is sufficient will fail automatically if there is
no secure lock screen.
Bug: 111072170
Bug: 111071972
Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases
Test: cts-tradefed run cts -m CtsAdminTestCases
Test: frameworks/base/core/tests/utiltests/runtests.sh
Test: adb shell am instrument -w -e class com.android.internal.widget.LockPatternUtilsTest com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Test: atest SyntheticPasswordTests
Test: atest LockSettingsServiceTests
Test: atest LockSettingsShellCommandTest
Test: atest DevicePolicyManagerTest (for servicestests)
Change-Id: Ie46b0de6cb03c26dd05c05711c5c3b5e36a872df
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8734ceb6..a9ae74f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -947,6 +947,10 @@
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
String managedUserPassword) {
checkWritePermission(userId);
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires secure lock screen feature.");
+ }
synchronized (mSeparateChallengeLock) {
setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword);
}
@@ -1305,6 +1309,10 @@
public void setLockCredential(String credential, int type, String savedCredential,
int requestedQuality, int userId)
throws RemoteException {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires secure lock screen feature");
+ }
checkWritePermission(userId);
synchronized (mSeparateChallengeLock) {
setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId);
@@ -2906,6 +2914,10 @@
@Override
public boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
byte[] token, int requestedQuality, int userId) {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires secure lock screen feature.");
+ }
try {
return LockSettingsService.this.setLockCredentialWithToken(credential, type,
tokenHandle, token, requestedQuality, userId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 07f23ce..6163077 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -18,6 +18,7 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+
import static com.android.internal.widget.LockPatternUtils.stringToPattern;
import android.app.ActivityManager;
@@ -58,6 +59,18 @@
mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
parseArgs();
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ switch (cmd) {
+ case COMMAND_HELP:
+ case COMMAND_GET_DISABLED:
+ case COMMAND_SET_DISABLED:
+ break;
+ default:
+ getErrPrintWriter().println(
+ "The device does not support lock screen - ignoring the command.");
+ return -1;
+ }
+ }
if (!checkCredential()) {
return -1;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0977323..fb7e47d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4223,7 +4223,7 @@
@Override
public void setPasswordHistoryLength(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4246,13 +4246,16 @@
@Override
public int getPasswordHistoryLength(ComponentName who, int userHandle, boolean parent) {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ return 0;
+ }
return getStrictestPasswordRequirement(who, userHandle, parent,
admin -> admin.passwordHistoryLength, PASSWORD_QUALITY_UNSPECIFIED);
}
@Override
public void setPasswordExpirationTimeout(ComponentName who, long timeout, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4288,7 +4291,7 @@
*/
@Override
public long getPasswordExpirationTimeout(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0L;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4423,7 +4426,7 @@
@Override
public long getPasswordExpiration(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0L;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4770,6 +4773,9 @@
@Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ return 0;
+ }
enforceFullCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
if (!isCallerWithSystemUid()) {
@@ -4789,7 +4795,7 @@
@Override
public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4815,7 +4821,7 @@
@Override
public int getMaximumFailedPasswordsForWipe(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4829,7 +4835,7 @@
@Override
public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return UserHandle.USER_NULL;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4910,6 +4916,11 @@
}
@Override
public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
+ return false;
+ }
+
final int callingUid = mInjector.binderGetCallingUid();
final int userHandle = mInjector.userHandleGetCallingUserId();
@@ -5252,7 +5263,7 @@
@Override
public void setRequiredStrongAuthTimeout(ComponentName who, long timeoutMs,
boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -5285,7 +5296,7 @@
*/
@Override
public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
}
enforceFullCrossUsersPermission(userId);
@@ -6494,7 +6505,7 @@
*/
@Override
public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
enforceFullCrossUsersPermission(userHandle);
@@ -6514,7 +6525,7 @@
@Override
public void reportPasswordChanged(@UserIdInt int userId) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
enforceFullCrossUsersPermission(userId);
@@ -8800,7 +8811,7 @@
@Override
public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
PersistableBundle args, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(admin, "admin is null");
@@ -8817,7 +8828,7 @@
@Override
public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
ComponentName agent, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return null;
}
Preconditions.checkNotNull(agent, "agent null");
@@ -13215,7 +13226,7 @@
@Override
public boolean setResetPasswordToken(ComponentName admin, byte[] token) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return false;
}
if (token == null || token.length < 32) {
@@ -13243,7 +13254,7 @@
@Override
public boolean clearResetPasswordToken(ComponentName admin) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return false;
}
synchronized (getLockObject()) {
@@ -13269,6 +13280,9 @@
@Override
public boolean isResetPasswordTokenActive(ComponentName admin) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+ return false;
+ }
synchronized (getLockObject()) {
final int userHandle = mInjector.userHandleGetCallingUserId();
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -13290,6 +13304,9 @@
@Override
public boolean resetPasswordWithToken(ComponentName admin, String passwordOrNull, byte[] token,
int flags) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+ return false;
+ }
Preconditions.checkNotNull(token);
synchronized (getLockObject()) {
final int userHandle = mInjector.userHandleGetCallingUserId();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 38e8ac2..0813e6fa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -206,6 +206,8 @@
mAdmin1Context.binder.callingUid = DpmMockContext.CALLER_UID;
setUpUserManager();
+
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
}
private TransferOwnershipMetadataManager getMockTransferMetadataManager() {
@@ -836,6 +838,7 @@
MockUtils.checkIntent(intent),
MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
}
+
/**
* Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully.
*/
@@ -2618,6 +2621,7 @@
when(getServices().lockPatternUtils
.isSeparateProfileChallengeEnabled(eq(PROFILE_USER))).thenReturn(false);
dpmi.reportSeparateProfileChallengeChanged(PROFILE_USER);
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
verifyScreenTimeoutCall(Long.MAX_VALUE, PROFILE_USER);
verifyScreenTimeoutCall(10L , UserHandle.USER_SYSTEM);
@@ -4233,6 +4237,41 @@
assertTrue(dpm.isActivePasswordSufficient());
}
+ public void testIsActivePasswordSufficient_noLockScreen() throws Exception {
+ // If there is no lock screen, the password is considered empty no matter what, because
+ // it provides no security.
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ mContext.packageName = admin1.getPackageName();
+ setupDeviceOwner();
+
+ // If no password requirements are set, isActivePasswordSufficient should succeed.
+ assertTrue(dpm.isActivePasswordSufficient());
+
+ // Now set some password quality requirements.
+ dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+
+ reset(mContext.spiedContext);
+ final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
+ PasswordMetrics passwordMetricsNoSymbols = new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 9,
+ 8, 2,
+ 6, 1,
+ 0, 1);
+ // This should be ignored, as there is no lock screen.
+ dpm.setActivePasswordState(passwordMetricsNoSymbols, userHandle);
+ dpm.reportPasswordChanged(userHandle);
+
+ // No broadcast should be sent.
+ verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED),
+ MockUtils.checkUserHandle(userHandle));
+
+ // The active (nonexistent) password doesn't comply with the requirements.
+ assertFalse(dpm.isActivePasswordSufficient());
+ }
+
private void setActivePasswordState(PasswordMetrics passwordMetrics)
throws Exception {
final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
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 2dc3510..cf89cb8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -37,8 +37,8 @@
import android.os.IProgressListener;
import android.os.RemoteException;
import android.os.UserManager;
-import android.os.storage.StorageManager;
import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
import android.security.KeyStore;
import android.test.AndroidTestCase;
@@ -46,6 +46,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -85,6 +86,8 @@
KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
IAuthSecret mAuthSecretService;
+ WindowManagerInternal mMockWindowManager;
+ protected boolean mHasSecureLockScreen;
@Override
protected void setUp() throws Exception {
@@ -97,10 +100,13 @@
mActivityManager = mock(IActivityManager.class);
mDevicePolicyManager = mock(DevicePolicyManager.class);
mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
+ mMockWindowManager = mock(WindowManagerInternal.class);
LocalServices.removeServiceForTest(LockSettingsInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal);
+ LocalServices.addService(WindowManagerInternal.class, mMockWindowManager);
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
@@ -114,11 +120,17 @@
storageDir.mkdirs();
}
+ mHasSecureLockScreen = true;
mLockPatternUtils = new LockPatternUtils(mContext) {
@Override
public ILockSettings getLockSettings() {
return mService;
}
+
+ @Override
+ public boolean hasSecureLockScreen() {
+ return mHasSecureLockScreen;
+ }
};
mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
mUserManager);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index e12f6d3..5124803 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -26,13 +26,12 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.service.gatekeeper.GateKeeperResponse;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
+import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
/**
* runtest frameworks-services -c com.android.server.locksettings.LockSettingsServiceTests
@@ -54,11 +53,21 @@
PASSWORD_QUALITY_ALPHABETIC);
}
+ public void testCreatePasswordFailsWithoutLockScreen() throws RemoteException {
+ testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "password",
+ CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
+ }
+
public void testCreatePatternPrimaryUser() throws RemoteException {
testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN,
PASSWORD_QUALITY_SOMETHING);
}
+ public void testCreatePatternFailsWithoutLockScreen() throws RemoteException {
+ testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "123456789",
+ CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
+ }
+
public void testChangePasswordPrimaryUser() throws RemoteException {
testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
"asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
@@ -198,6 +207,21 @@
assertVerifyCredentials(userId, credential, type, -1);
}
+ private void testCreateCredentialFailsWithoutLockScreen(
+ int userId, String credential, int type, int quality) throws RemoteException {
+ mHasSecureLockScreen = false;
+
+ try {
+ mService.setLockCredential(credential, type, null, quality, userId);
+ fail("An exception should have been thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Success - the exception was expected.
+ }
+
+ assertFalse(mService.havePassword(userId));
+ assertFalse(mService.havePattern(userId));
+ }
+
private void testChangeCredentials(int userId, String newCredential, int newType,
String oldCredential, int oldType, int quality) throws RemoteException {
final long sid = 1234;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index a28a5a1..929c3b5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -77,6 +78,7 @@
final Context context = InstrumentationRegistry.getTargetContext();
mUserId = ActivityManager.getCurrentUser();
mCommand = new LockSettingsShellCommand(mLockPatternUtils);
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true);
}
@Test
@@ -103,6 +105,16 @@
}
@Test
+ public void testChangePin_noLockScreen() throws Exception {
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pin", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).hasSecureLockScreen();
+ verifyNoMoreInteractions(mLockPatternUtils);
+ }
+
+ @Test
public void testChangePassword() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
@@ -115,6 +127,16 @@
}
@Test
+ public void testChangePassword_noLockScreen() throws Exception {
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-password", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).hasSecureLockScreen();
+ verifyNoMoreInteractions(mLockPatternUtils);
+ }
+
+ @Test
public void testChangePattern() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
@@ -126,6 +148,16 @@
}
@Test
+ public void testChangePattern_noLockScreen() throws Exception {
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pattern", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).hasSecureLockScreen();
+ verifyNoMoreInteractions(mLockPatternUtils);
+ }
+
+ @Test
public void testClear() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
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 94e02bc..0595a5b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -40,10 +40,10 @@
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
-import java.util.ArrayList;
-
import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+
/**
* runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests
@@ -448,6 +448,37 @@
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
}
+ public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
+ final String password = "password";
+ final String pattern = "123654";
+ final String token = "some-high-entropy-secure-token";
+
+ mHasSecureLockScreen = false;
+ enableSyntheticPassword();
+ long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
+ assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+
+ try {
+ mLocalService.setLockCredentialWithToken(password,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token.getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ fail("An exception should have been thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Success - the exception was expected.
+ }
+ assertFalse(mService.havePassword(PRIMARY_USER_ID));
+
+ try {
+ mLocalService.setLockCredentialWithToken(pattern,
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token.getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ fail("An exception should have been thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Success - the exception was expected.
+ }
+ assertFalse(mService.havePattern(PRIMARY_USER_ID));
+ }
+
public void testgetHashFactorPrimaryUser() throws RemoteException {
final String password = "password";
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,