Merge changes I9ded8efd,Ic26e47c1 into rvc-qpr-dev

* changes:
  Update authentication when encrypted or lockout
  Check foreground and appOps for BiometricPrompt#authenticate
This commit is contained in:
Kevin Chyn
2020-07-16 18:47:22 +00:00
committed by Android (Google) Code Review
11 changed files with 367 additions and 44 deletions

View File

@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.NonNull;
@@ -75,11 +76,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private static final int MSG_ERROR = 104;
private static final int MSG_REMOVED = 105;
private static final int MSG_ENUMERATED = 106;
private static final int MSG_FINGERPRINT_DETECTED = 107;
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
private FingerprintDetectionCallback mFingerprintDetectionCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
@@ -107,6 +110,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
private class OnFingerprintDetectionCancelListener implements OnCancelListener {
@Override
public void onCancel() {
cancelFingerprintDetect();
}
}
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
@@ -271,6 +281,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public void onAuthenticationAcquired(int acquireInfo) {}
};
/**
* Callback structure provided for {@link #detectFingerprint(CancellationSignal,
* FingerprintDetectionCallback, int)}.
* @hide
*/
public interface FingerprintDetectionCallback {
/**
* Invoked when a fingerprint has been detected.
*/
void onFingerprintDetected(int userId, boolean isStrongBiometric);
}
/**
* Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
* int, int, EnrollmentCallback)} must provide an implementation of this for listening to
@@ -453,6 +475,35 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
/**
* Uses the fingerprint hardware to detect for the presence of a finger, without giving details
* about accept/reject/lockout.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void detectFingerprint(@NonNull CancellationSignal cancel,
@NonNull FingerprintDetectionCallback callback, int userId) {
if (mService == null) {
return;
}
if (cancel.isCanceled()) {
Slog.w(TAG, "Detection already cancelled");
return;
} else {
cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
}
mFingerprintDetectionCallback = callback;
try {
mService.detectFingerprint(mToken, userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception when requesting finger detect", e);
}
}
/**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
@@ -797,6 +848,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
sendEnumeratedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
msg.arg2 /* groupId */);
break;
case MSG_FINGERPRINT_DETECTED:
sendFingerprintDetected(msg.arg1 /* userId */,
(boolean) msg.obj /* isStrongBiometric */);
break;
}
}
};
@@ -891,6 +946,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
private void sendFingerprintDetected(int userId, boolean isStrongBiometric) {
if (mFingerprintDetectionCallback == null) {
Slog.e(TAG, "sendFingerprintDetected, callback null");
return;
}
mFingerprintDetectionCallback.onFingerprintDetected(userId, isStrongBiometric);
}
/**
* @hide
*/
@@ -927,6 +990,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
private void cancelFingerprintDetect() {
if (mService == null) {
return;
}
try {
mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @hide
*/
@@ -1032,6 +1107,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
fp).sendToTarget();
}
@Override
public void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric) {
mHandler.obtainMessage(MSG_FINGERPRINT_DETECTED, userId, 0, isStrongBiometric)
.sendToTarget();
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();

View File

@@ -33,6 +33,11 @@ interface IFingerprintService {
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
// about accept/reject/lockout.
void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
String opPackageName);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
// called from BiometricService. The additional uid, pid, userId arguments should be determined
@@ -48,6 +53,9 @@ interface IFingerprintService {
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Cancel finger detection
void cancelFingerprintDetect(IBinder token, String opPackageName);
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,

View File

@@ -26,6 +26,7 @@ oneway interface IFingerprintServiceReceiver {
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId,
boolean isStrongBiometric);
void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int fingerId, int groupId, int remaining);

View File

@@ -1072,6 +1072,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
private boolean isEncryptedOrLockdown(int userId) {
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
final boolean isLockDown =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
return isEncrypted || isLockDown;
}
public boolean userNeedsStrongAuth() {
return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
!= LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -1248,6 +1257,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
// Trigger the fingerprint success path so the bouncer can be shown
private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
= this::handleFingerprintAuthenticated;
private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
@@ -2050,8 +2063,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
null, userId);
if (isEncryptedOrLockdown(userId)) {
mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
userId);
} else {
mFpm.authenticate(null, mFingerprintCancelSignal, 0,
mFingerprintAuthenticationCallback, null, userId);
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -2087,7 +2107,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean isUnlockWithFingerprintPossible(int userId) {
return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
&& mFpm.getEnrolledFingerprints(userId).size() > 0;
&& mFpm.hasEnrolledTemplates(userId);
}
private boolean isUnlockWithFacePossible(int userId) {

View File

@@ -174,6 +174,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -418,6 +420,43 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
}
@Test
public void testTriesToAuthenticateFingerprint_whenKeyguard() {
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
verify(mFingerprintManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
@Test
public void testFingerprintDoesNotAuth_whenEncrypted() {
testFingerprintWhenStrongAuth(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
}
@Test
public void testFingerprintDoesNotAuth_whenDpmLocked() {
testFingerprintWhenStrongAuth(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
}
@Test
public void testFingerprintDoesNotAuth_whenUserLockdown() {
testFingerprintWhenStrongAuth(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
private void testFingerprintWhenStrongAuth(int strongAuth) {
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
verify(mFingerprintManager, never())
.authenticate(any(), any(), anyInt(), any(), any(), anyInt());
verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt());
}
@Test
public void testTriesToAuthenticate_whenBouncer() {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);

View File

@@ -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;
}
}

View File

@@ -722,10 +722,9 @@ public abstract class BiometricServiceBase extends SystemService
}
}
protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
ArrayList<Byte> token) {
protected void handleAuthenticated(boolean authenticated,
BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
ClientMonitor client = mCurrentClient;
final boolean authenticated = identifier.getBiometricId() != 0;
if (client != null && client.onAuthenticated(identifier, authenticated, token)) {
removeClient(client);
@@ -1019,7 +1018,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 +1041,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

View File

@@ -16,20 +16,38 @@
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;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
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 com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
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 +274,50 @@ 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;
}
public static boolean isKeyguard(Context context, String clientPackage) {
final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
== PackageManager.PERMISSION_GRANTED;
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
context.getResources().getString(R.string.config_keyguardComponent));
final String keyguardPackage = keyguardComponent != null
? keyguardComponent.getPackageName() : null;
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
}
private static boolean containsFlag(int haystack, int needle) {
return (haystack & needle) != 0;
}
public static boolean isUserEncryptedOrLockdown(@NonNull LockPatternUtils lpu, int user) {
final int strongAuth = lpu.getStrongAuthForUser(user);
final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
final boolean isLockDown = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown);
return isEncrypted || isLockDown;
}
}

View File

@@ -896,8 +896,9 @@ public class FaceService extends BiometricServiceBase {
public void onAuthenticated(final long deviceId, final int faceId, final int userId,
ArrayList<Byte> token) {
mHandler.post(() -> {
Face face = new Face("", faceId, deviceId);
FaceService.super.handleAuthenticated(face, token);
final Face face = new Face("", faceId, deviceId);
final boolean authenticated = faceId != 0;
FaceService.super.handleAuthenticated(authenticated, face, token);
});
}

View File

@@ -56,6 +56,7 @@ import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -64,6 +65,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
@@ -72,6 +74,7 @@ import com.android.server.biometrics.ClientMonitor;
import com.android.server.biometrics.Constants;
import com.android.server.biometrics.EnumerateClient;
import com.android.server.biometrics.RemovalClient;
import com.android.server.biometrics.Utils;
import org.json.JSONArray;
import org.json.JSONException;
@@ -124,6 +127,8 @@ public class FingerprintService extends BiometricServiceBase {
}
private final class FingerprintAuthClient extends AuthenticationClientImpl {
private final boolean mDetectOnly;
@Override
protected boolean isFingerprint() {
return true;
@@ -133,9 +138,10 @@ public class FingerprintService extends BiometricServiceBase {
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, int cookie,
boolean requireConfirmation) {
boolean requireConfirmation, boolean detectOnly) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
restricted, owner, cookie, requireConfirmation);
mDetectOnly = detectOnly;
}
@Override
@@ -177,6 +183,10 @@ public class FingerprintService extends BiometricServiceBase {
return super.handleFailedAttempt();
}
boolean isDetectOnly() {
return mDetectOnly;
}
}
/**
@@ -234,18 +244,55 @@ public class FingerprintService extends BiometricServiceBase {
}
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
public void authenticate(final IBinder token, final long opId, final int userId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
updateActiveGroup(groupId, opPackageName);
if (Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)
&& Utils.isKeyguard(getContext(), opPackageName)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
// SafetyNet for b/79776455
EventLog.writeEvent(0x534e4554, "79776455");
Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
return;
}
updateActiveGroup(userId, opPackageName);
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, groupId, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */);
mCurrentUserId, userId, opId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */, false /* detectOnly */);
authenticateInternal(client, opId, opPackageName);
}
@Override
public void detectFingerprint(final IBinder token, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
return;
}
if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong. This should only
// ever be invoked when the user is encrypted or lockdown.
Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
return;
}
Slog.d(TAG, "detectFingerprint, owner: " + opPackageName + ", user: " + userId);
updateActiveGroup(userId, opPackageName);
final boolean restricted = isRestricted();
final int operationId = 0;
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, userId, operationId, restricted, opPackageName,
0 /* cookie */, false /* requireConfirmation */, true /* detectOnly */);
authenticateInternal(client, operationId, opPackageName);
}
@Override // Binder call
public void prepareForAuthentication(IBinder token, long opId, int groupId,
IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
@@ -257,7 +304,7 @@ public class FingerprintService extends BiometricServiceBase {
mDaemonWrapper, mHalDeviceId, token,
new BiometricPromptServiceListenerImpl(wrapperReceiver),
mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
false /* requireConfirmation */);
false /* requireConfirmation */, false /* detectOnly */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -274,6 +321,17 @@ public class FingerprintService extends BiometricServiceBase {
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelFingerprintDetect(final IBinder token, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: "
+ opPackageName);
return;
}
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId, boolean fromClient) {
@@ -518,7 +576,12 @@ public class FingerprintService extends BiometricServiceBase {
BiometricAuthenticator.Identifier biometric, int userId)
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
if (biometric == null || biometric instanceof Fingerprint) {
final ClientMonitor client = getCurrentClient();
if (client instanceof FingerprintAuthClient
&& ((FingerprintAuthClient) client).isDetectOnly()) {
mFingerprintServiceReceiver
.onFingerprintDetected(deviceId, userId, isStrongBiometric());
} else if (biometric == null || biometric instanceof Fingerprint) {
mFingerprintServiceReceiver.onAuthenticationSucceeded(deviceId,
(Fingerprint) biometric, userId, isStrongBiometric());
} else {
@@ -575,6 +638,7 @@ public class FingerprintService extends BiometricServiceBase {
private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
new ResetFailedAttemptsForUserRunnable();
private final LockPatternUtils mLockPatternUtils;
/**
* Receives callbacks from the HAL.
@@ -608,8 +672,17 @@ public class FingerprintService extends BiometricServiceBase {
public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
ArrayList<Byte> token) {
mHandler.post(() -> {
Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleAuthenticated(fp, token);
boolean authenticated = fingerId != 0;
final ClientMonitor client = getCurrentClient();
if (client instanceof FingerprintAuthClient) {
if (((FingerprintAuthClient) client).isDetectOnly()) {
Slog.w(TAG, "Detect-only. Device is encrypted or locked down");
authenticated = true;
}
}
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
FingerprintService.super.handleAuthenticated(authenticated, fp, token);
});
}
@@ -722,6 +795,7 @@ public class FingerprintService extends BiometricServiceBase {
mAlarmManager = context.getSystemService(AlarmManager.class);
context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
getLockoutBroadcastPermission(), null /* handler */);
mLockPatternUtils = new LockPatternUtils(context);
}
@Override

View File

@@ -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();
@@ -166,6 +172,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 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);