OSDN Git Service

Add protected BiometricPrompt API to allow default title
authorKevin Chyn <kchyn@google.com>
Mon, 8 Oct 2018 22:40:05 +0000 (15:40 -0700)
committerKevin Chyn <kchyn@google.com>
Tue, 9 Oct 2018 03:38:00 +0000 (20:38 -0700)
ConfirmDeviceCredentials sometimes shows up without a title. Since CC
is now using BiometricPrompt, we need at least a private API that allows
the client to have the dialog show a default title.

Bug: 111461540

Test: Manual test with modified BiometricPromptDemo, to launch CC with
      empty string

Change-Id: I02a3c9327635c04f201f76754f7c0e1135e5ff36

core/java/android/hardware/biometrics/BiometricPrompt.java
core/res/res/values/strings.xml
core/res/res/values/symbols.xml
packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
services/core/java/com/android/server/biometrics/AuthenticationClient.java
services/core/java/com/android/server/biometrics/BiometricService.java
services/core/java/com/android/server/biometrics/BiometricServiceBase.java

index 83998cc..7952c41 100644 (file)
@@ -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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
     /**
      * @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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
         }
 
         /**
+         * 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 class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
         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");
index 6d0127a..5c14e39 100644 (file)
     <!-- 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>
 
index 72ae0d6..ea44ab2 100644 (file)
   <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" />
 
index c90861e..7d77929 100644 (file)
@@ -200,7 +200,9 @@ public abstract class BiometricDialogView extends LinearLayout {
         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);
index bdbb0a4..61836fd 100644 (file)
@@ -27,6 +27,7 @@ import android.os.IBinder;
 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 @@ public abstract class AuthenticationClient extends ClientMonitor {
         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();
index 2b59386..25d5133 100644 (file)
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager;
 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;
@@ -255,6 +256,12 @@ public class BiometricService extends SystemService {
                 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();
@@ -400,7 +407,7 @@ public class BiometricService extends SystemService {
 
     private void checkInternalPermission() {
         getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
-                "Must have MANAGE_BIOMETRIC permission");
+                "Must have USE_BIOMETRIC_INTERNAL permission");
     }
 
     private void checkPermission() {
index 71bf560..74d742a 100644 (file)
@@ -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.Slog;
 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 @@ public abstract class BiometricServiceBase extends 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.