Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +40 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; Loading Loading @@ -995,8 +996,8 @@ public class AuthControllerTest extends SysuiTestCase { } @Test public void testShowDialog_whenOwnerNotInForeground() { PromptInfo promptInfo = createTestPromptInfo(); public void testShowDialog_whenOwnerNotInForegroundAndNotVisible() { final PromptInfo promptInfo = createTestPromptInfo(); promptInfo.setAllowBackgroundAuthentication(false); switchTask("other_package"); mAuthController.showAuthenticationDialog(promptInfo, Loading @@ -1013,6 +1014,27 @@ public class AuthControllerTest extends SysuiTestCase { verify(mDialog1, never()).show(any()); } @Test public void testShowDialog_whenOwnerNotInForegroundAndVisible() { final PromptInfo promptInfo = createTestPromptInfo(); final AuthController.Callback callback = mock(AuthController.Callback.class); promptInfo.setAllowBackgroundAuthentication(false); switchTaskWithVisibility("other_package", true /* isVisible */); mAuthController.addCallback(callback); mAuthController.showAuthenticationDialog(promptInfo, mReceiver /* receiver */, new int[]{1} /* sensorIds */, false /* credentialAllowed */, true /* requireConfirmation */, 0 /* userId */, 0 /* operationId */, "testPackage", REQUEST_ID); assertNotNull(mAuthController.mCurrentDialog); verify(mDialog1).show(mWindowManager); } @Test public void testShowDialog_visibleBackgroundUser() { int backgroundUserId = 1001; Loading Loading @@ -1109,16 +1131,31 @@ public class AuthControllerTest extends SysuiTestCase { REQUEST_ID); } private void switchTask(String packageName) { private void switchTaskWithVisibility(String packageName, boolean isVisible) { final List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>(); final ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); taskInfo.topActivity = mock(ComponentName.class); when(taskInfo.topActivity.getPackageName()).thenReturn(packageName); when(taskInfo.topActivity.getClassName()).thenReturn(AuthControllerTest.class.getName()); tasks.add(taskInfo); final ActivityManager.RunningTaskInfo callingTaskInfo = mock(ActivityManager.RunningTaskInfo.class); callingTaskInfo.topActivity = mock(ComponentName.class); when(callingTaskInfo.topActivity.getPackageName()).thenReturn("Dialog1"); when(callingTaskInfo.topActivity.getClassName()).thenReturn( AuthControllerTest.class.getName()); callingTaskInfo.isVisible = isVisible; tasks.add(callingTaskInfo); when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); } private void switchTask(String packageName) { switchTaskWithVisibility(packageName, false /* isVisible */); } private PromptInfo createTestPromptInfo() { PromptInfo promptInfo = new PromptInfo(); Loading packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt +71 −6 Original line number Diff line number Diff line Loading @@ -162,8 +162,10 @@ object Utils { fun ActivityTaskManager.isSystemAppOrInBackground( context: Context, clientPackage: String, clientClassNameIfItIsConfirmDeviceCredentialActivity: String? clientClassNameIfItIsConfirmDeviceCredentialActivity: String?, ): Boolean { // TODO (b/409812027): Consolidate and scope out auth requirements for biometric prompt Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage") val tasks = getTasks(Int.MAX_VALUE) if (tasks == null || tasks.isEmpty()) { Loading @@ -176,11 +178,74 @@ object Utils { val topPackageEqualsToClient = topActivity!!.packageName == clientPackage val isClientConfirmDeviceCredentialActivity = clientClassNameIfItIsConfirmDeviceCredentialActivity != null if ( !isClientInBackgroundOrNotVisible( isSystemApp, isVisible = true, topPackageEqualsToClient, isClientConfirmDeviceCredentialActivity, clientClassNameIfItIsConfirmDeviceCredentialActivity, topActivity!!.className, isTopPackage = true, ) ) { return false } for (task in tasks) { val packageName = task.topActivity!!.packageName val className = task.topActivity!!.className val isVisible = task.isVisible Log.v(TAG, "Running task, top: $packageName, isVisible: $isVisible") if ( !isClientInBackgroundOrNotVisible( isSystemApp = false, isVisible, taskPackageEqualsClientPackage = packageName == clientPackage, isClientConfirmDeviceCredentialActivity, clientClassNameIfItIsConfirmDeviceCredentialActivity, className, ) ) { return false } } return true } fun isClientInBackgroundOrNotVisible( isSystemApp: Boolean, isVisible: Boolean, taskPackageEqualsClientPackage: Boolean, isClientConfirmDeviceCredentialActivity: Boolean, clientClassNameIfItIsConfirmDeviceCredentialActivity: String?, topActivityClassName: String, isTopPackage: Boolean = false, ): Boolean { if (isVisible) { if (isSystemApp || taskPackageEqualsClientPackage) { // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on // class name. return !(isSystemApp || topPackageEqualsToClient) || (isClientConfirmDeviceCredentialActivity && topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity) if (isClientConfirmDeviceCredentialActivity) { return !isTopPackage || clientClassNameIfItIsConfirmDeviceCredentialActivity != topActivityClassName } return false } else if ( isTopPackage && isClientConfirmDeviceCredentialActivity && clientClassNameIfItIsConfirmDeviceCredentialActivity == topActivityClassName ) { return false } } return true } // LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java) } services/core/java/com/android/server/biometrics/AuthSession.java +3 −1 Original line number Diff line number Diff line Loading @@ -438,7 +438,8 @@ public final class AuthSession implements IBinder.DeathRecipient { */ boolean onErrorReceived(int sensorId, int cookie, @BiometricConstants.Errors int error, int vendorCode) throws RemoteException { Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error); Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error + " state: " + mState); if (!containsCookie(cookie)) { Slog.e(TAG, "Unknown/expired cookie: " + cookie); Loading Loading @@ -510,6 +511,7 @@ public final class AuthSession implements IBinder.DeathRecipient { mState = STATE_SHOWING_DEVICE_CREDENTIAL; mStatusBarService.onBiometricError(modality, error, vendorCode); } else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { cancelAllSensors(); mStatusBarService.hideAuthenticationDialog(mRequestId); // TODO: If multiple authenticators are simultaneously running, this will // need to be modified. Send the error to the client here, instead of doing Loading services/core/java/com/android/server/biometrics/Utils.java +13 −2 Original line number Diff line number Diff line Loading @@ -617,20 +617,31 @@ public class Utils { /** * Checks if a client package is running in the background. * * @param activityTaskManager Task manager which provides the list of clients running. * @param clientPackage The name of the package to be checked. * @return Whether the client package is running in background */ public static boolean isBackground(String clientPackage) { public static boolean isBackground(ActivityTaskManager activityTaskManager, String clientPackage) { Slog.v(TAG, "Checking if the authenticating is in background," + " clientPackage:" + clientPackage); final List<ActivityManager.RunningTaskInfo> tasks = ActivityTaskManager.getInstance().getTasks(Integer.MAX_VALUE); activityTaskManager.getTasks(Integer.MAX_VALUE); if (tasks == null || tasks.isEmpty()) { Slog.d(TAG, "No running tasks reported"); return true; } //Allow auth for top activity even if it is not visible final ActivityManager.RunningTaskInfo topTaskInfo = tasks.getFirst(); if (topTaskInfo != null && topTaskInfo.topActivity != null) { final String topPackage = topTaskInfo.topActivity.getPackageName(); if (topPackage.contentEquals(clientPackage)) { return false; } } for (ActivityManager.RunningTaskInfo taskInfo : tasks) { final ComponentName topActivity = taskInfo.topActivity; if (topActivity != null) { Loading services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +1 −1 Original line number Diff line number Diff line Loading @@ -208,7 +208,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> if (!mAllowBackgroundAuthentication && authenticated && !Utils.isKeyguard(getContext(), getOwnerString()) && !Utils.isSystem(getContext(), getOwnerString())) { isBackgroundAuth = Utils.isBackground(getOwnerString()); isBackgroundAuth = Utils.isBackground(mActivityTaskManager, getOwnerString()); } // Fail authentication if we can't confirm the client activity is on top. Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java +40 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; Loading Loading @@ -995,8 +996,8 @@ public class AuthControllerTest extends SysuiTestCase { } @Test public void testShowDialog_whenOwnerNotInForeground() { PromptInfo promptInfo = createTestPromptInfo(); public void testShowDialog_whenOwnerNotInForegroundAndNotVisible() { final PromptInfo promptInfo = createTestPromptInfo(); promptInfo.setAllowBackgroundAuthentication(false); switchTask("other_package"); mAuthController.showAuthenticationDialog(promptInfo, Loading @@ -1013,6 +1014,27 @@ public class AuthControllerTest extends SysuiTestCase { verify(mDialog1, never()).show(any()); } @Test public void testShowDialog_whenOwnerNotInForegroundAndVisible() { final PromptInfo promptInfo = createTestPromptInfo(); final AuthController.Callback callback = mock(AuthController.Callback.class); promptInfo.setAllowBackgroundAuthentication(false); switchTaskWithVisibility("other_package", true /* isVisible */); mAuthController.addCallback(callback); mAuthController.showAuthenticationDialog(promptInfo, mReceiver /* receiver */, new int[]{1} /* sensorIds */, false /* credentialAllowed */, true /* requireConfirmation */, 0 /* userId */, 0 /* operationId */, "testPackage", REQUEST_ID); assertNotNull(mAuthController.mCurrentDialog); verify(mDialog1).show(mWindowManager); } @Test public void testShowDialog_visibleBackgroundUser() { int backgroundUserId = 1001; Loading Loading @@ -1109,16 +1131,31 @@ public class AuthControllerTest extends SysuiTestCase { REQUEST_ID); } private void switchTask(String packageName) { private void switchTaskWithVisibility(String packageName, boolean isVisible) { final List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>(); final ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); taskInfo.topActivity = mock(ComponentName.class); when(taskInfo.topActivity.getPackageName()).thenReturn(packageName); when(taskInfo.topActivity.getClassName()).thenReturn(AuthControllerTest.class.getName()); tasks.add(taskInfo); final ActivityManager.RunningTaskInfo callingTaskInfo = mock(ActivityManager.RunningTaskInfo.class); callingTaskInfo.topActivity = mock(ComponentName.class); when(callingTaskInfo.topActivity.getPackageName()).thenReturn("Dialog1"); when(callingTaskInfo.topActivity.getClassName()).thenReturn( AuthControllerTest.class.getName()); callingTaskInfo.isVisible = isVisible; tasks.add(callingTaskInfo); when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks); } private void switchTask(String packageName) { switchTaskWithVisibility(packageName, false /* isVisible */); } private PromptInfo createTestPromptInfo() { PromptInfo promptInfo = new PromptInfo(); Loading
packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt +71 −6 Original line number Diff line number Diff line Loading @@ -162,8 +162,10 @@ object Utils { fun ActivityTaskManager.isSystemAppOrInBackground( context: Context, clientPackage: String, clientClassNameIfItIsConfirmDeviceCredentialActivity: String? clientClassNameIfItIsConfirmDeviceCredentialActivity: String?, ): Boolean { // TODO (b/409812027): Consolidate and scope out auth requirements for biometric prompt Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage") val tasks = getTasks(Int.MAX_VALUE) if (tasks == null || tasks.isEmpty()) { Loading @@ -176,11 +178,74 @@ object Utils { val topPackageEqualsToClient = topActivity!!.packageName == clientPackage val isClientConfirmDeviceCredentialActivity = clientClassNameIfItIsConfirmDeviceCredentialActivity != null if ( !isClientInBackgroundOrNotVisible( isSystemApp, isVisible = true, topPackageEqualsToClient, isClientConfirmDeviceCredentialActivity, clientClassNameIfItIsConfirmDeviceCredentialActivity, topActivity!!.className, isTopPackage = true, ) ) { return false } for (task in tasks) { val packageName = task.topActivity!!.packageName val className = task.topActivity!!.className val isVisible = task.isVisible Log.v(TAG, "Running task, top: $packageName, isVisible: $isVisible") if ( !isClientInBackgroundOrNotVisible( isSystemApp = false, isVisible, taskPackageEqualsClientPackage = packageName == clientPackage, isClientConfirmDeviceCredentialActivity, clientClassNameIfItIsConfirmDeviceCredentialActivity, className, ) ) { return false } } return true } fun isClientInBackgroundOrNotVisible( isSystemApp: Boolean, isVisible: Boolean, taskPackageEqualsClientPackage: Boolean, isClientConfirmDeviceCredentialActivity: Boolean, clientClassNameIfItIsConfirmDeviceCredentialActivity: String?, topActivityClassName: String, isTopPackage: Boolean = false, ): Boolean { if (isVisible) { if (isSystemApp || taskPackageEqualsClientPackage) { // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on // class name. return !(isSystemApp || topPackageEqualsToClient) || (isClientConfirmDeviceCredentialActivity && topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity) if (isClientConfirmDeviceCredentialActivity) { return !isTopPackage || clientClassNameIfItIsConfirmDeviceCredentialActivity != topActivityClassName } return false } else if ( isTopPackage && isClientConfirmDeviceCredentialActivity && clientClassNameIfItIsConfirmDeviceCredentialActivity == topActivityClassName ) { return false } } return true } // LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java) }
services/core/java/com/android/server/biometrics/AuthSession.java +3 −1 Original line number Diff line number Diff line Loading @@ -438,7 +438,8 @@ public final class AuthSession implements IBinder.DeathRecipient { */ boolean onErrorReceived(int sensorId, int cookie, @BiometricConstants.Errors int error, int vendorCode) throws RemoteException { Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error); Slog.d(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error + " state: " + mState); if (!containsCookie(cookie)) { Slog.e(TAG, "Unknown/expired cookie: " + cookie); Loading Loading @@ -510,6 +511,7 @@ public final class AuthSession implements IBinder.DeathRecipient { mState = STATE_SHOWING_DEVICE_CREDENTIAL; mStatusBarService.onBiometricError(modality, error, vendorCode); } else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { cancelAllSensors(); mStatusBarService.hideAuthenticationDialog(mRequestId); // TODO: If multiple authenticators are simultaneously running, this will // need to be modified. Send the error to the client here, instead of doing Loading
services/core/java/com/android/server/biometrics/Utils.java +13 −2 Original line number Diff line number Diff line Loading @@ -617,20 +617,31 @@ public class Utils { /** * Checks if a client package is running in the background. * * @param activityTaskManager Task manager which provides the list of clients running. * @param clientPackage The name of the package to be checked. * @return Whether the client package is running in background */ public static boolean isBackground(String clientPackage) { public static boolean isBackground(ActivityTaskManager activityTaskManager, String clientPackage) { Slog.v(TAG, "Checking if the authenticating is in background," + " clientPackage:" + clientPackage); final List<ActivityManager.RunningTaskInfo> tasks = ActivityTaskManager.getInstance().getTasks(Integer.MAX_VALUE); activityTaskManager.getTasks(Integer.MAX_VALUE); if (tasks == null || tasks.isEmpty()) { Slog.d(TAG, "No running tasks reported"); return true; } //Allow auth for top activity even if it is not visible final ActivityManager.RunningTaskInfo topTaskInfo = tasks.getFirst(); if (topTaskInfo != null && topTaskInfo.topActivity != null) { final String topPackage = topTaskInfo.topActivity.getPackageName(); if (topPackage.contentEquals(clientPackage)) { return false; } } for (ActivityManager.RunningTaskInfo taskInfo : tasks) { final ComponentName topActivity = taskInfo.topActivity; if (topActivity != null) { Loading
services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +1 −1 Original line number Diff line number Diff line Loading @@ -208,7 +208,7 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> if (!mAllowBackgroundAuthentication && authenticated && !Utils.isKeyguard(getContext(), getOwnerString()) && !Utils.isSystem(getContext(), getOwnerString())) { isBackgroundAuth = Utils.isBackground(getOwnerString()); isBackgroundAuth = Utils.isBackground(mActivityTaskManager, getOwnerString()); } // Fail authentication if we can't confirm the client activity is on top. Loading