Merge "Fix default logo is wrong for some apps." into main
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index a0e40f6..61f1ee1 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -34,6 +34,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Bitmap;
@@ -603,7 +604,6 @@
             mPromptInfo.setIsForLegacyFingerprintManager(sensorId);
             return this;
         }
-        // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java)
 
         /**
          * Set if emergency call button should show, for example if biometrics are
@@ -613,12 +613,33 @@
          * @hide
          */
         @NonNull
+        @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
         public Builder setShowEmergencyCallButton(boolean showEmergencyCallButton) {
             mPromptInfo.setShowEmergencyCallButton(showEmergencyCallButton);
             return this;
         }
 
         /**
+         * Set caller's component name for getting logo icon/description. This should only be used
+         * by ConfirmDeviceCredentialActivity, see b/337082634 for more context.
+         *
+         * @param componentNameForConfirmDeviceCredentialActivity set the component name for
+         *                                                        ConfirmDeviceCredentialActivity.
+         * @return This builder.
+         * @hide
+         */
+        @NonNull
+        @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
+        public Builder setComponentNameForConfirmDeviceCredentialActivity(
+                ComponentName componentNameForConfirmDeviceCredentialActivity) {
+            mPromptInfo.setComponentNameForConfirmDeviceCredentialActivity(
+                    componentNameForConfirmDeviceCredentialActivity);
+            return this;
+        }
+
+        // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java)
+
+        /**
          * Creates a {@link BiometricPrompt}.
          *
          * @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 18b75c9..bb07b9b 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.graphics.Bitmap;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -56,6 +57,7 @@
     private boolean mIsForLegacyFingerprintManager = false;
     private boolean mShowEmergencyCallButton = false;
     private boolean mUseParentProfileForDeviceCredential = false;
+    private ComponentName mComponentNameForConfirmDeviceCredentialActivity = null;
 
     public PromptInfo() {
 
@@ -87,6 +89,8 @@
         mIsForLegacyFingerprintManager = in.readBoolean();
         mShowEmergencyCallButton = in.readBoolean();
         mUseParentProfileForDeviceCredential = in.readBoolean();
+        mComponentNameForConfirmDeviceCredentialActivity = in.readParcelable(
+                ComponentName.class.getClassLoader(), ComponentName.class);
     }
 
     public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -132,10 +136,11 @@
         dest.writeBoolean(mIsForLegacyFingerprintManager);
         dest.writeBoolean(mShowEmergencyCallButton);
         dest.writeBoolean(mUseParentProfileForDeviceCredential);
+        dest.writeParcelable(mComponentNameForConfirmDeviceCredentialActivity, 0);
     }
 
     // LINT.IfChange
-    public boolean containsTestConfigurations() {
+    public boolean requiresTestOrInternalPermission() {
         if (mIsForLegacyFingerprintManager
                 && mAllowedSensorIds.size() == 1
                 && !mAllowBackgroundAuthentication) {
@@ -148,11 +153,15 @@
             return true;
         } else if (mIgnoreEnrollmentState) {
             return true;
+        } else if (mShowEmergencyCallButton) {
+            return true;
+        } else if (mComponentNameForConfirmDeviceCredentialActivity != null) {
+            return true;
         }
         return false;
     }
 
-    public boolean containsPrivateApiConfigurations() {
+    public boolean requiresInternalPermission() {
         if (mDisallowBiometricsIfPolicyExists) {
             return true;
         } else if (mUseDefaultTitle) {
@@ -177,7 +186,7 @@
      * Currently, logo res, logo bitmap, logo description, PromptContentViewWithMoreOptions needs
      * this permission.
      */
-    public boolean containsAdvancedApiConfigurations() {
+    public boolean requiresAdvancedPermission() {
         if (mLogoRes != -1) {
             return true;
         } else if (mLogoBitmap != null) {
@@ -305,6 +314,12 @@
         mShowEmergencyCallButton = showEmergencyCallButton;
     }
 
+    public void setComponentNameForConfirmDeviceCredentialActivity(
+            ComponentName componentNameForConfirmDeviceCredentialActivity) {
+        mComponentNameForConfirmDeviceCredentialActivity =
+                componentNameForConfirmDeviceCredentialActivity;
+    }
+
     public void setUseParentProfileForDeviceCredential(
             boolean useParentProfileForDeviceCredential) {
         mUseParentProfileForDeviceCredential = useParentProfileForDeviceCredential;
@@ -417,6 +432,10 @@
         return mShowEmergencyCallButton;
     }
 
+    public ComponentName getComponentNameForConfirmDeviceCredentialActivity() {
+        return mComponentNameForConfirmDeviceCredentialActivity;
+    }
+
     private void checkOnlyOneLogoSet() {
         if (mLogoRes != -1 && mLogoBitmap != null) {
             throw new IllegalStateException(
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b5ec5b2..aecc906 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -384,6 +384,8 @@
 
     <!-- Content description for the app logo icon on biometric prompt. [CHAR LIMIT=NONE] -->
     <string name="biometric_dialog_logo">App logo</string>
+    <!-- List of packages for which we want to show overridden logo. For example, an app overrides its launcher logo, if it's in this array, biometric dialog shows the overridden logo; otherwise biometric dialog still shows the default application info icon. [CHAR LIMIT=NONE] -->
+    <string-array name="biometric_dialog_package_names_for_logo_with_overrides" />
     <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
     <string name="biometric_dialog_confirm">Confirm</string>
     <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b69e196..d6d40f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -1249,6 +1249,8 @@
         }
         mCurrentDialog = newDialog;
 
+        // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+        //  removed.
         if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) {
             cancelIfOwnerIsNotInForeground();
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 20e81c2..14d8caf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -16,8 +16,10 @@
 
 package com.android.systemui.biometrics.dagger
 
+import android.content.Context
 import android.content.res.Resources
 import com.android.internal.R
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.EllipseOverlapDetectorParams
@@ -111,6 +113,9 @@
         @Provides fun providesUdfpsUtils(): UdfpsUtils = UdfpsUtils()
 
         @Provides
+        fun provideIconProvider(context: Context): IconProvider = IconProvider(context)
+
+        @Provides
         @SysUISingleton
         fun providesOverlapDetector(): OverlapDetector {
             val selectedOption =
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 6f079e2..4f96c1e 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.content.ComponentName
 import android.graphics.Bitmap
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptInfo
@@ -43,6 +44,9 @@
         val logoBitmap: Bitmap? = info.logoBitmap
         val logoDescription: String? = info.logoDescription
         val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
+        val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
+            info.componentNameForConfirmDeviceCredentialActivity
+        val allowBackgroundAuthentication = info.isAllowBackgroundAuthentication
     }
 
     /** Prompt using a credential (pin, pattern, password). */
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 a8c5976..156ec6b 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
@@ -16,7 +16,10 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.app.ActivityTaskManager
+import android.content.ComponentName
 import android.content.Context
+import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.graphics.Rect
@@ -26,18 +29,22 @@
 import android.hardware.biometrics.BiometricPrompt
 import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.biometrics.PromptContentView
+import android.os.UserHandle
 import android.util.Log
 import android.util.RotationUtils
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.Flags.bpTalkback
 import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.Utils.isSystem
 import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
 import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -71,7 +78,9 @@
     @Application private val context: Context,
     private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
     private val biometricStatusInteractor: BiometricStatusInteractor,
-    private val udfpsUtils: UdfpsUtils
+    private val udfpsUtils: UdfpsUtils,
+    private val iconProvider: IconProvider,
+    private val activityTaskManager: ActivityTaskManager,
 ) {
     /** The set of modalities available for this prompt */
     val modalities: Flow<BiometricModalities> =
@@ -499,14 +508,7 @@
                     !(customBiometricPrompt() && constraintBp()) || it == null -> null
                     it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme)
                     it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
-                    else ->
-                        try {
-                            val info = context.getApplicationInfo(it.opPackageName)
-                            context.packageManager.getApplicationIcon(info)
-                        } catch (e: Exception) {
-                            Log.w(TAG, "Cannot find icon for package " + it.opPackageName, e)
-                            null
-                        }
+                    else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager)
                 }
             }
             .distinctUntilChanged()
@@ -517,15 +519,8 @@
             .map {
                 when {
                     !(customBiometricPrompt() && constraintBp()) || it == null -> ""
-                    it.logoDescription != null -> it.logoDescription
-                    else ->
-                        try {
-                            val info = context.getApplicationInfo(it.opPackageName)
-                            context.packageManager.getApplicationLabel(info).toString()
-                        } catch (e: Exception) {
-                            Log.w(TAG, "Cannot find name for package " + it.opPackageName, e)
-                            ""
-                        }
+                    !it.logoDescription.isNullOrEmpty() -> it.logoDescription
+                    else -> context.getUserBadgedLabel(it, activityTaskManager)
                 }
             }
             .distinctUntilChanged()
@@ -926,15 +921,109 @@
     }
 
     companion object {
-        private const val TAG = "PromptViewModel"
+        const val TAG = "PromptViewModel"
     }
 }
 
-private fun Context.getApplicationInfo(packageName: String): ApplicationInfo =
-    packageManager.getApplicationInfo(
-        packageName,
-        PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
-    )
+private fun Context.getUserBadgedIcon(
+    prompt: BiometricPromptRequest.Biometric,
+    iconProvider: IconProvider,
+    activityTaskManager: ActivityTaskManager
+): Drawable? {
+    var icon: Drawable? = null
+    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
+    if (componentName != null && shouldShowLogoWithOverrides(componentName)) {
+        val activityInfo = getActivityInfo(componentName)
+        icon = if (activityInfo == null) null else iconProvider.getIcon(activityInfo)
+    }
+    if (icon == null) {
+        val appInfo = prompt.getApplicationInfoForLogo(this, componentName)
+        if (appInfo == null) {
+            Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
+            return null
+        } else {
+            icon = packageManager.getApplicationIcon(appInfo)
+        }
+    }
+    return packageManager.getUserBadgedIcon(icon, UserHandle.of(prompt.userInfo.userId))
+}
+
+private fun Context.getUserBadgedLabel(
+    prompt: BiometricPromptRequest.Biometric,
+    activityTaskManager: ActivityTaskManager
+): String {
+    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
+    val appInfo = prompt.getApplicationInfoForLogo(this, componentName)
+    return if (appInfo == null || packageManager.getApplicationLabel(appInfo).isNullOrEmpty()) {
+        Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
+        ""
+    } else {
+        packageManager
+            .getUserBadgedLabel(packageManager.getApplicationLabel(appInfo), UserHandle.of(userId))
+            .toString()
+    }
+}
+
+private fun BiometricPromptRequest.Biometric.getComponentNameForLogo(
+    activityTaskManager: ActivityTaskManager
+): ComponentName? {
+    val topActivity: ComponentName? = activityTaskManager.getTasks(1).firstOrNull()?.topActivity
+    return when {
+        componentNameForConfirmDeviceCredentialActivity != null ->
+            componentNameForConfirmDeviceCredentialActivity
+        topActivity?.packageName.contentEquals(opPackageName) -> topActivity
+        else -> {
+            Log.w(PromptViewModel.TAG, "Top activity $topActivity is not the client $opPackageName")
+            null
+        }
+    }
+}
+
+private fun BiometricPromptRequest.Biometric.getApplicationInfoForLogo(
+    context: Context,
+    componentNameForLogo: ComponentName?
+): ApplicationInfo? {
+    val packageName =
+        when {
+            componentNameForLogo != null -> componentNameForLogo.packageName
+            // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+            // removed.
+            // This is being consistent with the check in [AuthController.showDialog()].
+            allowBackgroundAuthentication || isSystem(context, opPackageName) -> opPackageName
+            else -> null
+        }
+    return if (packageName == null) {
+        Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName")
+        null
+    } else {
+        context.getApplicationInfo(packageName)
+    }
+}
+
+private fun Context.shouldShowLogoWithOverrides(componentName: ComponentName): Boolean {
+    return resources
+        .getStringArray(R.array.biometric_dialog_package_names_for_logo_with_overrides)
+        .find { componentName.packageName.contentEquals(it) } != null
+}
+
+private fun Context.getActivityInfo(componentName: ComponentName): ActivityInfo? =
+    try {
+        packageManager.getActivityInfo(componentName, 0)
+    } catch (e: PackageManager.NameNotFoundException) {
+        Log.w(PromptViewModel.TAG, "Cannot find activity info for $opPackageName", e)
+        null
+    }
+
+private fun Context.getApplicationInfo(packageName: String): ApplicationInfo? =
+    try {
+        packageManager.getApplicationInfo(
+            packageName,
+            PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+        )
+    } catch (e: PackageManager.NameNotFoundException) {
+        Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName", e)
+        null
+    }
 
 /** How the fingerprint sensor was started for the prompt. */
 enum class FingerprintStartMode {
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 7597e62..e81369d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -43,6 +43,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.widget.LockPatternUtils
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
@@ -150,6 +151,7 @@
     private lateinit var displayStateInteractor: DisplayStateInteractor
     private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
     private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+    private lateinit var iconProvider: IconProvider
 
     private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
@@ -178,6 +180,7 @@
         biometricStatusInteractor =
                 BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository,
                     fingerprintRepository)
+        iconProvider = IconProvider(context)
         // Set up default logo icon
         whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
         context.setMockPackageManager(packageManager)
@@ -655,7 +658,9 @@
             context,
             udfpsOverlayInteractor,
             biometricStatusInteractor,
-            udfpsUtils
+            udfpsUtils,
+            iconProvider,
+            activityTaskManager
         ),
         { credentialViewModel },
         Handler(TestableLooper.get(this).looper),
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 e0324df..4068404 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
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics.domain.interactor
 
 import android.app.admin.DevicePolicyManager
+import android.content.ComponentName
 import android.hardware.biometrics.BiometricManager.Authenticators
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptInfo
@@ -47,19 +48,22 @@
 import org.mockito.Mock
 import org.mockito.junit.MockitoJUnit
 
-private const val TITLE = "hey there"
-private const val SUBTITLE = "ok"
-private const val DESCRIPTION = "football"
-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
 @RunWith(JUnit4::class)
 class PromptSelectorInteractorImplTest : SysuiTestCase() {
+    companion object {
+        private const val TITLE = "hey there"
+        private const val SUBTITLE = "ok"
+        private const val DESCRIPTION = "football"
+        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"
+        private val componentNameOverriddenForConfirmDeviceCredentialActivity =
+            ComponentName("not.com.android.settings", "testapp")
+    }
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
 
@@ -103,7 +107,19 @@
     fun useBiometricsAndResetWithoutFallback() =
         testScope.runTest { useBiometricsAndReset(allowCredentialFallback = false) }
 
-    private fun TestScope.useBiometricsAndReset(allowCredentialFallback: Boolean) {
+    @Test
+    fun useBiometricsAndResetOnConfirmDeviceCredentialActivity() =
+        testScope.runTest {
+            useBiometricsAndReset(
+                allowCredentialFallback = true,
+                setComponentNameForConfirmDeviceCredentialActivity = true
+            )
+        }
+
+    private fun TestScope.useBiometricsAndReset(
+        allowCredentialFallback: Boolean,
+        setComponentNameForConfirmDeviceCredentialActivity: Boolean = false
+    ) {
         setUserCredentialType(isPassword = true)
 
         val confirmationRequired = true
@@ -117,6 +133,10 @@
                         Authenticators.BIOMETRIC_STRONG
                     }
                 isDeviceCredentialAllowed = allowCredentialFallback
+                componentNameForConfirmDeviceCredentialActivity =
+                    if (setComponentNameForConfirmDeviceCredentialActivity)
+                        componentNameOverriddenForConfirmDeviceCredentialActivity
+                    else null
             }
 
         val currentPrompt by collectLastValue(interactor.prompt)
@@ -143,6 +163,12 @@
         assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT)
         assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME)
         assertThat(promptKind!!.isBiometric()).isTrue()
+        assertThat(currentPrompt?.componentNameForConfirmDeviceCredentialActivity)
+            .isEqualTo(
+                if (setComponentNameForConfirmDeviceCredentialActivity)
+                    componentNameOverriddenForConfirmDeviceCredentialActivity
+                else null
+            )
 
         if (allowCredentialFallback) {
             assertThat(credentialKind).isSameInstanceAs(PromptKind.Password)
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 aae7ff6..97c3c42 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,9 +16,13 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.app.ActivityManager.RunningTaskInfo
 import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
 import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Point
@@ -37,6 +41,7 @@
 import android.view.MotionEvent
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.Flags.FLAG_BP_TALKBACK
 import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
@@ -94,6 +99,7 @@
 private const val DELAY = 1000L
 private const val OP_PACKAGE_NAME = "biometric.testapp"
 private const val OP_PACKAGE_NAME_NO_ICON = "biometric.testapp.noicon"
+private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -107,18 +113,23 @@
     @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var udfpsUtils: UdfpsUtils
     @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var iconProvider: IconProvider
     @Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
     @Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
+    @Mock private lateinit var activityInfo: ActivityInfo
+    @Mock private lateinit var runningTaskInfo: RunningTaskInfo
 
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val testScope = TestScope()
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
+    private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
     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 val defaultLogoDescription = "Test Android App"
     private val logoDescriptionFromApp = "Test Cake App"
+    private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
 
     private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
     private lateinit var promptRepository: FakePromptRepository
@@ -174,9 +185,7 @@
                 biometricStatusRepository,
                 fingerprintRepository
             )
-        selector =
-            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
-        selector.resetPrompt()
+
         promptContentView =
             PromptVerticalListContentView.Builder()
                 .addListItem(PromptContentItemBulletedText("content item 1"))
@@ -189,29 +198,32 @@
                 .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
                 .build()
 
-        viewModel =
-            PromptViewModel(
-                displayStateInteractor,
-                selector,
-                mContext,
-                udfpsOverlayInteractor,
-                biometricStatusInteractor,
-                udfpsUtils
-            )
-        iconViewModel = viewModel.iconViewModel
-
-        // Set up default logo icon and app customized icon
+        // Set up default logo info and app customized info
         whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
             .thenReturn(applicationInfoNoIcon)
         whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
             .thenReturn(applicationInfoWithIcon)
+        whenever(packageManager.getApplicationInfo(eq(packageNameForLogoWithOverrides), anyInt()))
+            .thenReturn(applicationInfoWithIcon)
+        whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), anyInt()))
+            .thenThrow(NameNotFoundException())
+
+        whenever(packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
+        whenever(iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
         whenever(packageManager.getApplicationIcon(applicationInfoWithIcon))
             .thenReturn(defaultLogoIcon)
         whenever(packageManager.getApplicationLabel(applicationInfoWithIcon))
             .thenReturn(defaultLogoDescription)
+        whenever(packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
+        whenever(packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
+
         context.setMockPackageManager(packageManager)
         val resources = context.getOrCreateTestableResources()
         resources.addOverride(logoResFromApp, logoFromApp)
+        resources.addOverride(
+            R.array.biometric_dialog_package_names_for_logo_with_overrides,
+            arrayOf(packageNameForLogoWithOverrides)
+        )
     }
 
     @Test
@@ -1322,7 +1334,30 @@
         }
 
     @Test
-    fun logoIsNullIfPackageNameNotFound() =
+    fun logo_nullIfPkgNameNotFound() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
+            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+            val logo by collectLastValue(viewModel.logo)
+            assertThat(logo).isNull()
+        }
+
+    @Test
+    fun logo_defaultWithOverrides() =
+        runGenericTest(packageName = packageNameForLogoWithOverrides) {
+            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+            val logo by collectLastValue(viewModel.logo)
+
+            // 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
+            // applicationInfoWithIcon with defaultLogoIcon,
+            // 2. iconProvider.getIcon() is set to return defaultLogoIconForGMSCore
+            // For the apps with packageNameForLogoWithOverrides, 2 should be called instead of 1
+            assertThat(logo).isEqualTo(defaultLogoIconWithOverrides)
+        }
+
+    @Test
+    fun logo_defaultIsNull() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
             mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
             mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
@@ -1331,7 +1366,7 @@
         }
 
     @Test
-    fun defaultLogoIfNoLogoSet() = runGenericTest {
+    fun logo_default() = runGenericTest {
         mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
         val logo by collectLastValue(viewModel.logo)
@@ -1339,7 +1374,7 @@
     }
 
     @Test
-    fun logoResSetByApp() =
+    fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
             mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
             mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
@@ -1348,7 +1383,7 @@
         }
 
     @Test
-    fun logoBitmapSetByApp() =
+    fun logo_bitmapSetByApp() =
         runGenericTest(logoBitmap = logoBitmapFromApp) {
             mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
             mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
@@ -1357,7 +1392,16 @@
         }
 
     @Test
-    fun logoDescriptionIsEmptyIfPackageNameNotFound() =
+    fun logoDescription_emptyIfPkgNameNotFound() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
+            mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+            mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
+            val logoDescription by collectLastValue(viewModel.logoDescription)
+            assertThat(logoDescription).isEqualTo("")
+        }
+
+    @Test
+    fun logoDescription_defaultIsEmpty() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
             mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
             mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
@@ -1366,7 +1410,7 @@
         }
 
     @Test
-    fun defaultLogoDescriptionIfNoLogoDescriptionSet() = runGenericTest {
+    fun logoDescription_default() = runGenericTest {
         mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
         val logoDescription by collectLastValue(viewModel.logoDescription)
@@ -1374,7 +1418,7 @@
     }
 
     @Test
-    fun logoDescriptionSetByApp() =
+    fun logoDescription_setByApp() =
         runGenericTest(logoDescription = logoDescriptionFromApp) {
             mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
             mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
@@ -1420,6 +1464,26 @@
         packageName: String = OP_PACKAGE_NAME,
         block: suspend TestScope.() -> Unit,
     ) {
+        val topActivity = ComponentName(packageName, "test app")
+        runningTaskInfo.topActivity = topActivity
+        whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
+        selector =
+            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
+        selector.resetPrompt()
+
+        viewModel =
+            PromptViewModel(
+                displayStateInteractor,
+                selector,
+                mContext,
+                udfpsOverlayInteractor,
+                biometricStatusInteractor,
+                udfpsUtils,
+                iconProvider,
+                activityTaskManager
+            )
+        iconViewModel = viewModel.iconViewModel
+
         selector.initializePrompt(
             requireConfirmation = testCase.confirmationRequested,
             allowCredentialFallback = allowCredentialFallback,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 2ae6f542..4999a5a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.biometrics.ui.viewmodel
 
 import android.content.applicationContext
+import com.android.app.activityTaskManager
+import com.android.launcher3.icons.IconProvider
 import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
 import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
@@ -32,6 +34,8 @@
         context = applicationContext,
         udfpsOverlayInteractor = udfpsOverlayInteractor,
         biometricStatusInteractor = biometricStatusInteractor,
-        udfpsUtils = udfpsUtils
+        udfpsUtils = udfpsUtils,
+        iconProvider = IconProvider(applicationContext),
+        activityTaskManager = activityTaskManager,
     )
 }
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 11cca66..2a16872 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -298,7 +298,7 @@
                 return -1;
             }
 
-            if (promptInfo.containsTestConfigurations()) {
+            if (promptInfo.requiresTestOrInternalPermission()) {
                 if (getContext().checkCallingOrSelfPermission(TEST_BIOMETRIC)
                         != PackageManager.PERMISSION_GRANTED) {
                     checkInternalPermission();
@@ -306,10 +306,10 @@
             }
 
             // Only allow internal clients to enable non-public options.
-            if (promptInfo.containsPrivateApiConfigurations()) {
+            if (promptInfo.requiresInternalPermission()) {
                 checkInternalPermission();
             }
-            if (promptInfo.containsAdvancedApiConfigurations()) {
+            if (promptInfo.requiresAdvancedPermission()) {
                 checkBiometricAdvancedPermission();
             }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 3dc375c..9cd3186 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -368,24 +368,32 @@
     }
 
     @Test
-    public void testAuthenticate_throwsWhenUsingTestConfigurations() {
+    public void testAuthenticate_throwsWhenUsingTestApis() {
         final PromptInfo promptInfo = mock(PromptInfo.class);
-        when(promptInfo.containsPrivateApiConfigurations()).thenReturn(false);
-        when(promptInfo.containsTestConfigurations()).thenReturn(true);
+        when(promptInfo.requiresInternalPermission()).thenReturn(false);
+        when(promptInfo.requiresTestOrInternalPermission()).thenReturn(true);
 
-        testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+        testAuthenticate_throwsSecurityException(promptInfo);
     }
 
     @Test
     public void testAuthenticate_throwsWhenUsingPrivateApis() {
         final PromptInfo promptInfo = mock(PromptInfo.class);
-        when(promptInfo.containsPrivateApiConfigurations()).thenReturn(true);
-        when(promptInfo.containsTestConfigurations()).thenReturn(false);
+        when(promptInfo.requiresInternalPermission()).thenReturn(true);
+        when(promptInfo.requiresTestOrInternalPermission()).thenReturn(false);
 
-        testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+        testAuthenticate_throwsSecurityException(promptInfo);
     }
 
-    private void testAuthenticate_throwsWhenUsingTestConfigurations(PromptInfo promptInfo) {
+    @Test
+    public void testAuthenticate_throwsWhenUsingAdvancedApis() {
+        final PromptInfo promptInfo = mock(PromptInfo.class);
+        when(promptInfo.requiresAdvancedPermission()).thenReturn(true);
+
+        testAuthenticate_throwsSecurityException(promptInfo);
+    }
+
+    private void testAuthenticate_throwsSecurityException(PromptInfo promptInfo) {
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();