summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hao Dong <spdonghao@google.com> 2024-01-18 00:46:26 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-01-18 00:46:26 +0000
commitbe9914891cb15b3325222491e60b16257c4f3fda (patch)
tree7cb06c1258e835337e80939038839222259586a7
parent007cee95f15b53b8f8b328efaa30008e283be177 (diff)
parente591439669f0dbd9e2bb14078f44a4acf6d3a95d (diff)
Merge "Add setLogo() API on Biometric Prompt." into main
-rw-r--r--core/api/current.txt4
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java70
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java44
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt5
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_layout.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt45
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt16
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java9
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) {