Merge changes from topics "cc-implicit-2", "cc-implicit"
* changes:
Deprecate CC and add BP#buildIntent()
Revert "Add plumbing for setRequireConfirmation to CC"
diff --git a/api/current.txt b/api/current.txt
index bb6dbeb..602ec41 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5101,7 +5101,7 @@
}
public class KeyguardManager {
- method public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence);
+ method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence);
method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
method @Deprecated public boolean inKeyguardRestrictedInputMode();
method public boolean isDeviceLocked();
@@ -16498,6 +16498,7 @@
ctor public BiometricPrompt.Builder(android.content.Context);
method public android.hardware.biometrics.BiometricPrompt build();
method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
+ method public android.hardware.biometrics.BiometricPrompt.Builder setEnableFallback(boolean);
method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
method public android.hardware.biometrics.BiometricPrompt.Builder setRequireConfirmation(boolean);
method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 181acce..f522d71 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.hardware.biometrics.BiometricPrompt;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -87,6 +88,12 @@
"android.app.action.CONFIRM_FRP_CREDENTIAL";
/**
+ * @hide
+ */
+ public static final String EXTRA_BIOMETRIC_PROMPT_BUNDLE =
+ "android.app.extra.BIOMETRIC_PROMPT_BUNDLE";
+
+ /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
@@ -101,12 +108,6 @@
public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
/**
- * A boolean value to forward to {@link android.hardware.biometrics.BiometricPrompt}.
- * @hide
- */
- public static final String EXTRA_USE_IMPLICIT = "android.app.extra.USE_IMPLICIT";
-
- /**
* A CharSequence description to show to the user on the alternate button when used with
* {@link #ACTION_CONFIRM_FRP_CREDENTIAL}.
* @hide
@@ -124,44 +125,23 @@
public static final int RESULT_ALTERNATE = 1;
/**
- * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
- * for the current user of the device. The caller is expected to launch this activity using
- * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
+ * @deprecated see {@link BiometricPrompt.Builder#setEnableFallback(boolean)}
+ *
+ * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics
+ * if enrolled) for the current user of the device. The caller is expected to launch this
+ * activity using {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
* {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
*
- * @param title Title to be shown on the dialog.
- * @param description Description to be shown on the dialog.
* @return the intent for launching the activity or null if no password is required.
**/
+ @Deprecated
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
- public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
- return createConfirmDeviceCredentialIntent(title, description, false /* useImplicit */);
- }
-
- /**
- * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
- * for the current user of the device. The caller is expected to launch this activity using
- * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
- * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
- *
- * @param title Title to be shown on the dialog.
- * @param description Description to be shown on the dialog.
- * @param useImplicit If useImplicit is set to true, ConfirmDeviceCredentials will invoke
- * {@link android.hardware.biometrics.BiometricPrompt} with
- * {@link android.hardware.biometrics.BiometricPrompt.Builder#setRequireConfirmation(
- * boolean)} set to false.
- * @return the intent for launching the activity or null if no password is required.
- * @hide
- */
- public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description,
- boolean useImplicit) {
- if (!isDeviceSecure()) {
- return null;
- }
+ public Intent createConfirmDeviceCredentialIntent(CharSequence title,
+ CharSequence description) {
+ if (!isDeviceSecure()) return null;
Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
intent.putExtra(EXTRA_TITLE, title);
intent.putExtra(EXTRA_DESCRIPTION, description);
- intent.putExtra(EXTRA_USE_IMPLICIT, useImplicit);
// explicitly set the package for security
intent.setPackage(getSettingsPackageForIntent(intent));
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 8aac1bf..5afe1a9 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -171,5 +171,39 @@
Slog.w(TAG, "resetTimeout(): Service not connected");
}
}
+
+ /**
+ * TODO(b/123378871): Remove when moved.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onConfirmDeviceCredentialSuccess() {
+ if (mService != null) {
+ try {
+ mService.onConfirmDeviceCredentialSuccess();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "onConfirmDeviceCredentialSuccess(): Service not connected");
+ }
+ }
+
+ /**
+ * TODO(b/123378871): Remove when moved.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onConfirmDeviceCredentialError(int error, String message) {
+ if (mService != null) {
+ try {
+ mService.onConfirmDeviceCredentialError(error, message);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "onConfirmDeviceCredentialError(): Service not connected");
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c69b68e4..ec62aba 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -77,6 +77,10 @@
* @hide
*/
public static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation";
+ /**
+ * @hide
+ */
+ public static final String KEY_ENABLE_FALLBACK = "enable_fallback";
/**
* Error/help message will show for this amount of time.
@@ -242,6 +246,18 @@
}
/**
+ * The user will first be prompted to authenticate with biometrics, but also given the
+ * option to authenticate with their device PIN, pattern, or password.
+ * @param enable When true, the prompt will fall back to ask for the user's device
+ * credentials (PIN, pattern, or password).
+ * @return
+ */
+ public Builder setEnableFallback(boolean enable) {
+ mBundle.putBoolean(KEY_ENABLE_FALLBACK, enable);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
* @return a {@link BiometricPrompt}
* @throws IllegalArgumentException if any of the required fields are not set.
@@ -250,11 +266,15 @@
final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE);
+ final boolean enableFallback = mBundle.getBoolean(KEY_ENABLE_FALLBACK);
if (TextUtils.isEmpty(title) && !useDefaultTitle) {
throw new IllegalArgumentException("Title must be set and non-empty");
- } else if (TextUtils.isEmpty(negative)) {
+ } else if (TextUtils.isEmpty(negative) && !enableFallback) {
throw new IllegalArgumentException("Negative text must be set and non-empty");
+ } else if (!TextUtils.isEmpty(negative) && enableFallback) {
+ throw new IllegalArgumentException("Can't have both negative button behavior"
+ + " and fallback enabled");
}
return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
}
@@ -514,6 +534,9 @@
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
+ if (mBundle.getBoolean(KEY_ENABLE_FALLBACK)) {
+ throw new IllegalArgumentException("Fallback not supported with crypto");
+ }
authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId());
}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index de828f2..e4336d1 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -51,4 +51,12 @@
// Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
void resetTimeout(in byte [] token);
+
+ // TODO(b/123378871): Remove when moved.
+ // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's
+ // setEnableFallback method, since there's no way for us to intercept onActivityResult.
+ // CDCA is launched from BiometricService (startActivityAsUser) instead of *ForResult.
+ void onConfirmDeviceCredentialSuccess();
+ // TODO(b/123378871): Remove when moved.
+ void onConfirmDeviceCredentialError(int error, String message);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 41fedc5..15d66e6 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -29,10 +29,12 @@
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityTaskManager;
+import android.app.KeyguardManager;
import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -377,6 +379,13 @@
new BiometricTaskStackListener();
private final Random mRandom = new Random();
+ // TODO(b/123378871): Remove when moved.
+ // When BiometricPrompt#setEnableFallback is set to true, we need to store the client (app)
+ // receiver. BiometricService internally launches CDCA which invokes BiometricService to
+ // start authentication (normal path). When auth is success/rejected, CDCA will use an aidl
+ // method to poke BiometricService - the result will then be forwarded to this receiver.
+ private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
+
// The current authentication session, null if idle/done. We need to track both the current
// and pending sessions since errors may be sent to either.
private AuthSession mCurrentAuthSession;
@@ -705,6 +714,22 @@
}
}
+ // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
+ // we can't get activity results. Store the receiver somewhere so we can forward the
+ // result back to the client.
+ // TODO(b/123378871): Remove when moved.
+ if (bundle.getBoolean(BiometricPrompt.KEY_ENABLE_FALLBACK)) {
+ mConfirmDeviceCredentialReceiver = receiver;
+ final KeyguardManager kgm = getContext().getSystemService(KeyguardManager.class);
+ // Use this so we don't need to duplicate logic..
+ final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
+ null /* description */);
+ // Then give it the bundle to do magic behavior..
+ intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
+ getContext().startActivityAsUser(intent, UserHandle.CURRENT);
+ return;
+ }
+
mHandler.post(() -> {
final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
final int modality = result.first;
@@ -745,6 +770,36 @@
});
}
+ @Override // Binder call
+ public void onConfirmDeviceCredentialSuccess() {
+ checkInternalPermission();
+ if (mConfirmDeviceCredentialReceiver == null) {
+ Slog.w(TAG, "onCDCASuccess null!");
+ return;
+ }
+ try {
+ mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ mConfirmDeviceCredentialReceiver = null;
+ }
+
+ @Override // Binder call
+ public void onConfirmDeviceCredentialError(int error, String message) {
+ checkInternalPermission();
+ if (mConfirmDeviceCredentialReceiver == null) {
+ Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
+ return;
+ }
+ try {
+ mConfirmDeviceCredentialReceiver.onError(error, message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ mConfirmDeviceCredentialReceiver = null;
+ }
+
/**
* authenticate() (above) which is called from BiometricPrompt determines which
* modality/modalities to start authenticating with. authenticateInternal() should only be