Merge "Remove all biometrics data of a user when password is cleared."
This commit is contained in:
committed by
Android (Google) Code Review
commit
9e1f99359a
@@ -61,7 +61,10 @@ import android.database.ContentObserver;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.hardware.authsecret.V1_0.IAuthSecret;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.face.Face;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
@@ -117,6 +120,7 @@ import com.android.internal.widget.LockPatternUtils.CredentialType;
|
||||
import com.android.internal.widget.LockSettingsInternal;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
|
||||
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
|
||||
@@ -387,8 +391,15 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return new Handler();
|
||||
public ServiceThread getServiceThread() {
|
||||
ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
|
||||
true /*allowIo*/);
|
||||
handlerThread.start();
|
||||
return handlerThread;
|
||||
}
|
||||
|
||||
public Handler getHandler(ServiceThread handlerThread) {
|
||||
return new Handler(handlerThread.getLooper());
|
||||
}
|
||||
|
||||
public LockSettingsStorage getStorage() {
|
||||
@@ -483,6 +494,23 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
public boolean isGsiRunning() {
|
||||
return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
|
||||
}
|
||||
|
||||
public FingerprintManager getFingerprintManager() {
|
||||
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
|
||||
return (FingerprintManager) mContext.getSystemService(Context.FINGERPRINT_SERVICE);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public FaceManager getFaceManager() {
|
||||
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
|
||||
return (FaceManager) mContext.getSystemService(Context.FACE_SERVICE);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public LockSettingsService(Context context) {
|
||||
@@ -495,7 +523,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
mContext = injector.getContext();
|
||||
mKeyStore = injector.getKeyStore();
|
||||
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
|
||||
mHandler = injector.getHandler();
|
||||
mHandler = injector.getHandler(injector.getServiceThread());
|
||||
mStrongAuth = injector.getStrongAuth();
|
||||
mActivityManager = injector.getActivityManager();
|
||||
|
||||
@@ -2713,6 +2741,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
fixateNewestUserKeyAuth(userId);
|
||||
unlockKeystore(auth.deriveKeyStorePassword(), userId);
|
||||
setKeystorePassword(null, userId);
|
||||
removeBiometricsForUser(userId);
|
||||
}
|
||||
setSyntheticPasswordHandleLocked(newHandle, userId);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
|
||||
@@ -2728,6 +2757,85 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return newHandle;
|
||||
}
|
||||
|
||||
private void removeBiometricsForUser(int userId) {
|
||||
removeAllFingerprintForUser(userId);
|
||||
removeAllFaceForUser(userId);
|
||||
}
|
||||
|
||||
private void removeAllFingerprintForUser(final int userId) {
|
||||
FingerprintManager mFingerprintManager = mInjector.getFingerprintManager();
|
||||
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
|
||||
if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
|
||||
mFingerprintManager.setActiveUser(userId);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
// For the purposes of M and N, groupId is the same as userId.
|
||||
Fingerprint finger = new Fingerprint(null, userId, 0, 0);
|
||||
mFingerprintManager.remove(finger, userId,
|
||||
fingerprintManagerRemovalCallback(latch));
|
||||
try {
|
||||
latch.await(10000, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Slog.e(TAG, "Latch interrupted when removing fingerprint", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAllFaceForUser(final int userId) {
|
||||
FaceManager mFaceManager = mInjector.getFaceManager();
|
||||
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
|
||||
if (mFaceManager.hasEnrolledTemplates(userId)) {
|
||||
mFaceManager.setActiveUser(userId);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
Face face = new Face(null, 0, 0);
|
||||
mFaceManager.remove(face, userId, faceManagerRemovalCallback(latch));
|
||||
try {
|
||||
latch.await(10000, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Slog.e(TAG, "Latch interrupted when removing face", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FingerprintManager.RemovalCallback fingerprintManagerRemovalCallback(
|
||||
CountDownLatch latch) {
|
||||
return new FingerprintManager.RemovalCallback() {
|
||||
@Override
|
||||
public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence err) {
|
||||
Slog.e(TAG, String.format(
|
||||
"Can't remove fingerprint %d in group %d. Reason: %s",
|
||||
fp.getBiometricId(), fp.getGroupId(), err));
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(Fingerprint fp, int remaining) {
|
||||
if (remaining == 0) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private FaceManager.RemovalCallback faceManagerRemovalCallback(CountDownLatch latch) {
|
||||
return new FaceManager.RemovalCallback() {
|
||||
@Override
|
||||
public void onRemovalError(Face face, int errMsgId, CharSequence err) {
|
||||
Slog.e(TAG, String.format("Can't remove face %d. Reason: %s",
|
||||
face.getBiometricId(), err));
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(Face face, int remaining) {
|
||||
if (remaining == 0) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@GuardedBy("mSpManager")
|
||||
private boolean spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
|
||||
byte[] savedCredential, int requestedQuality, int userId,
|
||||
|
||||
@@ -32,8 +32,11 @@ import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.app.admin.DeviceStateCache;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.hardware.authsecret.V1_0.IAuthSecret;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.FileUtils;
|
||||
import android.os.IProgressListener;
|
||||
import android.os.RemoteException;
|
||||
@@ -95,6 +98,9 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
RecoverableKeyStoreManager mRecoverableKeyStoreManager;
|
||||
UserManagerInternal mUserManagerInternal;
|
||||
DeviceStateCache mDeviceStateCache;
|
||||
FingerprintManager mFingerprintManager;
|
||||
FaceManager mFaceManager;
|
||||
PackageManager mPackageManager;
|
||||
protected boolean mHasSecureLockScreen;
|
||||
|
||||
@Override
|
||||
@@ -114,6 +120,9 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);
|
||||
mUserManagerInternal = mock(UserManagerInternal.class);
|
||||
mDeviceStateCache = mock(DeviceStateCache.class);
|
||||
mFingerprintManager = mock(FingerprintManager.class);
|
||||
mFaceManager = mock(FaceManager.class);
|
||||
mPackageManager = mock(PackageManager.class);
|
||||
|
||||
LocalServices.removeServiceForTest(LockSettingsInternal.class);
|
||||
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
|
||||
@@ -123,7 +132,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
|
||||
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
|
||||
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
|
||||
mock(KeyguardManager.class));
|
||||
mock(KeyguardManager.class), mFingerprintManager, mFaceManager, mPackageManager);
|
||||
mStorage = new LockSettingsStorageTestable(mContext,
|
||||
new File(getContext().getFilesDir(), "locksettings"));
|
||||
File storageDir = mStorage.mStorageDir;
|
||||
@@ -181,6 +190,8 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
|
||||
when(mUserManagerInternal.isDeviceManaged()).thenReturn(true);
|
||||
when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
|
||||
mockBiometricsHardwareFingerprintsAndTemplates(PRIMARY_USER_ID);
|
||||
mockBiometricsHardwareFingerprintsAndTemplates(MANAGED_PROFILE_USER_ID);
|
||||
|
||||
mLocalService = LocalServices.getService(LockSettingsInternal.class);
|
||||
}
|
||||
@@ -233,6 +244,18 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
return sm;
|
||||
}
|
||||
|
||||
private void mockBiometricsHardwareFingerprintsAndTemplates(int userId) {
|
||||
// Hardware must be detected and fingerprints must be enrolled
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(userId)).thenReturn(true);
|
||||
|
||||
// Hardware must be detected and templates must be enrolled
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.hasEnrolledTemplates(userId)).thenReturn(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
|
||||
@@ -23,7 +23,6 @@ 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;
|
||||
@@ -32,6 +31,7 @@ import android.security.KeyStore;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -70,8 +70,8 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Handler getHandler() {
|
||||
return new Handler(Looper.getMainLooper());
|
||||
public Handler getHandler(ServiceThread handlerThread) {
|
||||
return new Handler(handlerThread.getLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -330,6 +330,27 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void testSetLockCredential_nullCredential_removeBiometrics() throws RemoteException {
|
||||
final String oldCredential = "oldPassword";
|
||||
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID,
|
||||
oldCredential,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
|
||||
|
||||
// Verify fingerprint is removed
|
||||
verify(mFingerprintManager).remove(any(), eq(PRIMARY_USER_ID), any());
|
||||
verify(mFaceManager).remove(any(), eq(PRIMARY_USER_ID), any());
|
||||
|
||||
verify(mFingerprintManager).remove(any(), eq(MANAGED_PROFILE_USER_ID), any());
|
||||
verify(mFaceManager).remove(any(), eq(MANAGED_PROFILE_USER_ID), any());
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials()
|
||||
throws Exception {
|
||||
final String parentPassword = "parentPassword";
|
||||
|
||||
@@ -24,8 +24,11 @@ import android.app.KeyguardManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.FileUtils;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
@@ -86,7 +89,9 @@ public class LockSettingsStorageTests extends AndroidTestCase {
|
||||
|
||||
MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager,
|
||||
mock(NotificationManager.class), mock(DevicePolicyManager.class),
|
||||
mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class));
|
||||
mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class),
|
||||
mock(FingerprintManager.class), mock(FaceManager.class),
|
||||
mock(PackageManager.class));
|
||||
mStorage = new LockSettingsStorageTestable(context,
|
||||
new File(getContext().getFilesDir(), "locksettings"));
|
||||
mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
|
||||
|
||||
@@ -23,6 +23,8 @@ import android.app.trust.TrustManager;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
|
||||
@@ -34,11 +36,15 @@ public class MockLockSettingsContext extends ContextWrapper {
|
||||
private StorageManager mStorageManager;
|
||||
private TrustManager mTrustManager;
|
||||
private KeyguardManager mKeyguardManager;
|
||||
private FingerprintManager mFingerprintManager;
|
||||
private FaceManager mFaceManager;
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
public MockLockSettingsContext(Context base, UserManager userManager,
|
||||
NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
|
||||
StorageManager storageManager, TrustManager trustManager,
|
||||
KeyguardManager keyguardManager) {
|
||||
KeyguardManager keyguardManager, FingerprintManager fingerprintManager,
|
||||
FaceManager faceManager, PackageManager packageManager) {
|
||||
super(base);
|
||||
mUserManager = userManager;
|
||||
mNotificationManager = notificationManager;
|
||||
@@ -46,6 +52,9 @@ public class MockLockSettingsContext extends ContextWrapper {
|
||||
mStorageManager = storageManager;
|
||||
mTrustManager = trustManager;
|
||||
mKeyguardManager = keyguardManager;
|
||||
mFingerprintManager = fingerprintManager;
|
||||
mFaceManager = faceManager;
|
||||
mPackageManager = packageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,11 +71,20 @@ public class MockLockSettingsContext extends ContextWrapper {
|
||||
return mTrustManager;
|
||||
} else if (KEYGUARD_SERVICE.equals(name)) {
|
||||
return mKeyguardManager;
|
||||
} else if (FINGERPRINT_SERVICE.equals(name)) {
|
||||
return mFingerprintManager;
|
||||
} else if (FACE_SERVICE.equals(name)) {
|
||||
return mFaceManager;
|
||||
} else {
|
||||
throw new RuntimeException("System service not mocked: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return mPackageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceCallingOrSelfPermission(String permission, String message) {
|
||||
// Skip permission checks for unit tests.
|
||||
|
||||
Reference in New Issue
Block a user