Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6183cf99 authored by Alex Johnston's avatar Alex Johnston
Browse files

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

Previously, the biometrics were only cleared if the password was cleared from the Settings.
Moved the logic from the Settings app to the system server side.
Now, the biometrics will be removed no matter how the password is cleared (Settings, adb, TestDPC).

Bug: 130653263
Test: Atest LockSettingsServiceTests
      manual testing from Settings, adb and TestDPC

Change-Id: I840856b2e2db5e595d33583fd79359fde6bccb6f
parent 87dfbb36
Loading
Loading
Loading
Loading
+111 −3
Original line number Diff line number Diff line
@@ -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,
+24 −1
Original line number Diff line number Diff line
@@ -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();
+3 −3
Original line number Diff line number Diff line
@@ -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
+21 −0
Original line number Diff line number Diff line
@@ -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";
+6 −1
Original line number Diff line number Diff line
@@ -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() {
Loading