blob: 0f549842a7638df454648db7ea03f287b29a224e [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics;
// TODO(b/141025588): Create separate internal and external permissions for AuthService.
// TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.iris.IIrisService;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
import com.android.server.biometrics.face.FaceAuthenticator;
import com.android.server.biometrics.fingerprint.FingerprintAuthenticator;
import com.android.server.biometrics.iris.IrisAuthenticator;
/**
* System service that provides an interface for authenticating with biometrics and
* PIN/pattern/password to BiometricPrompt and lock screen.
*/
public class AuthService extends SystemService {
private static final String TAG = "AuthService";
private static final boolean DEBUG = false;
private final Injector mInjector;
private IBiometricService mBiometricService;
@VisibleForTesting
final IAuthService.Stub mImpl;
/**
* Class for injecting dependencies into AuthService.
* TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger).
*/
@VisibleForTesting
public static class Injector {
/**
* Allows to mock BiometricService for testing.
*/
@VisibleForTesting
public IBiometricService getBiometricService() {
return IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
}
/**
* Allows to stub publishBinderService(...) for testing.
*/
@VisibleForTesting
public void publishBinderService(AuthService service, IAuthService.Stub impl) {
service.publishBinderService(Context.AUTH_SERVICE, impl);
}
/**
* Allows to test with various device sensor configurations.
* @param context
* @return
*/
@VisibleForTesting
public String[] getConfiguration(Context context) {
return context.getResources().getStringArray(R.array.config_biometric_sensors);
}
/**
* Allows us to mock FingerprintService for testing
*/
@VisibleForTesting
public IFingerprintService getFingerprintService() {
return IFingerprintService.Stub.asInterface(
ServiceManager.getService(Context.FINGERPRINT_SERVICE));
}
/**
* Allows us to mock FaceService for testing
*/
@VisibleForTesting
public IFaceService getFaceService() {
return IFaceService.Stub.asInterface(
ServiceManager.getService(Context.FACE_SERVICE));
}
/**
* Allows us to mock IrisService for testing
*/
@VisibleForTesting
public IIrisService getIrisService() {
return IIrisService.Stub.asInterface(
ServiceManager.getService(Context.IRIS_SERVICE));
}
}
private final class AuthServiceImpl extends IAuthService.Stub {
@Override
public void authenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
throws RemoteException {
final int callingUserId = UserHandle.getCallingUserId();
// In the BiometricServiceBase, do the AppOps and foreground check.
if (userId == callingUserId) {
// Check the USE_BIOMETRIC permission here.
checkPermission();
} else {
// Only allow internal clients to authenticate with a different userId
Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
+ userId);
checkInternalPermission();
}
if (token == null || receiver == null || opPackageName == null || bundle == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
}
mBiometricService.authenticate(token, sessionId, userId, receiver, opPackageName,
bundle);
}
@Override
public int canAuthenticate(String opPackageName, int userId,
@Authenticators.Types int authenticators) throws RemoteException {
final int callingUserId = UserHandle.getCallingUserId();
Slog.d(TAG, "canAuthenticate, userId: " + userId + ", callingUserId: " + callingUserId
+ ", authenticators: " + authenticators);
if (userId != callingUserId) {
checkInternalPermission();
} else {
checkPermission();
}
return mBiometricService.canAuthenticate(opPackageName, userId, authenticators);
}
@Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName)
throws RemoteException {
checkInternalPermission();
return mBiometricService.hasEnrolledBiometrics(userId, opPackageName);
}
@Override
public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
throws RemoteException {
checkInternalPermission();
mBiometricService.registerEnabledOnKeyguardCallback(callback);
}
@Override
public void setActiveUser(int userId) throws RemoteException {
checkInternalPermission();
mBiometricService.setActiveUser(userId);
}
@Override
public void resetLockout(byte[] token) throws RemoteException {
checkInternalPermission();
mBiometricService.resetLockout(token);
}
}
public AuthService(Context context) {
this(context, new Injector());
}
public AuthService(Context context, Injector injector) {
super(context);
mInjector = injector;
mImpl = new AuthServiceImpl();
}
private void registerAuthenticator(SensorConfig config) throws RemoteException {
Slog.d(TAG, "Registering ID: " + config.mId
+ " Modality: " + config.mModality
+ " Strength: " + config.mStrength);
final IBiometricAuthenticator.Stub authenticator;
switch (config.mModality) {
case TYPE_FINGERPRINT:
final IFingerprintService fingerprintService = mInjector.getFingerprintService();
if (fingerprintService == null) {
Slog.e(TAG, "Attempting to register with null FingerprintService. Please check"
+ " your device configuration.");
return;
}
authenticator = new FingerprintAuthenticator(fingerprintService);
break;
case TYPE_FACE:
final IFaceService faceService = mInjector.getFaceService();
if (faceService == null) {
Slog.e(TAG, "Attempting to register with null FaceService. Please check your"
+ " device configuration.");
return;
}
authenticator = new FaceAuthenticator(faceService);
break;
case TYPE_IRIS:
final IIrisService irisService = mInjector.getIrisService();
if (irisService == null) {
Slog.e(TAG, "Attempting to register with null IrisService. Please check your"
+ " device configuration.");
return;
}
authenticator = new IrisAuthenticator(irisService);
break;
default:
Slog.e(TAG, "Unknown modality: " + config.mModality);
return;
}
mBiometricService.registerAuthenticator(config.mId, config.mModality, config.mStrength,
authenticator);
}
@Override
public void onStart() {
mBiometricService = mInjector.getBiometricService();
final String[] configs = mInjector.getConfiguration(getContext());
for (int i = 0; i < configs.length; i++) {
try {
registerAuthenticator(new SensorConfig(configs[i]));
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
}
mInjector.publishBinderService(this, mImpl);
}
private void checkInternalPermission() {
getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
"Must have USE_BIOMETRIC_INTERNAL permission");
}
private void checkPermission() {
if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
!= PackageManager.PERMISSION_GRANTED) {
getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
"Must have USE_BIOMETRIC permission");
}
}
}