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;
/**
* @hide
*/
+ public static final String KEY_USE_DEFAULT_TITLE = "use_default_title";
+ /**
+ * @hide
+ */
public static final String KEY_SUBTITLE = "subtitle";
/**
* @hide
}
/**
+ * 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
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");
<!-- 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>
<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" />
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);
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;
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();
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;
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();
private void checkInternalPermission() {
getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
- "Must have MANAGE_BIOMETRIC permission");
+ "Must have USE_BIOMETRIC_INTERNAL permission");
}
private void checkPermission() {
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;
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;
}
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.