diff options
| author | 2024-03-20 18:37:18 +0000 | |
|---|---|---|
| committer | 2024-03-22 21:32:03 +0000 | |
| commit | cf7d9a50e4a49a94af8e22f6836589d7bec9299a (patch) | |
| tree | 27a59b723651c9417058e9a3802e747943122936 | |
| parent | fa4bde6d2134572356ff998945cea6a813369aef (diff) | |
Add char limits and permission enforcement for custom bp.
Flag: ACONFIG android.hardware.biometrics.custom_biometric_prompt DEVELOPMENT
Bug: 302735104
Test: atest BiometricPromptLogoTests
Test: atest BiometricPromptContentViewTest
Change-Id: Id159cde0df2520b4474c216f8d6503558262fc67
6 files changed, 67 insertions, 46 deletions
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 7d61c142fa04..d9614d1acd6b 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -70,6 +70,7 @@ import javax.crypto.Mac; public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants { private static final String TAG = "BiometricPrompt"; + private static final int MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER = 30; /** * Error/help message will show for this amount of time. @@ -222,11 +223,19 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * * @param logoDescription The logo description text that will be shown on the prompt. * @return This builder. + * @throws IllegalStateException If logo description is null or exceeds certain character + * limit. */ @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @NonNull public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) { + if (logoDescription == null + || logoDescription.length() > MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER) { + throw new IllegalStateException( + "Logo description passed in can not be null or exceed " + + MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER + " character number."); + } mPromptInfo.setLogoDescription(logoDescription); return this; } @@ -814,7 +823,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * Gets the logo description for the prompt, as set by - * {@link Builder#setDescription(CharSequence)}. + * {@link Builder#setLogoDescription(String)}. * Currently for system applications use only. * * @return The logo description of the prompt, or null if the prompt has no logo description diff --git a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java index 9ebfa8f4301d..853d86cf94dc 100644 --- a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java +++ b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java @@ -40,9 +40,12 @@ import java.util.concurrent.Executor; * or if the user has already selected the appropriate account to use before invoking * BiometricPrompt because it will create additional steps that the user must navigate through. * Clicking the more options button will dismiss the prompt, provide the app an opportunity to ask - * the user for the correct account, &finally allow the app to decide how to proceed once selected. + * the user for the correct account, and finally allow the app to decide how to proceed once + * selected. + * * <p> - * Here's how you'd set a <code>PromptContentViewWithMoreOptionsButton</code> on a Biometric Prompt: + * Here's how you'd set a <code>PromptContentViewWithMoreOptionsButton</code> on a Biometric + * Prompt: * <pre class="prettyprint"> * BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(...) * .setTitle(...) @@ -56,6 +59,7 @@ import java.util.concurrent.Executor; */ @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable { + private static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225; private final String mDescription; private DialogInterface.OnClickListener mListener; @@ -139,10 +143,15 @@ public final class PromptContentViewWithMoreOptionsButton implements PromptConte * * @param description The description to display. * @return This builder. + * @throws IllegalArgumentException If description exceeds certain character limit. */ @NonNull @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) public Builder setDescription(@NonNull String description) { + if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) { + throw new IllegalStateException("The character number of description exceeds " + + MAX_DESCRIPTION_CHARACTER_NUMBER); + } mDescription = description; return this; } @@ -150,14 +159,6 @@ public final class PromptContentViewWithMoreOptionsButton implements PromptConte /** * Required: Sets the executor and click listener for the more options button on the * prompt content. - * This button should be used to provide more options for sign in or other purposes, such - * as when a user needs to select between multiple app-specific accounts or profiles that - * are available for sign in. This is not common and apps should avoid using it if there - * is only one choice available or if the user has already selected the appropriate - * account to use before invoking BiometricPrompt because it will create additional steps - * that the user must navigate through. Clicking the more options button will dismiss the - * prompt, provide the app an opportunity to ask the user for the correct account, &finally - * allow the app to decide how to proceed once selected. * * @param executor Executor that will be used to run the on click callback. * @param listener Listener containing a callback to be run when the button is pressed. @@ -167,12 +168,6 @@ public final class PromptContentViewWithMoreOptionsButton implements PromptConte @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) public Builder setMoreOptionsButtonListener(@NonNull @CallbackExecutor Executor executor, @NonNull DialogInterface.OnClickListener listener) { - if (executor == null) { - throw new IllegalArgumentException("Executor must not be null"); - } - if (listener == null) { - throw new IllegalArgumentException("Listener must not be null"); - } mExecutor = executor; mListener = listener; return this; @@ -183,15 +178,23 @@ public final class PromptContentViewWithMoreOptionsButton implements PromptConte * Creates a {@link PromptContentViewWithMoreOptionsButton}. * * @return An instance of {@link PromptContentViewWithMoreOptionsButton}. + * @throws IllegalArgumentException If the executor of more options button is null, or the + * listener of more options button is null. */ @NonNull @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) public PromptContentViewWithMoreOptionsButton build() { - if (mListener == null) { + if (mExecutor == null) { throw new IllegalArgumentException( - "The listener of more options button on prompt content must be set if " + "The executor for the listener of more options button on prompt content " + + "must be set and non-null if " + "PromptContentViewWithMoreOptionsButton is used."); } + if (mListener == null) { + throw new IllegalArgumentException( + "The listener of more options button on prompt content must be set and " + + "non-null if PromptContentViewWithMoreOptionsButton is used."); + } return new PromptContentViewWithMoreOptionsButton(mDescription, mExecutor, mListener); } } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index 814321319e2f..18b75c9b8f8f 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -173,14 +173,19 @@ public class PromptInfo implements Parcelable { /** * Returns whether SET_BIOMETRIC_DIALOG_ADVANCED is contained. + * + * Currently, logo res, logo bitmap, logo description, PromptContentViewWithMoreOptions needs + * this permission. */ - public boolean containsSetLogoApiConfigurations() { + public boolean containsAdvancedApiConfigurations() { if (mLogoRes != -1) { return true; } else if (mLogoBitmap != null) { return true; } else if (mLogoDescription != null) { return true; + } else if (mContentView != null && isContentViewMoreOptionsButtonUsed()) { + return true; } return false; } diff --git a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java index 38d32dc73ccb..02b2a50ade3c 100644 --- a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java +++ b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java @@ -29,9 +29,7 @@ import java.util.List; /** - * Contains the information of the template of vertical list content view for Biometric Prompt. Note - * that there are limits on the item count and the number of characters allowed for each item's - * text. + * Contains the information of the template of vertical list content view for Biometric Prompt. * <p> * Here's how you'd set a <code>PromptVerticalListContentView</code> on a Biometric Prompt: * <pre class="prettyprint"> @@ -51,6 +49,8 @@ import java.util.List; public final class PromptVerticalListContentView implements PromptContentViewParcelable { private static final int MAX_ITEM_NUMBER = 20; private static final int MAX_EACH_ITEM_CHARACTER_NUMBER = 640; + private static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225; + private final List<PromptContentItemParcelable> mContentList; private final String mDescription; @@ -150,51 +150,59 @@ public final class PromptVerticalListContentView implements PromptContentViewPar * * @param description The description to display. * @return This builder. + * @throws IllegalArgumentException If description exceeds certain character limit. */ @NonNull public Builder setDescription(@NonNull String description) { + if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) { + throw new IllegalStateException("The character number of description exceeds " + + MAX_DESCRIPTION_CHARACTER_NUMBER); + } mDescription = description; return this; } /** - * Optional: Adds a list item in the current row. Maximum {@value MAX_ITEM_NUMBER} items in - * total. The maximum length for each item is {@value MAX_EACH_ITEM_CHARACTER_NUMBER} - * characters. + * Optional: Adds a list item in the current row. * * @param listItem The list item view to display * @return This builder. + * @throws IllegalArgumentException If this list item exceeds certain character limits or + * the number of list items exceeds certain limit. */ @NonNull public Builder addListItem(@NonNull PromptContentItem listItem) { - if (doesListItemExceedsCharLimit(listItem)) { - throw new IllegalStateException( - "The character number of list item exceeds " - + MAX_EACH_ITEM_CHARACTER_NUMBER); - } mContentList.add((PromptContentItemParcelable) listItem); + checkItemLimits(listItem); return this; } - /** - * Optional: Adds a list item in the current row. Maximum {@value MAX_ITEM_NUMBER} items in - * total. The maximum length for each item is {@value MAX_EACH_ITEM_CHARACTER_NUMBER} - * characters. + * Optional: Adds a list item in the current row. * * @param listItem The list item view to display - * @param index The position at which to add the item + * @param index The position at which to add the item * @return This builder. + * @throws IllegalArgumentException If this list item exceeds certain character limits or + * the number of list items exceeds certain limit. */ @NonNull public Builder addListItem(@NonNull PromptContentItem listItem, int index) { + mContentList.add(index, (PromptContentItemParcelable) listItem); + checkItemLimits(listItem); + return this; + } + + private void checkItemLimits(@NonNull PromptContentItem listItem) { if (doesListItemExceedsCharLimit(listItem)) { throw new IllegalStateException( "The character number of list item exceeds " + MAX_EACH_ITEM_CHARACTER_NUMBER); } - mContentList.add(index, (PromptContentItemParcelable) listItem); - return this; + if (mContentList.size() > MAX_ITEM_NUMBER) { + throw new IllegalStateException( + "The number of list items exceeds " + MAX_ITEM_NUMBER); + } } private boolean doesListItemExceedsCharLimit(PromptContentItem listItem) { @@ -217,10 +225,6 @@ public final class PromptVerticalListContentView implements PromptContentViewPar */ @NonNull public PromptVerticalListContentView build() { - if (mContentList.size() > MAX_ITEM_NUMBER) { - throw new IllegalStateException( - "The number of list items exceeds " + MAX_ITEM_NUMBER); - } return new PromptVerticalListContentView(mContentList, mDescription); } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 31f2d8b13794..6431db9d174e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6778,7 +6778,7 @@ android:protectionLevel="signature" /> <!-- Allows an application to set the advanced features on BiometricDialog (SystemUI), including - logo, logo description. + logo, logo description, and content view with more options button. <p>Not for use by third-party applications. @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") --> diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index b7e11a7fe261..14f3120b60b6 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -312,8 +312,8 @@ public class AuthService extends SystemService { if (promptInfo.containsPrivateApiConfigurations()) { checkInternalPermission(); } - if (promptInfo.containsSetLogoApiConfigurations()) { - checkManageBiometricPermission(); + if (promptInfo.containsAdvancedApiConfigurations()) { + checkBiometricAdvancedPermission(); } final long identity = Binder.clearCallingIdentity(); @@ -1029,7 +1029,7 @@ public class AuthService extends SystemService { "Must have USE_BIOMETRIC_INTERNAL permission"); } - private void checkManageBiometricPermission() { + private void checkBiometricAdvancedPermission() { getContext().enforceCallingOrSelfPermission(SET_BIOMETRIC_DIALOG_ADVANCED, "Must have SET_BIOMETRIC_DIALOG_ADVANCED permission"); } |