Check foreground and appOps for BiometricPrompt#authenticate
Bug: 158481661
Test: Callers with incorrect opPackageName are no longer able to
request auth
Test: Callers not in foreground are no longer able to request auth
Test: atest AuthServiceTest
Change-Id: Ic26e47c11395a5fded1d2ab3e75466fdbd6c2f1b
Merged-In: Ic26e47c11395a5fded1d2ab3e75466fdbd6c2f1b
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 061972c..1312679 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -28,6 +28,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricPrompt;
@@ -128,6 +129,11 @@
return IIrisService.Stub.asInterface(
ServiceManager.getService(Context.IRIS_SERVICE));
}
+
+ @VisibleForTesting
+ public AppOpsManager getAppOps(Context context) {
+ return context.getSystemService(AppOpsManager.class);
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -138,6 +144,8 @@
// Only allow internal clients to authenticate with a different userId.
final int callingUserId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
if (userId == callingUserId) {
checkPermission();
} else {
@@ -146,6 +154,16 @@
checkInternalPermission();
}
+ if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
+ Slog.e(TAG, "Denied by app ops: " + opPackageName);
+ return;
+ }
+
+ if (!Utils.isForeground(callingUid, callingPid)) {
+ Slog.e(TAG, "Caller is not foreground: " + opPackageName);
+ return;
+ }
+
if (token == null || receiver == null || opPackageName == null || bundle == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
@@ -163,8 +181,6 @@
checkInternalPermission();
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
final long identity = Binder.clearCallingIdentity();
try {
mBiometricService.authenticate(
@@ -392,4 +408,9 @@
"Must have USE_BIOMETRIC permission");
}
}
+
+ private boolean checkAppOps(int uid, String opPackageName, String reason) {
+ return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
+ opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 75452ea..88469a2 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -1019,7 +1019,7 @@
return false;
}
- if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
+ if (requireForeground && !(Utils.isForeground(uid, pid) || isCurrentClient(
opPackageName))) {
Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
return false;
@@ -1042,29 +1042,6 @@
return mKeyguardPackage.equals(clientPackage);
}
- private boolean isForegroundActivity(int uid, int pid) {
- try {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- if (procs == null) {
- Slog.e(getTag(), "Processes null, defaulting to true");
- return true;
- }
-
- int N = procs.size();
- for (int i = 0; i < N; i++) {
- ActivityManager.RunningAppProcessInfo proc = procs.get(i);
- if (proc.pid == pid && proc.uid == uid
- && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
- return true;
- }
- }
- } catch (RemoteException e) {
- Slog.w(getTag(), "am.getRunningAppProcesses() failed");
- }
- return false;
- }
-
/**
* Calls the HAL to switch states to the new task. If there's already a current task,
* it calls cancel() and sets mPendingClient to begin when the current task finishes
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 14378da..543ce57 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -16,8 +16,10 @@
package com.android.server.biometrics;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.app.ActivityManager;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
@@ -25,11 +27,16 @@
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.os.Build;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
+import java.util.List;
+
public class Utils {
+ private static final String TAG = "BiometricUtils";
+
public static boolean isDebugEnabled(Context context, int targetUserId) {
if (targetUserId == UserHandle.USER_NULL) {
return false;
@@ -256,4 +263,26 @@
throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
}
}
+
+ public static boolean isForeground(int callingUid, int callingPid) {
+ try {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ if (procs == null) {
+ Slog.e(TAG, "No running app processes found, defaulting to true");
+ return true;
+ }
+
+ for (int i = 0; i < procs.size(); i++) {
+ ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == callingPid && proc.uid == callingUid
+ && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
}
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 30bb38a..ad0b092 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -72,6 +73,8 @@
IIrisService mIrisService;
@Mock
IFaceService mFaceService;
+ @Mock
+ AppOpsManager mAppOpsManager;
@Before
public void setUp() {
@@ -90,6 +93,7 @@
when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
when(mInjector.getFaceService()).thenReturn(mFaceService);
when(mInjector.getIrisService()).thenReturn(mIrisService);
+ when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
}
@Test
@@ -137,7 +141,9 @@
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
- public void testAuthenticate_callsBiometricServiceAuthenticate() throws Exception {
+ public void testAuthenticate_appOpsOk_callsBiometricServiceAuthenticate() throws Exception {
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
+ any())).thenReturn(AppOpsManager.MODE_ALLOWED);
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
@@ -167,6 +173,38 @@
}
@Test
+ public void testAuthenticate_appOpsDenied_doesNotCallBiometricService() throws Exception {
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_USE_BIOMETRIC), anyInt(), any(), any(),
+ any())).thenReturn(AppOpsManager.MODE_ERRORED);
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final Binder token = new Binder();
+ final Bundle bundle = new Bundle();
+ final long sessionId = 0;
+ final int userId = 0;
+
+ mAuthService.mImpl.authenticate(
+ token,
+ sessionId,
+ userId,
+ mReceiver,
+ TEST_OP_PACKAGE_NAME,
+ bundle);
+ waitForIdle();
+ verify(mBiometricService, never()).authenticate(
+ eq(token),
+ eq(sessionId),
+ eq(userId),
+ eq(mReceiver),
+ eq(TEST_OP_PACKAGE_NAME),
+ eq(bundle),
+ eq(Binder.getCallingUid()),
+ eq(Binder.getCallingPid()),
+ eq(UserHandle.getCallingUserId()));
+ }
+
+ @Test
public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();