diff options
25 files changed, 335 insertions, 15 deletions
| diff --git a/core/api/current.txt b/core/api/current.txt index 5fb94dfe47bf..f052b85a9f44 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -18679,6 +18679,8 @@ package android.hardware.biometrics {      method @Nullable public int getAllowedAuthenticators();      method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();      method @Nullable public CharSequence getDescription(); +    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.graphics.Bitmap getLogoBitmap(); +    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public int getLogoRes();      method @Nullable public CharSequence getNegativeButtonText();      method @Nullable public CharSequence getSubtitle();      method @NonNull public CharSequence getTitle(); @@ -18728,6 +18730,8 @@ package android.hardware.biometrics {      method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);      method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);      method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean); +    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.hardware.biometrics.BiometricPrompt.Builder setLogo(@DrawableRes int); +    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.hardware.biometrics.BiometricPrompt.Builder setLogo(@NonNull android.graphics.Bitmap);      method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);      method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);      method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence); diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index a0f4d8df0bd5..c0424dbeb813 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -16,6 +16,7 @@  package android.hardware.biometrics; +import static android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG;  import static android.Manifest.permission.TEST_BIOMETRIC;  import static android.Manifest.permission.USE_BIOMETRIC;  import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; @@ -25,6 +26,7 @@ import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;  import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;  import android.annotation.CallbackExecutor; +import android.annotation.DrawableRes;  import android.annotation.FlaggedApi;  import android.annotation.IntDef;  import android.annotation.NonNull; @@ -33,6 +35,7 @@ import android.annotation.RequiresPermission;  import android.annotation.TestApi;  import android.content.Context;  import android.content.DialogInterface; +import android.graphics.Bitmap;  import android.hardware.face.FaceManager;  import android.hardware.fingerprint.FingerprintManager;  import android.os.Binder; @@ -160,6 +163,45 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan          }          /** +         * Optional: Sets the drawable resource of the logo that will be shown on the prompt. +         * +         * <p> Note that using this method is not recommended in most scenarios because the calling +         * application's icon will be used by default. Setting the logo is intended for large +         * bundled applications that perform a wide range of functions and need to show distinct +         * icons for each function. +         * +         * @param logoRes A drawable resource of the logo that will be shown on the prompt. +         * @return This builder. +         */ +        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) +        @RequiresPermission(MANAGE_BIOMETRIC_DIALOG) +        @NonNull +        public BiometricPrompt.Builder setLogo(@DrawableRes int logoRes) { +            mPromptInfo.setLogoRes(logoRes); +            return this; +        } + +        /** +         * Optional: Sets the bitmap drawable of the logo that will be shown on the prompt. +         * +         * <p> Note that using this method is not recommended in most scenarios because the calling +         * application's icon will be used by default. Setting the logo is intended for large +         * bundled applications that perform a wide range of functions and need to show distinct +         * icons for each function. +         * +         * @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt. +         * @return This builder. +         */ +        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) +        @RequiresPermission(MANAGE_BIOMETRIC_DIALOG) +        @NonNull +        public BiometricPrompt.Builder setLogo(@NonNull Bitmap logoBitmap) { +            mPromptInfo.setLogoBitmap(logoBitmap); +            return this; +        } + + +        /**           * Required: Sets the title that will be shown on the prompt.           * @param title The title to display.           * @return This builder. @@ -676,6 +718,34 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan      }      /** +     * Gets the drawable resource of the logo for the prompt, as set by +     * {@link Builder#setLogo(int)}. Currently for system applications use only. +     * +     * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set. +     */ +    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) +    @RequiresPermission(MANAGE_BIOMETRIC_DIALOG) +    @DrawableRes +    public int getLogoRes() { +        return mPromptInfo.getLogoRes(); +    } + +    /** +     * Gets the logo bitmap for the prompt, as set by {@link Builder#setLogo(Bitmap)}. Currently for +     * system applications use only. +     * +     * @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set. +     */ +    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) +    @RequiresPermission(MANAGE_BIOMETRIC_DIALOG) +    @Nullable +    public Bitmap getLogoBitmap() { +        return mPromptInfo.getLogoBitmap(); +    } + + + +    /**       * Gets the title for the prompt, as set by {@link Builder#setTitle(CharSequence)}.       * @return The title of the prompt, which is guaranteed to be non-null.       */ diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index c73ebd4dbe76..d788b37c781d 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -16,8 +16,10 @@  package android.hardware.biometrics; +import android.annotation.DrawableRes;  import android.annotation.NonNull;  import android.annotation.Nullable; +import android.graphics.Bitmap;  import android.os.Parcel;  import android.os.Parcelable; @@ -30,6 +32,8 @@ import java.util.List;   */  public class PromptInfo implements Parcelable { +    @DrawableRes private int mLogoRes = -1; +    @Nullable private Bitmap mLogoBitmap;      @NonNull private CharSequence mTitle;      private boolean mUseDefaultTitle;      @Nullable private CharSequence mSubtitle; @@ -56,6 +60,8 @@ public class PromptInfo implements Parcelable {      }      PromptInfo(Parcel in) { +        mLogoRes = in.readInt(); +        mLogoBitmap = in.readTypedObject(Bitmap.CREATOR);          mTitle = in.readCharSequence();          mUseDefaultTitle = in.readBoolean();          mSubtitle = in.readCharSequence(); @@ -98,6 +104,8 @@ public class PromptInfo implements Parcelable {      @Override      public void writeToParcel(Parcel dest, int flags) { +        dest.writeInt(mLogoRes); +        dest.writeTypedObject(mLogoBitmap, 0);          dest.writeCharSequence(mTitle);          dest.writeBoolean(mUseDefaultTitle);          dest.writeCharSequence(mSubtitle); @@ -156,9 +164,30 @@ public class PromptInfo implements Parcelable {          }          return false;      } + +    /** +     * Returns whether MANAGE_BIOMETRIC_DIALOG is contained. +     */ +    public boolean containsManageBioApiConfigurations() { +        if (mLogoRes != -1) { +            return true; +        } else if (mLogoBitmap != null) { +            return true; +        } +        return false; +    }      // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)      // Setters +    public void setLogoRes(@DrawableRes int logoRes) { +        mLogoRes = logoRes; +        checkOnlyOneLogoSet(); +    } + +    public void setLogoBitmap(@NonNull Bitmap logoBitmap) { +        mLogoBitmap = logoBitmap; +        checkOnlyOneLogoSet(); +    }      public void setTitle(CharSequence title) {          mTitle = title; @@ -244,6 +273,14 @@ public class PromptInfo implements Parcelable {      }      // Getters +    @DrawableRes +    public int getLogoRes() { +        return mLogoRes; +    } + +    public Bitmap getLogoBitmap() { +        return mLogoBitmap; +    }      public CharSequence getTitle() {          return mTitle; @@ -337,4 +374,11 @@ public class PromptInfo implements Parcelable {      public boolean isShowEmergencyCallButton() {          return mShowEmergencyCallButton;      } + +    private void checkOnlyOneLogoSet() { +        if (mLogoRes != -1 && mLogoBitmap != null) { +            throw new IllegalStateException( +                    "Exclusively one of logo resource or logo bitmap can be set"); +        } +    }  } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 42068aa14cda..4be75f83422e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -426,6 +426,7 @@ applications that come with the platform          <!-- Permissions required for CTS test - android.server.biometrics -->          <permission name="android.permission.USE_BIOMETRIC" />          <permission name="android.permission.TEST_BIOMETRIC" /> +        <permission name="android.permission.MANAGE_BIOMETRIC_DIALOG" />          <!-- Permissions required for CTS test - CtsContactsProviderTestCases -->          <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />          <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases --> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index f204e4814579..3dfc4540d6e7 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -559,6 +559,9 @@      <!-- Permission required for CTS test - android.server.biometrics -->      <uses-permission android:name="android.permission.TEST_BIOMETRIC" /> +    <!-- Permission required for CTS test - android.server.biometrics --> +    <uses-permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG" /> +      <!-- Permissions required for CTS test - NotificationManagerTest -->      <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" /> diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt index 4a39799fd64f..72e884e9e5d6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt @@ -16,6 +16,7 @@  package com.android.systemui.biometrics +import android.graphics.Bitmap  import android.hardware.biometrics.BiometricManager.Authenticators  import android.hardware.biometrics.ComponentInfoInternal  import android.hardware.biometrics.PromptContentView @@ -117,6 +118,8 @@ internal fun Collection<SensorPropertiesInternal?>.extractAuthenticatorTypes():  }  internal fun promptInfo( +    logoRes: Int = -1, +    logoBitmap: Bitmap? = null,      title: String = "title",      subtitle: String = "sub",      description: String = "desc", @@ -127,6 +130,8 @@ internal fun promptInfo(      negativeButton: String = "neg",  ): PromptInfo {      val info = PromptInfo() +    info.logoRes = logoRes +    info.logoBitmap = logoBitmap      info.title = title      info.subtitle = subtitle      info.description = description diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml index 23fbb12f3036..10f71134c4cc 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml @@ -20,6 +20,13 @@      android:layout_height="wrap_content"      android:orientation="vertical"> +    <ImageView +        android:id="@+id/logo" +        android:layout_width="@dimen/biometric_auth_icon_size" +        android:layout_height="@dimen/biometric_auth_icon_size" +        android:layout_gravity="center" +        android:scaleType="fitXY"/> +      <TextView          android:id="@+id/title"          android:layout_width="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index ab23564a1df4..57e308ff16e8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -399,7 +399,8 @@ public class AuthContainerView extends LinearLayout                      config.mPromptInfo,                      config.mUserId,                      config.mOperationId, -                    new BiometricModalities(fpProps, faceProps)); +                    new BiometricModalities(fpProps, faceProps), +                    config.mOpPackageName);              final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(                      R.layout.biometric_prompt_layout, null, false); @@ -470,7 +471,8 @@ public class AuthContainerView extends LinearLayout          mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);          mPromptSelectorInteractorProvider.get().useCredentialsForAuthentication( -                mConfig.mPromptInfo, credentialType, mConfig.mUserId, mConfig.mOperationId); +                mConfig.mPromptInfo, credentialType, mConfig.mUserId, mConfig.mOperationId, +                mConfig.mOpPackageName);          final CredentialViewModel vm = mCredentialViewModelProvider.get();          vm.setAnimateContents(animateContents);          ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java index 86802a5b58b0..02eae9cedf74 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java @@ -148,6 +148,12 @@ public class UdfpsDialogMeasureAdapter {                      || child.getId() == R.id.customized_view_container) {                  //skip description view and compute later                  continue; +            } else if (child.getId() == R.id.logo) { +                child.measure( +                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().width, +                                MeasureSpec.EXACTLY), +                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, +                                MeasureSpec.EXACTLY));              } else {                  child.measure(                          MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt index b35fbbc7bb32..ad7bb0e61178 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt @@ -55,6 +55,9 @@ interface PromptRepository {      /** The kind of credential to use (biometric, pin, pattern, etc.). */      val kind: StateFlow<PromptKind> +    /** The package name that the prompt is called from. */ +    val opPackageName: StateFlow<String?> +      /**       * If explicit confirmation is required.       * @@ -68,6 +71,7 @@ interface PromptRepository {          userId: Int,          gatekeeperChallenge: Long?,          kind: PromptKind, +        opPackageName: String,      )      /** Unset the prompt info. */ @@ -108,6 +112,9 @@ constructor(      private val _kind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.Biometric())      override val kind = _kind.asStateFlow() +    private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null) +    override val opPackageName = _opPackageName.asStateFlow() +      private val _faceSettings =          _userId.map { id -> faceSettings.forUser(id) }.distinctUntilChanged()      private val _faceSettingAlwaysRequireConfirmation = @@ -127,11 +134,13 @@ constructor(          userId: Int,          gatekeeperChallenge: Long?,          kind: PromptKind, +        opPackageName: String,      ) {          _kind.value = kind          _userId.value = userId          _challenge.value = gatekeeperChallenge          _promptInfo.value = promptInfo +        _opPackageName.value = opPackageName      }      override fun unsetPrompt() { @@ -139,6 +148,7 @@ constructor(          _userId.value = null          _challenge.value = null          _kind.value = PromptKind.Biometric() +        _opPackageName.value = null      }      companion object { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt index ac4b717a23ec..359e2e703ee6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt @@ -115,12 +115,14 @@ constructor(          @Utils.CredentialType kind: Int,          userId: Int,          challenge: Long, +        opPackageName: String,      ) {          biometricPromptRepository.setPrompt(              promptInfo,              userId,              challenge, -            kind.asBiometricPromptCredential() +            kind.asBiometricPromptCredential(), +            opPackageName,          )      } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt index 65a2c0a2490f..b3f95748ccdc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt @@ -76,6 +76,7 @@ interface PromptSelectorInteractor {          userId: Int,          challenge: Long,          modalities: BiometricModalities, +        opPackageName: String,      )      /** Use credential-based authentication instead of biometrics. */ @@ -84,6 +85,7 @@ interface PromptSelectorInteractor {          @Utils.CredentialType kind: Int,          userId: Int,          challenge: Long, +        opPackageName: String,      )      /** Unset the current authentication request. */ @@ -104,9 +106,12 @@ constructor(              promptRepository.promptInfo,              promptRepository.challenge,              promptRepository.userId, -            promptRepository.kind -        ) { promptInfo, challenge, userId, kind -> -            if (promptInfo == null || userId == null || challenge == null) { +            promptRepository.kind, +            promptRepository.opPackageName, +        ) { promptInfo, challenge, userId, kind, opPackageName -> +            if ( +                promptInfo == null || userId == null || challenge == null || opPackageName == null +            ) {                  return@combine null              } @@ -117,6 +122,7 @@ constructor(                          userInfo = BiometricUserInfo(userId = userId),                          operationInfo = BiometricOperationInfo(gatekeeperChallenge = challenge),                          modalities = kind.activeModalities, +                        opPackageName = opPackageName,                      )                  else -> null              } @@ -152,13 +158,15 @@ constructor(          promptInfo: PromptInfo,          userId: Int,          challenge: Long, -        modalities: BiometricModalities +        modalities: BiometricModalities, +        opPackageName: String,      ) {          promptRepository.setPrompt(              promptInfo = promptInfo,              userId = userId,              gatekeeperChallenge = challenge,              kind = PromptKind.Biometric(modalities), +            opPackageName = opPackageName,          )      } @@ -167,12 +175,14 @@ constructor(          @Utils.CredentialType kind: Int,          userId: Int,          challenge: Long, +        opPackageName: String,      ) {          promptRepository.setPrompt(              promptInfo = promptInfo,              userId = userId,              gatekeeperChallenge = challenge,              kind = kind.asBiometricPromptCredential(), +            opPackageName = opPackageName,          )      } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt index 437793798567..c17c8dced668 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt @@ -1,5 +1,6 @@  package com.android.systemui.biometrics.domain.model +import android.graphics.Bitmap  import android.hardware.biometrics.PromptContentView  import android.hardware.biometrics.PromptInfo  import com.android.systemui.biometrics.shared.model.BiometricModalities @@ -26,6 +27,7 @@ sealed class BiometricPromptRequest(          userInfo: BiometricUserInfo,          operationInfo: BiometricOperationInfo,          val modalities: BiometricModalities, +        val opPackageName: String,      ) :          BiometricPromptRequest(              title = info.title?.toString() ?: "", @@ -36,6 +38,8 @@ sealed class BiometricPromptRequest(              showEmergencyCallButton = info.isShowEmergencyCallButton          ) {          val contentView: PromptContentView? = info.contentView +        val logoRes: Int = info.logoRes +        val logoBitmap: Bitmap? = info.logoBitmap          val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""      } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java index 60b454e9670e..b450896729b7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java @@ -115,6 +115,12 @@ public class BiometricPromptLayout extends LinearLayout {                                  MeasureSpec.EXACTLY),                          MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().height,                                  MeasureSpec.EXACTLY)); +            } else if (child.getId() == R.id.logo) { +                child.measure( +                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().width, +                                MeasureSpec.EXACTLY), +                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, +                                MeasureSpec.EXACTLY));              } else if (child.getId() == R.id.biometric_icon) {                  child.measure(                          MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 04dc7a8da9f3..285ab4a800b6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -31,6 +31,7 @@ import android.view.View  import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO  import android.view.accessibility.AccessibilityManager  import android.widget.Button +import android.widget.ImageView  import android.widget.ScrollView  import android.widget.TextView  import androidx.lifecycle.DefaultLifecycleObserver @@ -92,6 +93,7 @@ object BiometricViewBinder {          val textColorHint =              view.resources.getColor(R.color.biometric_dialog_gray, view.context.theme) +        val logoView = view.requireViewById<ImageView>(R.id.logo)          val titleView = view.requireViewById<TextView>(R.id.title)          val subtitleView = view.requireViewById<TextView>(R.id.subtitle)          val descriptionView = view.requireViewById<TextView>(R.id.description) @@ -99,6 +101,8 @@ object BiometricViewBinder {              view.requireViewById<ScrollView>(R.id.customized_view_container)          // set selected to enable marquee unless a screen reader is enabled +        logoView.isSelected = +            !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled          titleView.isSelected =              !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled          subtitleView.isSelected = @@ -152,6 +156,7 @@ object BiometricViewBinder {                  }              } +            logoView.setImageDrawable(viewModel.logo.first())              titleView.text = viewModel.title.first()              subtitleView.text = viewModel.subtitle.first()              descriptionView.text = viewModel.description.first() @@ -183,6 +188,7 @@ object BiometricViewBinder {                      viewModel = viewModel,                      viewsToHideWhenSmall =                          listOf( +                            logoView,                              titleView,                              subtitleView,                              descriptionView, @@ -190,6 +196,7 @@ object BiometricViewBinder {                          ),                      viewsToFadeInOnSizeChange =                          listOf( +                            logoView,                              titleView,                              subtitleView,                              descriptionView, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index c3bbaedb2670..d5695f31f121 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -25,6 +25,7 @@ import android.view.ViewGroup  import android.view.WindowInsets  import android.view.WindowManager  import android.view.accessibility.AccessibilityManager +import android.widget.ImageView  import android.widget.TextView  import androidx.core.animation.addListener  import androidx.core.view.doOnLayout @@ -234,7 +235,13 @@ private fun View.isLandscape(): Boolean {  private fun View.showContentOrHide(forceHide: Boolean = false) {      val isTextViewWithBlankText = this is TextView && this.text.isBlank() -    visibility = if (forceHide || isTextViewWithBlankText) View.GONE else View.VISIBLE +    val isImageViewWithoutImage = this is ImageView && this.drawable == null +    visibility = +        if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) { +            View.GONE +        } else { +            View.VISIBLE +        }  }  private fun View.asVerticalAnimator( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 1c789283ec70..dca0338dc8e7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -18,6 +18,8 @@ package com.android.systemui.biometrics.ui.viewmodel  import android.content.Context  import android.graphics.Rect +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable  import android.hardware.biometrics.BiometricPrompt  import android.hardware.biometrics.PromptContentView  import android.util.Log @@ -233,6 +235,19 @@ constructor(              }          } +    /** Logo for the prompt. */ +    val logo: Flow<Drawable?> = +        promptSelectorInteractor.prompt +            .map { +                when { +                    it == null -> null +                    it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme) +                    it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap) +                    else -> context.packageManager.getApplicationIcon(it.opPackageName) +                } +            } +            .distinctUntilChanged() +      /** Title for the prompt. */      val title: Flow<String> =          promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 0ee09390d03a..43f7c60721ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -16,6 +16,7 @@  package com.android.systemui.biometrics  import android.app.admin.DevicePolicyManager +import android.content.pm.PackageManager  import android.hardware.biometrics.BiometricAuthenticator  import android.hardware.biometrics.BiometricConstants  import android.hardware.biometrics.BiometricManager @@ -79,6 +80,8 @@ import org.mockito.Mockito.verify  import org.mockito.junit.MockitoJUnit  import org.mockito.Mockito.`when` as whenever +private const val OP_PACKAGE_NAME = "biometric.testapp" +  @RunWith(AndroidJUnit4::class)  @RunWithLooper(setAsMainLooper = true)  @SmallTest @@ -109,6 +112,8 @@ open class AuthContainerViewTest : SysuiTestCase() {      lateinit var authController: AuthController      @Mock      lateinit var selectedUserInteractor: SelectedUserInteractor +    @Mock +    private lateinit var packageManager: PackageManager      private val testScope = TestScope(StandardTestDispatcher())      private val fakeExecutor = FakeExecutor(FakeSystemClock()) @@ -134,6 +139,7 @@ open class AuthContainerViewTest : SysuiTestCase() {      private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor      private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor) +    private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)      private var authContainer: TestAuthContainerView? = null @@ -156,6 +162,9 @@ open class AuthContainerViewTest : SysuiTestCase() {                          selectedUserInteractor,                          testScope.backgroundScope,                  ) +        // Set up default logo icon +        whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon) +        context.setMockPackageManager(packageManager)      }      @After @@ -533,6 +542,7 @@ open class AuthContainerViewTest : SysuiTestCase() {              mPromptInfo = PromptInfo().apply {                  this.authenticators = authenticators              } +            mOpPackageName = OP_PACKAGE_NAME          },          testScope.backgroundScope,          fingerprintProps, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt index ec7ce634fd78..b39e09df9d2e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt @@ -43,6 +43,7 @@ import org.mockito.junit.MockitoJUnit  private const val USER_ID = 9  private const val CHALLENGE = 90L +private const val OP_PACKAGE_NAME = "biometric.testapp"  @OptIn(ExperimentalCoroutinesApi::class)  @SmallTest @@ -102,7 +103,8 @@ class PromptRepositoryImplTest : SysuiTestCase() {                      PromptInfo().apply { isConfirmationRequested = case },                      USER_ID,                      CHALLENGE, -                    PromptKind.Biometric() +                    PromptKind.Biometric(), +                    OP_PACKAGE_NAME                  )                  assertThat(isConfirmationRequired).isEqualTo(case) @@ -120,7 +122,8 @@ class PromptRepositoryImplTest : SysuiTestCase() {                      PromptInfo().apply { isConfirmationRequested = case },                      USER_ID,                      CHALLENGE, -                    PromptKind.Biometric() +                    PromptKind.Biometric(), +                    OP_PACKAGE_NAME                  )                  assertThat(isConfirmationRequired).isTrue() @@ -133,17 +136,19 @@ class PromptRepositoryImplTest : SysuiTestCase() {              val kind = PromptKind.Pin              val promptInfo = PromptInfo() -            repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind) +            repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind, OP_PACKAGE_NAME)              assertThat(repository.kind.value).isEqualTo(kind)              assertThat(repository.userId.value).isEqualTo(USER_ID)              assertThat(repository.challenge.value).isEqualTo(CHALLENGE)              assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo) +            assertThat(repository.opPackageName.value).isEqualTo(OP_PACKAGE_NAME)              repository.unsetPrompt()              assertThat(repository.promptInfo.value).isNull()              assertThat(repository.userId.value).isNull()              assertThat(repository.challenge.value).isNull() +            assertThat(repository.opPackageName.value).isNull()          }  } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt index dcefea28d4c8..8a46c0c6da9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt @@ -30,6 +30,7 @@ import org.mockito.junit.MockitoJUnit  private const val USER_ID = 22  private const val OPERATION_ID = 100L +private const val OP_PACKAGE_NAME = "biometric.testapp"  @OptIn(ExperimentalCoroutinesApi::class)  @SmallTest @@ -114,7 +115,8 @@ class PromptCredentialInteractorTest : SysuiTestCase() {                  },                  kind = kind,                  userId = USER_ID, -                challenge = OPERATION_ID +                challenge = OPERATION_ID, +                opPackageName = OP_PACKAGE_NAME              )              assertThat(prompt?.title).isEqualTo(title) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt index f15b738f3e95..52b42750847a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt @@ -51,6 +51,7 @@ private const val NEGATIVE_TEXT = "escape"  private const val USER_ID = 8  private const val CHALLENGE = 999L +private const val OP_PACKAGE_NAME = "biometric.testapp"  @OptIn(ExperimentalCoroutinesApi::class)  @SmallTest @@ -113,13 +114,20 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {          assertThat(currentPrompt).isNull() -        interactor.useBiometricsForAuthentication(info, USER_ID, CHALLENGE, modalities) +        interactor.useBiometricsForAuthentication( +            info, +            USER_ID, +            CHALLENGE, +            modalities, +            OP_PACKAGE_NAME +        )          assertThat(currentPrompt).isNotNull()          assertThat(currentPrompt?.title).isEqualTo(TITLE)          assertThat(currentPrompt?.description).isEqualTo(DESCRIPTION)          assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE)          assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT) +        assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME)          if (allowCredentialFallback) {              assertThat(credentialKind).isSameInstanceAs(PromptKind.Password) @@ -167,7 +175,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() {          assertThat(currentPrompt).isNull() -        interactor.useCredentialsForAuthentication(info, kind, USER_ID, CHALLENGE) +        interactor.useCredentialsForAuthentication(info, kind, USER_ID, CHALLENGE, OP_PACKAGE_NAME)          // not using biometrics, should be null with no fallback option          assertThat(currentPrompt).isNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt index a57b8905843c..a46167a423f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt @@ -1,5 +1,6 @@  package com.android.systemui.biometrics.domain.model +import android.graphics.Bitmap  import android.hardware.biometrics.PromptContentItemBulletedText  import android.hardware.biometrics.PromptVerticalListContentView  import androidx.test.filters.SmallTest @@ -8,6 +9,7 @@ import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal  import com.android.systemui.biometrics.promptInfo  import com.android.systemui.biometrics.shared.model.BiometricModalities  import com.android.systemui.biometrics.shared.model.BiometricUserInfo +import com.android.systemui.res.R  import com.google.common.truth.Truth.assertThat  import org.junit.Test  import org.junit.runner.RunWith @@ -15,6 +17,7 @@ import org.junit.runners.JUnit4  private const val USER_ID = 2  private const val OPERATION_ID = 8L +private const val OP_PACKAGE_NAME = "biometric.testapp"  @SmallTest  @RunWith(JUnit4::class) @@ -22,6 +25,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {      @Test      fun biometricRequestFromPromptInfo() { +        val logoRes = R.drawable.ic_cake          val title = "what"          val subtitle = "a"          val description = "request" @@ -36,6 +40,7 @@ class BiometricPromptRequestTest : SysuiTestCase() {          val request =              BiometricPromptRequest.Biometric(                  promptInfo( +                    logoRes = logoRes,                      title = title,                      subtitle = subtitle,                      description = description, @@ -44,8 +49,10 @@ class BiometricPromptRequestTest : SysuiTestCase() {                  BiometricUserInfo(USER_ID),                  BiometricOperationInfo(OPERATION_ID),                  BiometricModalities(fingerprintProperties = fpPros), +                OP_PACKAGE_NAME,              ) +        assertThat(request.logoRes).isEqualTo(logoRes)          assertThat(request.title).isEqualTo(title)          assertThat(request.subtitle).isEqualTo(subtitle)          assertThat(request.description).isEqualTo(description) @@ -57,6 +64,23 @@ class BiometricPromptRequestTest : SysuiTestCase() {      }      @Test +    fun biometricRequestLogoBitmapFromPromptInfo() { +        val logoBitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888) +        val fpPros = fingerprintSensorPropertiesInternal().first() +        val request = +            BiometricPromptRequest.Biometric( +                promptInfo( +                    logoBitmap = logoBitmap, +                ), +                BiometricUserInfo(USER_ID), +                BiometricOperationInfo(OPERATION_ID), +                BiometricModalities(fingerprintProperties = fpPros), +                OP_PACKAGE_NAME, +            ) +        assertThat(request.logoBitmap).isEqualTo(logoBitmap) +    } + +    @Test      fun credentialRequestFromPromptInfo() {          val title = "what"          val subtitle = "a" diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 394405440d7e..3888f2b940b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -16,8 +16,11 @@  package com.android.systemui.biometrics.ui.viewmodel +import android.content.pm.PackageManager  import android.content.res.Configuration +import android.graphics.Bitmap  import android.graphics.Point +import android.graphics.drawable.BitmapDrawable  import android.hardware.biometrics.PromptContentItemBulletedText  import android.hardware.biometrics.PromptContentView  import android.hardware.biometrics.PromptInfo @@ -76,6 +79,7 @@ import org.mockito.junit.MockitoJUnit  private const val USER_ID = 4  private const val CHALLENGE = 2L  private const val DELAY = 1000L +private const val OP_PACKAGE_NAME = "biometric.testapp"  @OptIn(ExperimentalCoroutinesApi::class)  @SmallTest @@ -88,9 +92,14 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa      @Mock private lateinit var authController: AuthController      @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor      @Mock private lateinit var udfpsUtils: UdfpsUtils +    @Mock private lateinit var packageManager: PackageManager      private val fakeExecutor = FakeExecutor(FakeSystemClock())      private val testScope = TestScope() +    private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android) +    private val logoResFromApp = R.drawable.ic_cake +    private val logoFromApp = context.getDrawable(logoResFromApp) +    private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)      private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository      private lateinit var promptRepository: FakePromptRepository @@ -153,6 +162,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa                  udfpsUtils              )          iconViewModel = viewModel.iconViewModel + +        // Set up default logo icon and app customized icon +        whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon) +        context.setMockPackageManager(packageManager) +        val resources = context.getOrCreateTestableResources() +        resources.addOverride(logoResFromApp, logoFromApp)      }      @Test @@ -1227,6 +1242,26 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa              assertThat(contentView).isNull()          } +    @Test +    fun defaultLogoIfNoLogoSet() = runGenericTest { +        val logo by collectLastValue(viewModel.logo) +        assertThat(logo).isEqualTo(defaultLogoIcon) +    } + +    @Test +    fun logoResSetByApp() = +        runGenericTest(logoRes = logoResFromApp) { +            val logo by collectLastValue(viewModel.logo) +            assertThat(logo).isEqualTo(logoFromApp) +        } + +    @Test +    fun logoBitmapSetByApp() = +        runGenericTest(logoBitmap = logoBitmapFromApp) { +            val logo by collectLastValue(viewModel.logo) +            assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp) +        } +      /** Asserts that the selected buttons are visible now. */      private suspend fun TestScope.assertButtonsVisible(          tryAgain: Boolean = false, @@ -1248,6 +1283,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa          allowCredentialFallback: Boolean = false,          description: String? = null,          contentView: PromptContentView? = null, +        logoRes: Int = -1, +        logoBitmap: Bitmap? = null,          block: suspend TestScope.() -> Unit      ) {          selector.initializePrompt( @@ -1257,6 +1294,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa              face = testCase.face,              descriptionFromApp = description,              contentViewFromApp = contentView, +            logoResFromApp = logoRes, +            logoBitmapFromApp = logoBitmap,          )          // put the view model in the initial authenticating state, unless explicitly skipped @@ -1434,9 +1473,13 @@ private fun PromptSelectorInteractor.initializePrompt(      allowCredentialFallback: Boolean = false,      descriptionFromApp: String? = null,      contentViewFromApp: PromptContentView? = null, +    logoResFromApp: Int = -1, +    logoBitmapFromApp: Bitmap? = null,  ) {      val info =          PromptInfo().apply { +            logoRes = logoResFromApp +            logoBitmap = logoBitmapFromApp              title = "t"              subtitle = "s"              description = descriptionFromApp @@ -1445,11 +1488,13 @@ private fun PromptSelectorInteractor.initializePrompt(              isDeviceCredentialAllowed = allowCredentialFallback              isConfirmationRequested = requireConfirmation          } +      useBiometricsForAuthentication(          info,          USER_ID,          CHALLENGE,          BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face), +        OP_PACKAGE_NAME,      )  } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt index 42ec8fed0127..f192de23fecc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt @@ -26,12 +26,24 @@ class FakePromptRepository : PromptRepository {      private val _isConfirmationRequired = MutableStateFlow(false)      override val isConfirmationRequired = _isConfirmationRequired.asStateFlow() +    private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null) +    override val opPackageName = _opPackageName.asStateFlow() +      override fun setPrompt(          promptInfo: PromptInfo,          userId: Int,          gatekeeperChallenge: Long?,          kind: PromptKind, -    ) = setPrompt(promptInfo, userId, gatekeeperChallenge, kind, forceConfirmation = false) +        opPackageName: String, +    ) = +        setPrompt( +            promptInfo, +            userId, +            gatekeeperChallenge, +            kind, +            forceConfirmation = false, +            opPackageName = opPackageName +        )      fun setPrompt(          promptInfo: PromptInfo, @@ -39,12 +51,14 @@ class FakePromptRepository : PromptRepository {          gatekeeperChallenge: Long?,          kind: PromptKind,          forceConfirmation: Boolean = false, +        opPackageName: String? = null,      ) {          _promptInfo.value = promptInfo          _userId.value = userId          _challenge.value = gatekeeperChallenge          _kind.value = kind          _isConfirmationRequired.value = promptInfo.isConfirmationRequested || forceConfirmation +        _opPackageName.value = opPackageName      }      override fun unsetPrompt() { diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index d5d8fd22314b..8fd2ee2bdc33 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -20,6 +20,7 @@ package com.android.server.biometrics;  // TODO(b/141025588): Create separate internal and external permissions for AuthService.  // TODO(b/141025588): Get rid of the USE_FINGERPRINT permission. +import static android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG;  import static android.Manifest.permission.TEST_BIOMETRIC;  import static android.Manifest.permission.USE_BIOMETRIC;  import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; @@ -304,6 +305,9 @@ public class AuthService extends SystemService {              if (promptInfo.containsPrivateApiConfigurations()) {                  checkInternalPermission();              } +            if (promptInfo.containsManageBioApiConfigurations()) { +                checkManageBiometricPermission(); +            }              final long identity = Binder.clearCallingIdentity();              try { @@ -984,6 +988,11 @@ public class AuthService extends SystemService {                  "Must have USE_BIOMETRIC_INTERNAL permission");      } +    private void checkManageBiometricPermission() { +        getContext().enforceCallingOrSelfPermission(MANAGE_BIOMETRIC_DIALOG, +                "Must have MANAGE_BIOMETRIC_DIALOG permission"); +    } +      private void checkPermission() {          if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)                  != PackageManager.PERMISSION_GRANTED) { |