diff options
| author | 2019-10-03 15:32:37 -0700 | |
|---|---|---|
| committer | 2019-10-04 13:13:12 -0700 | |
| commit | c70d6b83e8360623add93d90d71b0b84c6733116 (patch) | |
| tree | a76c47937acddb268f3306dbb581eee46657db94 | |
| parent | 8110ebb57c67556f6e6bdc43d60a5f33b8b17e57 (diff) | |
17/n: Show credential UI if setDeviceCredentialAllowed(true) and no biometrics
Also, get credential type after userId is set. Otherwise the UI is
incorrect.
Bug: 140127687
Test: atest BiometricServiceTest
Test: manual test with managed profile, one-lock disabled, with/without
fingerprint, and with different types of credentials between
owner and managed profile
Change-Id: Ibaf537acf6190458d093a404d9b20d279937a6cc
5 files changed, 126 insertions, 67 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java index 99a40ba5ebf4..8df072e9b99e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java @@ -51,12 +51,6 @@ public class AuthCredentialPasswordView extends AuthCredentialView super.onFinishInflate(); mPasswordField = findViewById(R.id.lockPassword); mPasswordField.setOnEditorActionListener(this); - - if (mCredentialType == Utils.CREDENTIAL_PIN) { - mPasswordField.setInputType( - InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD); - } - mPasswordField.setOnKeyListener((v, keyCode, event) -> { if (keyCode != KeyEvent.KEYCODE_BACK) { return false; @@ -72,6 +66,11 @@ public class AuthCredentialPasswordView extends AuthCredentialView protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (mCredentialType == Utils.CREDENTIAL_PIN) { + mPasswordField.setInputType( + InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD); + } + // Wait a bit to focus the field so the focusable flag on the window is already set then. post(() -> { mPasswordField.requestFocus(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index 1c75ae2dccde..8c8611e49dfb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -166,6 +166,8 @@ public abstract class AuthCredentialView extends LinearLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); + mCredentialType = Utils.getCredentialType(mContext, mUserId); + setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE)); setTextOrHide(mSubtitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE)); @@ -200,7 +202,6 @@ public abstract class AuthCredentialView extends LinearLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); - mCredentialType = Utils.getCredentialType(mContext, mUserId); mTitleView = findViewById(R.id.title); mSubtitleView = findViewById(R.id.subtitle); mDescriptionView = findViewById(R.id.description); diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 1003bf7856e7..4f1db3c96faf 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -212,8 +212,7 @@ public class BiometricService extends SystemService { } boolean isAllowDeviceCredential() { - final int authenticators = mBundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); - return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0; + return Utils.isDeviceCredentialAllowed(mBundle); } } @@ -613,7 +612,7 @@ public class BiometricService extends SystemService { checkInternalPermission(); } - combineAuthenticatorBundles(bundle); + Utils.combineAuthenticatorBundles(bundle); // Check the usage of this in system server. Need to remove this check if it becomes // a public API. @@ -1392,8 +1391,14 @@ public class BiometricService extends SystemService { final int modality = result.first; final int error = result.second; - // Check for errors, notify callback, and return - if (error != BiometricConstants.BIOMETRIC_SUCCESS) { + final boolean credentialAllowed = Utils.isDeviceCredentialAllowed(bundle); + + if (error != BiometricConstants.BIOMETRIC_SUCCESS && credentialAllowed) { + // If there's a problem but device credential is allowed, only show credential UI. + bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, + Authenticator.TYPE_CREDENTIAL); + } else if (error != BiometricConstants.BIOMETRIC_SUCCESS) { + // Check for errors, notify callback, and return try { final String hardwareUnavailable = getContext().getString(R.string.biometric_error_hw_unavailable); @@ -1450,27 +1455,49 @@ public class BiometricService extends SystemService { // with the cookie. Once all cookies are received, we can show the prompt // and let the services start authenticating. The cookie should be non-zero. final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); Slog.d(TAG, "Creating auth session. Modality: " + modality - + ", cookie: " + cookie); - final HashMap<Integer, Integer> authenticators = new HashMap<>(); - authenticators.put(modality, cookie); - mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, + + ", cookie: " + cookie + + ", authenticators: " + authenticators); + final HashMap<Integer, Integer> modalities = new HashMap<>(); + + // If it's only device credential, we don't need to wait - LockSettingsService is + // always ready to check credential (SystemUI invokes that path). + if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) { + modalities.put(modality, cookie); + } + mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId, receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, modality, requireConfirmation); - mPendingAuthSession.mState = STATE_AUTH_CALLED; - // No polymorphism :( - if ((modality & TYPE_FINGERPRINT) != 0) { - mFingerprintService.prepareForAuthentication(token, sessionId, userId, - mInternalReceiver, opPackageName, cookie, - callingUid, callingPid, callingUserId); - } - if ((modality & TYPE_IRIS) != 0) { - Slog.w(TAG, "Iris unsupported"); - } - if ((modality & TYPE_FACE) != 0) { - mFaceService.prepareForAuthentication(requireConfirmation, - token, sessionId, userId, mInternalReceiver, opPackageName, - cookie, callingUid, callingPid, callingUserId); + + if (authenticators == Authenticator.TYPE_CREDENTIAL) { + mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL; + mCurrentAuthSession = mPendingAuthSession; + mPendingAuthSession = null; + + mStatusBarService.showAuthenticationDialog( + mCurrentAuthSession.mBundle, + mInternalReceiver, + 0 /* biometricModality */, + false /* requireConfirmation */, + mCurrentAuthSession.mUserId, + mCurrentAuthSession.mOpPackageName); + } else { + mPendingAuthSession.mState = STATE_AUTH_CALLED; + // No polymorphism :( + if ((modality & TYPE_FINGERPRINT) != 0) { + mFingerprintService.prepareForAuthentication(token, sessionId, userId, + mInternalReceiver, opPackageName, cookie, + callingUid, callingPid, callingUserId); + } + if ((modality & TYPE_IRIS) != 0) { + Slog.w(TAG, "Iris unsupported"); + } + if ((modality & TYPE_FACE) != 0) { + mFaceService.prepareForAuthentication(requireConfirmation, + token, sessionId, userId, mInternalReceiver, opPackageName, + cookie, callingUid, callingPid, callingUserId); + } } } catch (RemoteException e) { Slog.e(TAG, "Unable to start authentication", e); @@ -1536,38 +1563,4 @@ public class BiometricService extends SystemService { Slog.e(TAG, "Unable to cancel authentication"); } } - - - /** - * Combine {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with - * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible - * enough. - */ - @VisibleForTesting - static void combineAuthenticatorBundles(Bundle bundle) { - boolean biometricEnabled = true; // enabled by default - boolean credentialEnabled = false; // disabled by default - if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false)) { - credentialEnabled = true; - } - if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) { - final int authenticatorFlags = - bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); - biometricEnabled = (authenticatorFlags & Authenticator.TYPE_BIOMETRIC) != 0; - // Using both KEY_ALLOW_DEVICE_CREDENTIAL and KEY_AUTHENTICATORS_ALLOWED together - // is not supported. Default to overwriting. - credentialEnabled = (authenticatorFlags & Authenticator.TYPE_CREDENTIAL) != 0; - } - - bundle.remove(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL); - - int authenticators = 0; - if (biometricEnabled) { - authenticators |= Authenticator.TYPE_BIOMETRIC; - } - if (credentialEnabled) { - authenticators |= Authenticator.TYPE_CREDENTIAL; - } - bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators); - } } diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 4fa29ac541f9..ed5f9de01bcf 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -17,10 +17,15 @@ package com.android.server.biometrics; import android.content.Context; +import android.hardware.biometrics.Authenticator; +import android.hardware.biometrics.BiometricPrompt; import android.os.Build; +import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; +import com.android.internal.annotations.VisibleForTesting; + public class Utils { public static boolean isDebugEnabled(Context context, int targetUserId) { if (targetUserId == UserHandle.USER_NULL) { @@ -38,4 +43,43 @@ public class Utils { } return true; } + + /** + * Combine {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with + * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible + * enough. + */ + public static void combineAuthenticatorBundles(Bundle bundle) { + boolean biometricEnabled = true; // enabled by default + boolean credentialEnabled = bundle.getBoolean( + BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false); + if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) { + final int authenticatorFlags = + bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); + biometricEnabled = (authenticatorFlags & Authenticator.TYPE_BIOMETRIC) != 0; + // Using both KEY_ALLOW_DEVICE_CREDENTIAL and KEY_AUTHENTICATORS_ALLOWED together + // is not supported. Default to overwriting. + credentialEnabled = (authenticatorFlags & Authenticator.TYPE_CREDENTIAL) != 0; + } + + bundle.remove(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL); + + int authenticators = 0; + if (biometricEnabled) { + authenticators |= Authenticator.TYPE_BIOMETRIC; + } + if (credentialEnabled) { + authenticators |= Authenticator.TYPE_CREDENTIAL; + } + bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators); + } + + /** + * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)} + * @return true if device credential allowed. + */ + public static boolean isDeviceCredentialAllowed(Bundle bundle) { + final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); + return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0; + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 555b927551aa..4aeeb0af1bf8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -353,6 +353,28 @@ public class BiometricServiceTest { } @Test + public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); + when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false); + invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + true /* requireConfirmation */, true /* allowDeviceCredential */); + waitForIdle(); + + assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL, + mBiometricService.mCurrentAuthSession.mState); + assertEquals(Authenticator.TYPE_CREDENTIAL, + mBiometricService.mCurrentAuthSession.mBundle + .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED)); + verify(mBiometricService.mStatusBarService).showAuthenticationDialog( + eq(mBiometricService.mCurrentAuthSession.mBundle), + any(IBiometricServiceReceiverInternal.class), + eq(0 /* biometricModality */), + anyBoolean() /* requireConfirmation */, + anyInt() /* userId */, + eq(TEST_PACKAGE_NAME)); + } + + @Test public void testAuthenticate_happyPathWithConfirmation() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE); invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, @@ -624,7 +646,7 @@ public class BiometricServiceTest { bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true); authenticators = Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC; bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators); - BiometricService.combineAuthenticatorBundles(bundle); + Utils.combineAuthenticatorBundles(bundle); assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)); assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED)); @@ -638,7 +660,7 @@ public class BiometricServiceTest { bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true); authenticators = Authenticator.TYPE_BIOMETRIC; bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators); - BiometricService.combineAuthenticatorBundles(bundle); + Utils.combineAuthenticatorBundles(bundle); assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)); assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED)); @@ -651,7 +673,7 @@ public class BiometricServiceTest { bundle = new Bundle(); authenticators = Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL; bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators); - BiometricService.combineAuthenticatorBundles(bundle); + Utils.combineAuthenticatorBundles(bundle); assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)); assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED)); } |