diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index b8c639ceb8ba5..a47904c40cc35 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_FINGERPRIN 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.IAuthService; @@ -127,6 +128,11 @@ public class AuthService extends SystemService { 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 { @@ -137,6 +143,8 @@ public class AuthService extends SystemService { // 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 { @@ -145,6 +153,16 @@ public class AuthService extends SystemService { 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 || promptInfo == null) { Slog.e(TAG, "Unable to authenticate, one or more null arguments"); return; @@ -155,8 +173,6 @@ public class AuthService extends SystemService { checkInternalPermission(); } - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); final long identity = Binder.clearCallingIdentity(); try { mBiometricService.authenticate( @@ -370,4 +386,9 @@ public class AuthService extends SystemService { "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/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index efa03b88d62d3..88fd44456ff66 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -17,6 +17,7 @@ package com.android.server.biometrics; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; import static android.hardware.biometrics.BiometricManager.Authenticators; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; @@ -60,6 +61,8 @@ import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; import com.android.server.biometrics.sensors.ClientMonitor; +import java.util.List; + public class Utils { private static final String TAG = "BiometricUtils"; @@ -419,4 +422,26 @@ public class Utils { Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown); return isEncrypted || isLockDown; } + + public static boolean isForeground(int callingUid, int callingPid) { + try { + final List 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/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 4e614a783440a..4d33fd6301e24 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -415,7 +415,7 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied"); return false; } - if (requireForeground && !(isForegroundActivity(uid, pid))) { + if (requireForeground && !Utils.isForeground(uid, pid)) { Slog.w(TAG, "Rejecting " + opPackageName + "; not in foreground"); return false; } @@ -434,28 +434,5 @@ public class FingerprintService extends SystemService { return appOpsOk; } - private boolean isForegroundActivity(int uid, int pid) { - try { - final List procs = - ActivityManager.getService().getRunningAppProcesses(); - if (procs == null) { - Slog.e(TAG, "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(TAG, "am.getRunningAppProcesses() failed"); - } - return false; - } - private native NativeHandle convertSurfaceToNativeHandle(Surface surface); } 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 75e0d9144f573..a5df53205a367 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.never; 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; @@ -75,6 +76,8 @@ public class AuthServiceTest { IIrisService mIrisService; @Mock IFaceService mFaceService; + @Mock + AppOpsManager mAppOpsManager; @Before public void setUp() { @@ -93,6 +96,7 @@ public class AuthServiceTest { when(mInjector.getFingerprintService()).thenReturn(mFingerprintService); when(mInjector.getFaceService()).thenReturn(mFaceService); when(mInjector.getIrisService()).thenReturn(mIrisService); + when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager); } @Test @@ -140,7 +144,9 @@ public class AuthServiceTest { // 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(); @@ -169,6 +175,38 @@ public class AuthServiceTest { eq(UserHandle.getCallingUserId())); } + @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 PromptInfo promptInfo = new PromptInfo(); + final long sessionId = 0; + final int userId = 0; + + mAuthService.mImpl.authenticate( + token, + sessionId, + userId, + mReceiver, + TEST_OP_PACKAGE_NAME, + promptInfo); + waitForIdle(); + verify(mBiometricService, never()).authenticate( + eq(token), + eq(sessionId), + eq(userId), + eq(mReceiver), + eq(TEST_OP_PACKAGE_NAME), + eq(promptInfo), + eq(Binder.getCallingUid()), + eq(Binder.getCallingPid()), + eq(UserHandle.getCallingUserId())); + } + @Test public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception { mAuthService = new AuthService(mContext, mInjector);