Merge "Remove all biometrics data of a user when password is cleared."

This commit is contained in:
Alex Johnston
2019-10-04 12:58:17 +00:00
committed by Android (Google) Code Review
6 changed files with 184 additions and 9 deletions

View File

@@ -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,

View File

@@ -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();

View File

@@ -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

View File

@@ -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";

View File

@@ -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() {

View File

@@ -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.