summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Diya Bera <diyab@google.com> 2024-06-28 01:19:34 +0000
committer Diya Bera <diyab@google.com> 2024-06-28 01:21:46 +0000
commit4708292020397d8a0b427295b45f25d2bda433bd (patch)
tree3aa8e10b9489440591d5de83ba68747a1c387ebe
parenta514bd3b33789b2a82e2e40539b272c4d69210e2 (diff)
Add mandatory biometric prompt to platform surfaces (3/N)
1. Power off Flag: android.hardware.biometrics.Flags.MANDATORY_BIOMETRICS Bug: 339910718 Test: atest GlobalActionsDialogLiteTest Change-Id: Icd024c52b3e4ecc5a3800215e8e0181147b468ce
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--packages/SystemUI/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java45
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java12
9 files changed, 125 insertions, 1 deletions
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7a166dac046e..57668e892193 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -722,6 +722,9 @@
<!-- label for screenshot item in power menu [CHAR LIMIT=24]-->
<string name="global_action_screenshot">Screenshot</string>
+ <!-- description for mandatory biometrics prompt -->
+ <string name="identity_check_biometric_prompt_description">This is needed since Identity Check is on</string>
+
<!-- Take bug report menu title [CHAR LIMIT=30] -->
<string name="bugreport_title">Bug report</string>
<!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0f54d89e49c0..9f9ef86e3580 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1918,6 +1918,7 @@
<java-symbol type="string" name="global_action_voice_assist" />
<java-symbol type="string" name="global_action_assist" />
<java-symbol type="string" name="global_action_screenshot" />
+ <java-symbol type="string" name="identity_check_biometric_prompt_description" />
<java-symbol type="string" name="invalidPuk" />
<java-symbol type="string" name="lockscreen_carrier_default" />
<java-symbol type="style" name="Animation.LockScreen" />
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 66b47dae1b3e..c573cf4ad628 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -92,5 +92,6 @@
<permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" />
<permission name="android.permission.CONTROL_UI_TRACING" />
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
+ <permission name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
</privapp-permissions>
</permissions>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b37db16f5786..1b9a09d71b09 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -373,6 +373,8 @@
<!-- Listen to (dis-)connection of external displays and enable / disable them. -->
<uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
+ <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 1e4fb4f15062..c1de3818ef8d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -51,10 +51,14 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -857,6 +861,29 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
if (ActivityManager.isUserAMonkey()) {
return;
}
+ if (Flags.mandatoryBiometrics()
+ && requestBiometricAuthenticationForMandatoryBiometrics()) {
+ launchBiometricPromptForMandatoryBiometrics(
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errorCode,
+ CharSequence errString) {
+ super.onAuthenticationError(errorCode, errString);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ shutDown();
+ }
+ });
+ } else {
+ shutDown();
+ }
+ }
+
+ private void shutDown() {
mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown();
@@ -2261,6 +2288,35 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@VisibleForTesting
+ void launchBiometricPromptForMandatoryBiometrics(
+ BiometricPrompt.AuthenticationCallback authenticationCallback) {
+ final CancellationSignal cancellationSignal = new CancellationSignal();
+ final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext)
+ .setAllowedAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)
+ .setUseDefaultTitle()
+ .setDescription(mContext.getString(
+ R.string.identity_check_biometric_prompt_description))
+ .setNegativeButton(mContext.getString(R.string.cancel), mContext.getMainExecutor(),
+ (dialog, which) -> cancellationSignal.cancel())
+ .setAllowBackgroundAuthentication(true)
+ .build();
+ biometricPrompt.authenticate(cancellationSignal, mContext.getMainExecutor(),
+ authenticationCallback);
+ }
+
+ private boolean requestBiometricAuthenticationForMandatoryBiometrics() {
+ final BiometricManager biometricManager =
+ (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
+ if (biometricManager == null) {
+ Log.e(TAG, "Biometric Manager is null.");
+ return false;
+ }
+ final int status = biometricManager.canAuthenticate(
+ BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ return status == BiometricManager.BIOMETRIC_SUCCESS;
+ }
+
+ @VisibleForTesting
static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index e2cca3873bf7..b58eb49fc9cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -33,13 +34,20 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.media.AudioManager;
import android.os.Handler;
import android.os.UserManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.testing.TestableLooper;
@@ -88,6 +96,7 @@ import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -102,6 +111,9 @@ import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class GlobalActionsDialogLiteTest extends SysuiTestCase {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
private GlobalActionsDialogLite mGlobalActionsDialogLite;
@Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs;
@@ -141,6 +153,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private DialogTransitionAnimator mDialogTransitionAnimator;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
@Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Mock private BiometricManager mBiometricManager;
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
private TestableLooper mTestableLooper;
@@ -157,10 +170,13 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
when(mUserContextProvider.getUserContext()).thenReturn(mContext);
when(mResources.getConfiguration()).thenReturn(
getContext().getResources().getConfiguration());
+ when(mBiometricManager.canAuthenticate(anyInt())).thenReturn(
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
mInteractor = mKosmos.getGlobalActionsInteractor();
+ mContext.addMockSystemService(Context.BIOMETRIC_SERVICE, mBiometricManager);
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -551,6 +567,35 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void requestBiometricAuth_whenShutDownShortPressAndMandatoryBiometricsActive() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ ArgumentCaptor<BiometricPrompt.AuthenticationCallback>
+ authenticationCallbackArgumentCaptor = ArgumentCaptor.forClass(
+ BiometricPrompt.AuthenticationCallback.class);
+
+ when(mBiometricManager.canAuthenticate(
+ BiometricManager.Authenticators.MANDATORY_BIOMETRICS)).thenReturn(
+ BiometricManager.BIOMETRIC_SUCCESS);
+ doNothing().when(mGlobalActionsDialogLite).launchBiometricPromptForMandatoryBiometrics(
+ any());
+
+ GlobalActionsDialogLite.ShutDownAction shutDownAction =
+ mGlobalActionsDialogLite.new ShutDownAction();
+ shutDownAction.onPress();
+
+ verify(mGlobalActionsDialogLite).launchBiometricPromptForMandatoryBiometrics(
+ authenticationCallbackArgumentCaptor.capture());
+
+ BiometricPrompt.AuthenticationCallback authenticationCallback =
+ authenticationCallbackArgumentCaptor.getValue();
+ authenticationCallback.onAuthenticationSucceeded(null);
+
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
+ verify(mWindowManagerFuncs).shutdown();
+ }
+
+ @Test
public void testShouldLogLockdownPress() {
GlobalActionsDialogLite.LockDownAction lockDownAction =
mGlobalActionsDialogLite.new LockDownAction();
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 2a1687209aad..f5a2a218684d 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -369,6 +369,10 @@ public class AuthService extends SystemService {
checkPermission();
}
+ if ((authenticators & Authenticators.MANDATORY_BIOMETRICS) != 0) {
+ checkBiometricAdvancedPermission();
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
final int result = mBiometricService.canAuthenticate(
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index daaafcb61bc5..693a3e66f911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -418,7 +418,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions>
}
protected int getRequestReason() {
- if (isKeyguard()) {
+ if (isKeyguard() && !isBiometricPrompt()) {
return BiometricRequestConstants.REASON_AUTH_KEYGUARD;
} else if (isBiometricPrompt()) {
// BP reason always takes precedent over settings, since callers from within
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 9cd3186f99f3..36a5cda5285f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -393,6 +393,18 @@ public class AuthServiceTest {
testAuthenticate_throwsSecurityException(promptInfo);
}
+ @Test
+ public void testCanAuthenticate_throwsWhenUsingAdvancedApis() {
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ assertThrows(SecurityException.class, () -> {
+ mAuthService.mImpl.canAuthenticate(TEST_OP_PACKAGE_NAME, 1 /* userId */,
+ BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ waitForIdle();
+ });
+ }
+
private void testAuthenticate_throwsSecurityException(PromptInfo promptInfo) {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();