summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kevin Chyn <kchyn@google.com> 2019-10-03 15:32:37 -0700
committer Kevin Chyn <kchyn@google.com> 2019-10-04 13:13:12 -0700
commitc70d6b83e8360623add93d90d71b0b84c6733116 (patch)
treea76c47937acddb268f3306dbb581eee46657db94
parent8110ebb57c67556f6e6bdc43d60a5f33b8b17e57 (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
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java3
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java107
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java44
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java28
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));
}