diff options
4 files changed, 92 insertions, 27 deletions
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 061972c5723a..131267924179 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.BiometricPrompt; @@ -128,6 +129,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 { @@ -138,6 +144,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 { @@ -146,6 +154,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 || bundle == null) { Slog.e(TAG, "Unable to authenticate, one or more null arguments"); return; @@ -163,8 +181,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( @@ -392,4 +408,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/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 75452ea5fb61..88469a2caee8 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 @@ public abstract class BiometricServiceBase extends SystemService 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 @@ public abstract class BiometricServiceBase extends SystemService 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 14378da0a90b..543ce575b1fc 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; 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 @@ public class Utils { 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 30bb38a075be..ad0b092b5543 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; @@ -72,6 +73,8 @@ public class AuthServiceTest { IIrisService mIrisService; @Mock IFaceService mFaceService; + @Mock + AppOpsManager mAppOpsManager; @Before public void setUp() { @@ -90,6 +93,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 @@ -137,7 +141,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(); @@ -167,6 +173,38 @@ public class AuthServiceTest { } @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(); |