Merge changes I02a3c932,I04b6f64d
* changes:
Add protected BiometricPrompt API to allow default title
Add setActiveUser to BiometricManager/Service
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 1734b41..79e15a7a 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -201,11 +201,22 @@
throw new UnsupportedOperationException("Stub!");
}
+ /**
+ * @return true if the user has enrolled templates for this biometric.
+ */
default boolean hasEnrolledTemplates(int userId) {
throw new UnsupportedOperationException("Stub!");
}
/**
+ * Sets the active user. This is meant to be used to select the current profile
+ * to allow separate templates for work profile.
+ */
+ default void setActiveUser(int userId) {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ /**
* This call warms up the hardware and starts scanning for valid biometrics. It terminates
* when {@link AuthenticationCallback#onAuthenticationError(int,
* CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 0faecb0..1d40001 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -107,4 +107,22 @@
Slog.w(TAG, "registerEnabledOnKeyguardCallback(): Service not connected");
}
}
+
+ /**
+ * Sets the active user.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void setActiveUser(int userId) {
+ if (mService != null) {
+ try {
+ mService.setActiveUser(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "setActiveUser(): Service not connected");
+ }
+ }
}
+
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 83998cc..7952c41 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -55,6 +56,10 @@
/**
* @hide
*/
+ public static final String KEY_USE_DEFAULT_TITLE = "use_default_title";
+ /**
+ * @hide
+ */
public static final String KEY_SUBTITLE = "subtitle";
/**
* @hide
@@ -131,6 +136,17 @@
}
/**
+ * For internal use currently. Only takes effect if title is null/empty. Shows a default
+ * modality-specific title.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public Builder setUseDefaultTitle() {
+ mBundle.putBoolean(KEY_USE_DEFAULT_TITLE, true);
+ return this;
+ }
+
+ /**
* Optional: Set the subtitle to display.
* @param subtitle
* @return
@@ -206,8 +222,9 @@
public BiometricPrompt build() {
final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
+ final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE);
- if (TextUtils.isEmpty(title)) {
+ if (TextUtils.isEmpty(title) && !useDefaultTitle) {
throw new IllegalArgumentException("Title must be set and non-empty");
} else if (TextUtils.isEmpty(negative)) {
throw new IllegalArgumentException("Negative text must be set and non-empty");
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 51e4ecb..e17feff 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -43,4 +43,7 @@
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
-}
\ No newline at end of file
+
+ // Explicitly set the active user.
+ void setActiveUser(int userId);
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 7aec43e..536c4a5 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -317,6 +317,7 @@
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
+ @Override
public void setActiveUser(int userId) {
if (mService != null) {
try {
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index bf2280d..bb98211 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -521,6 +521,7 @@
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
+ @Override
public void setActiveUser(int userId) {
if (mService != null) try {
mService.setActiveUser(userId);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1e3aeae..adefa20 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1418,6 +1418,9 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_mediaLocation">Allows the app to read locations from your media collection.</string>
+ <!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
+ <string name="biometric_dialog_default_title">Application <xliff:g id="app" example="Gmail">%s</xliff:g> wants to authenticate.</string>
+
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a7b6dde..7e7c7b2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2394,6 +2394,7 @@
<java-symbol type="string" name="config_keyguardComponent" />
<!-- Biometric messages -->
+ <java-symbol type="string" name="biometric_dialog_default_title" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
<java-symbol type="string" name="biometric_not_recognized" />
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index c90861e..7d77929 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -200,7 +200,9 @@
mLastState = STATE_NONE;
updateState(STATE_AUTHENTICATING);
- title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
+ CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE);
+
+ title.setText(titleText);
title.setSelected(true);
positive.setVisibility(View.INVISIBLE);
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index bdbb0a4..61836fdd 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -27,6 +27,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.security.KeyStore;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.statusbar.IStatusBarService;
@@ -205,6 +206,16 @@
return super.onError(deviceId, error, vendorCode);
}
+ public void setTitleIfEmpty(CharSequence title) {
+ if (TextUtils.isEmpty(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
+ mBundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
+ }
+ }
+
+ public boolean isBiometricPrompt() {
+ return mBundle != null;
+ }
+
private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier)
throws RemoteException {
final BiometricServiceBase.ServiceListener listener = getListener();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 3d2f567..b80dca6 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -29,6 +29,7 @@
import android.database.ContentObserver;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricPromptReceiver;
@@ -239,7 +240,7 @@
* should not carry any state. The reality is we need to keep a tiny amount of state so that
* cancelAuthentication() can go to the right place.
*/
- private final class BiometricPromptServiceWrapper extends IBiometricService.Stub {
+ private final class BiometricServiceWrapper extends IBiometricService.Stub {
@Override // Binder call
public void authenticate(IBinder token, long sessionId, int userId,
@@ -255,6 +256,12 @@
return;
}
+ // Check the usage of this in system server. Need to remove this check if it becomes
+ // a public API.
+ if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+ checkInternalPermission();
+ }
+
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -363,7 +370,7 @@
return error;
}
- @Override
+ @Override // Binder call
public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
throws RemoteException {
checkInternalPermission();
@@ -375,6 +382,19 @@
Slog.w(TAG, "Remote exception", e);
}
}
+
+ @Override // Binder call
+ public void setActiveUser(int userId) {
+ checkInternalPermission();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mAuthenticators.size(); i++) {
+ mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
private void checkAppOp(String opPackageName, int callingUid) {
@@ -387,7 +407,7 @@
private void checkInternalPermission() {
getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
- "Must have MANAGE_BIOMETRIC permission");
+ "Must have USE_BIOMETRIC_INTERNAL permission");
}
private void checkPermission() {
@@ -455,7 +475,7 @@
}
}
- publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricPromptServiceWrapper());
+ publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
}
/**
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 71bf560..74d742a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import android.app.ActivityManager;
@@ -55,6 +56,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
@@ -704,6 +706,30 @@
}
mHandler.post(() -> {
+ if (client.isBiometricPrompt()) {
+ try {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ for (int i = 0; i < procs.size(); i++) {
+ final ActivityManager.RunningAppProcessInfo info = procs.get(i);
+ if (info.uid == callingUid && info.importance == IMPORTANCE_FOREGROUND) {
+ PackageManager pm = getContext().getPackageManager();
+ final CharSequence label = pm.getApplicationLabel(
+ pm.getApplicationInfo(info.processName,
+ PackageManager.GET_META_DATA));
+ final String title = getContext()
+ .getString(R.string.biometric_dialog_default_title, label);
+ client.setTitleIfEmpty(title);
+ break;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to get application name", e);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(getTag(), "Unable to get application name", e);
+ }
+ }
+
mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
// Get performance stats object for this user.