几个路径:
/frameworks/base/services/core/java/com/android/server/fingerprint/
/frameworks/base/core/java/android/hardware/fingerprint/
/system/core/fingerprintd/
/hardware/libhardware/include/hardware/fingerprint.h
/hardware/libhardware/modules/fingerprint/fingerprint.c
====================================================
/frameworks/base/services/core/java/com/android/server/fingerprint/
-------------------------------------------------------------------------------------------
FingerprintService.java
/**
* Copyright (C) 2014 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.fingerprint;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IUserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import com.android.server.SystemService;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintDaemonCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_FINGERPRINT;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
* The service is responsible for maintaining a list of clients and dispatching all
* fingerprint -related events.
*
* @hide
*/
public class FingerprintService extends SystemService implements IBinder.DeathRecipient {
private static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
private static final String FP_DATA_DIR = "fpdata";
private static final String FINGERPRINTD = "android.hardware.fingerprint.IFingerprintDaemon";
private static final int MSG_USER_SWITCHING = 10;
private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
private ClientMonitor mAuthClient = null;
private ClientMonitor mEnrollClient = null;
private ClientMonitor mRemoveClient = null;
private final AppOpsManager mAppOps;
private static final long MS_PER_SEC = 1000;
private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
private static final int MAX_FAILED_ATTEMPTS = 5;
private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
Handler mHandler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_USER_SWITCHING:
handleUserSwitching(msg.arg1);
break;
default:
Slog.w(TAG, "Unknown message:" + msg.what);
}
}
};
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
private Context mContext;
private long mHalDeviceId;
private int mFailedAttempts;
private IFingerprintDaemon mDaemon;
private PowerManager mPowerManager;
private final Runnable mLockoutReset = new Runnable() {
@Override
public void run() {
resetFailedAttempts();
}
};
public FingerprintService(Context context) {
super(context);
mContext = context;
mAppOps = context.getSystemService(AppOpsManager.class);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@Override
public void binderDied() {
Slog.v(TAG, "fingerprintd died");
mDaemon = null;
dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
public IFingerprintDaemon getFingerprintDaemon() {
if (mDaemon == null) {
mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(FINGERPRINTD));
if (mDaemon != null) {
try {
mDaemon.asBinder().linkToDeath(this, 0);
mDaemon.init(mDaemonCallback);
mHalDeviceId = mDaemon.openHal();
if (mHalDeviceId != 0) {
updateActiveGroup(ActivityManager.getCurrentUser());
} else {
Slog.w(TAG, "Failed to open Fingerprint HAL!");
mDaemon = null;
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to open fingeprintd HAL", e);
mDaemon = null; // try again later!
}
} else {
Slog.w(TAG, "fingerprint service not available");
}
}
return mDaemon;
}
protected void dispatchEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
if (fingerIds.length != groupIds.length) {
Slog.w(TAG, "fingerIds and groupIds differ in length: f[]="
+ fingerIds + ", g[]=" + groupIds);
return;
}
if (DEBUG) Slog.w(TAG, "Enumerate: f[]=" + fingerIds + ", g[]=" + groupIds);
// TODO: update fingerprint/name pairs
}
protected void dispatchRemoved(long deviceId, int fingerId, int groupId) {
final ClientMonitor client = mRemoveClient;
if (fingerId != 0) {
removeTemplateForUser(mRemoveClient, fingerId);
}
if (client != null && client.sendRemoved(fingerId, groupId)) {
removeClient(mRemoveClient);
}
}
protected void dispatchError(long deviceId, int error) {
if (mEnrollClient != null) {
final IBinder token = mEnrollClient.token;
if (mEnrollClient.sendError(error)) {
stopEnrollment(token, false);
}
} else if (mAuthClient != null) {
final IBinder token = mAuthClient.token;
if (mAuthClient.sendError(error)) {
stopAuthentication(token, false);
}
} else if (mRemoveClient != null) {
if (mRemoveClient.sendError(error)) removeClient(mRemoveClient);
}
}
protected void dispatchAuthenticated(long deviceId, int fingerId, int groupId) {
if (mAuthClient != null) {
final IBinder token = mAuthClient.token;
if (mAuthClient.sendAuthenticated(fingerId, groupId)) {
stopAuthentication(token, false);
removeClient(mAuthClient);
}
}
}
protected void dispatchAcquired(long deviceId, int acquiredInfo) {
if (mEnrollClient != null) {
if (mEnrollClient.sendAcquired(acquiredInfo)) {
removeClient(mEnrollClient);
}
} else if (mAuthClient != null) {
if (mAuthClient.sendAcquired(acquiredInfo)) {
removeClient(mAuthClient);
}
}
}
private void userActivity() {
long now = SystemClock.uptimeMillis();
mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
}
void handleUserSwitching(int userId) {
updateActiveGroup(userId);
}
protected void dispatchEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
if (mEnrollClient != null) {
if (mEnrollClient.sendEnrollResult(fingerId, groupId, remaining)) {
if (remaining == 0) {
addTemplateForUser(mEnrollClient, fingerId);
removeClient(mEnrollClient);
}
}
}
}
private void removeClient(ClientMonitor client) {
if (client == null) return;
client.destroy();
if (client == mAuthClient) {
mAuthClient = null;
} else if (client == mEnrollClient) {
mEnrollClient = null;
} else if (client == mRemoveClient) {
mRemoveClient = null;
}
}
private boolean inLockoutMode() {
return mFailedAttempts > MAX_FAILED_ATTEMPTS;
}
private void resetFailedAttempts() {
if (DEBUG && inLockoutMode()) {
Slog.v(TAG, "Reset fingerprint lockout");
}
mFailedAttempts = 0;
}
private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
mFailedAttempts++;
if (mFailedAttempts > MAX_FAILED_ATTEMPTS) {
// Failing multiple times will continue to push out the lockout time.
mHandler.removeCallbacks(mLockoutReset);
mHandler.postDelayed(mLockoutReset, FAIL_LOCKOUT_TIMEOUT_MS);
if (clientMonitor != null
&& !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
Slog.w(TAG, "Cannot send lockout message to client");
}
return true;
}
return false;
}
private void removeTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, clientMonitor.userId);
}
private void addTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
mFingerprintUtils.addFingerprintForUser(mContext, fingerId, clientMonitor.userId);
}
void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll: no fingeprintd!");
return;
}
stopPendingOperations(true);
mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted);
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final int result = daemon.enroll(cryptoToken, groupId, timeout);
if (result != 0) {
Slog.w(TAG, "startEnroll failed, result=" + result);
}
} catch (RemoteException e) {
Slog.e(TAG, "startEnroll failed", e);
}
}
public long startPreEnroll(IBinder token) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startPreEnroll: no fingeprintd!");
return 0;
}
try {
return daemon.preEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "startPreEnroll failed", e);
}
return 0;
}
public int startPostEnroll(IBinder token) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startPostEnroll: no fingeprintd!");
return 0;
}
try {
return daemon.postEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "startPostEnroll failed", e);
}
return 0;
}
private void stopPendingOperations(boolean initiatedByClient) {
if (mEnrollClient != null) {
stopEnrollment(mEnrollClient.token, initiatedByClient);
}
if (mAuthClient != null) {
stopAuthentication(mAuthClient.token, initiatedByClient);
}
// mRemoveClient is allowed to continue
}
/**
* Stop enrollment in progress and inform client if they initiated it.
*
* @param token token for client
* @param initiatedByClient if this call is the result of client action (e.g. calling cancel)
*/
void stopEnrollment(IBinder token, boolean initiatedByClient) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopEnrollment: no fingeprintd!");
return;
}
final ClientMonitor client = mEnrollClient;
if (client == null || client.token != token) return;
if (initiatedByClient) {
try {
int result = daemon.cancelEnrollment();
if (result != 0) {
Slog.w(TAG, "startEnrollCancel failed, result = " + result);
}
} catch (RemoteException e) {
Slog.e(TAG, "stopEnrollment failed", e);
}
client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
removeClient(mEnrollClient);
}
void startAuthentication(IBinder token, long opId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startAuthentication: no fingeprintd!");
return;
}
stopPendingOperations(true);
mAuthClient = new ClientMonitor(token, receiver, groupId, restricted);
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
Slog.w(TAG, "Cannot send timeout message to client");
}
mAuthClient = null;
return;
}
try {
final int result = daemon.authenticate(opId, groupId);
if (result != 0) {
Slog.w(TAG, "startAuthentication failed, result=" + result);
}
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
}
}
/**
* Stop authentication in progress and inform client if they initiated it.
*
* @param token token for client
* @param initiatedByClient if this call is the result of client action (e.g. calling cancel)
*/
void stopAuthentication(IBinder token, boolean initiatedByClient) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopAuthentication: no fingeprintd!");
return;
}
final ClientMonitor client = mAuthClient;
if (client == null || client.token != token) return;
if (initiatedByClient) {
try {
int result = daemon.cancelAuthentication();
if (result != 0) {
Slog.w(TAG, "stopAuthentication failed, result=" + result);
}
} catch (RemoteException e) {
Slog.e(TAG, "stopAuthentication failed", e);
}
client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
removeClient(mAuthClient);
}
void startRemove(IBinder token, int fingerId, int userId,
IFingerprintServiceReceiver receiver, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startRemove: no fingeprintd!");
return;
}
mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
if (result != 0) {
Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
}
} catch (RemoteException e) {
Slog.e(TAG, "startRemove failed", e);
}
}
public List<Fingerprint> getEnrolledFingerprints(int userId) {
return mFingerprintUtils.getFingerprintsForUser(mContext, userId);
}
public boolean hasEnrolledFingerprints(int userId) {
return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
}
boolean hasPermission(String permission) {
return getContext().checkCallingOrSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED;
}
void checkPermission(String permission) {
getContext().enforceCallingOrSelfPermission(permission,
"Must have " + permission + " permission.");
}
int getEffectiveUserId(int userId) {
UserManager um = UserManager.get(mContext);
if (um != null) {
final long callingIdentity = Binder.clearCallingIdentity();
userId = um.getCredentialOwnerProfile(userId);
Binder.restoreCallingIdentity(callingIdentity);
} else {
Slog.e(TAG, "Unable to acquire UserManager");
}
return userId;
}
boolean isCurrentUserOrProfile(int userId) {
UserManager um = UserManager.get(mContext);
// Allow current user or profiles of the current user...
List<UserInfo> profiles = um.getEnabledProfiles(userId);
final int n = profiles.size();
for (int i = 0; i < n; i++) {
if (profiles.get(i).id == userId) {
return true;
}
}
return false;
}
private boolean canUseFingerprint(String opPackageName) {
checkPermission(USE_FINGERPRINT);
return mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, Binder.getCallingUid(),
opPackageName) == AppOpsManager.MODE_ALLOWED;
}
private class ClientMonitor implements IBinder.DeathRecipient {
IBinder token;
IFingerprintServiceReceiver receiver;
int userId;
boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
boolean restricted) {
this.token = token;
this.receiver = receiver;
this.userId = userId;
this.restricted = restricted;
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
}
}
public void destroy() {
if (token != null) {
try {
token.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
// TODO: remove when duplicate call bug is found
Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
}
token = null;
}
receiver = null;
}
@Override
public void binderDied() {
token = null;
removeClient(this);
receiver = null;
}
@Override
protected void finalize() throws Throwable {
try {
if (token != null) {
if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
removeClient(this);
}
} finally {
super.finalize();
}
}
/*
* @return true if we're done.
*/
private boolean sendRemoved(int fingerId, int groupId) {
if (receiver == null) return true; // client not listening
try {
receiver.onRemoved(mHalDeviceId, fingerId, groupId);
return fingerId == 0;
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Removed:", e);
}
return false;
}
/*
* @return true if we're done.
*/
private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
if (receiver == null) return true; // client not listening
FingerprintUtils.vibrateFingerprintSuccess(getContext());
try {
receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
return remaining == 0;
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify EnrollResult:", e);
return true;
}
}
/*
* @return true if we're done.
*/
private boolean sendAuthenticated(int fpId, int groupId) {
boolean result = false;
boolean authenticated = fpId != 0;
if (receiver != null) {
try {
if (!authenticated) {
receiver.onAuthenticationFailed(mHalDeviceId);
} else {
Fingerprint fp = !restricted ?
new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Authenticated:", e);
result = true; // client failed
}
} else {
result = true; // client not listening
}
if (fpId == 0) {
FingerprintUtils.vibrateFingerprintError(getContext());
result |= handleFailedAttempt(this);
} else {
FingerprintUtils.vibrateFingerprintSuccess(getContext());
result |= true; // we have a valid fingerprint
mLockoutReset.run();
}
return result;
}
/*
* @return true if we're done.
*/
private boolean sendAcquired(int acquiredInfo) {
if (receiver == null) return true; // client not listening
try {
receiver.onAcquired(mHalDeviceId, acquiredInfo);
return false; // acquisition continues...
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendAcquired:", e);
return true; // client failed
}
finally {
// Good scans will keep the device awake
if (acquiredInfo == FINGERPRINT_ACQUIRED_GOOD) {
userActivity();
}
}
}
/*
* @return true if we're done.
*/
private boolean sendError(int error) {
if (receiver != null) {
try {
receiver.onError(mHalDeviceId, error);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendError:", e);
}
}
return true; // errors always terminate progress
}
}
private IFingerprintDaemonCallback mDaemonCallback = new IFingerprintDaemonCallback.Stub() {
@Override
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
dispatchEnrollResult(deviceId, fingerId, groupId, remaining);
}
@Override
public void onAcquired(long deviceId, int acquiredInfo) {
dispatchAcquired(deviceId, acquiredInfo);
}
@Override
public void onAuthenticated(long deviceId, int fingerId, int groupId) {
dispatchAuthenticated(deviceId, fingerId, groupId);
}
@Override
public void onError(long deviceId, int error) {
dispatchError(deviceId, error);
}
@Override
public void onRemoved(long deviceId, int fingerId, int groupId) {
dispatchRemoved(deviceId, fingerId, groupId);
}
@Override
public void onEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
dispatchEnumerate(deviceId, fingerIds, groupIds);
}
};
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
private static final String KEYGUARD_PACKAGE = "com.android.systemui";
@Override // Binder call
public long preEnroll(IBinder token) {
checkPermission(MANAGE_FINGERPRINT);
return startPreEnroll(token);
}
@Override // Binder call
public int postEnroll(IBinder token) {
checkPermission(MANAGE_FINGERPRINT);
return startPostEnroll(token);
}
@Override // Binder call
public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags) {
checkPermission(MANAGE_FINGERPRINT);
final int limit = mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(callingUid);
final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
if (enrolled >= limit) {
Slog.w(TAG, "Too many fingerprints registered");
return;
}
final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default fingerprints for the user.
final int effectiveGroupId = getEffectiveUserId(groupId);
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
startEnrollment(token, cryptoClone, effectiveGroupId, receiver, flags, restricted);
}
});
}
private boolean isRestricted() {
// Only give privileged apps (like Settings) access to fingerprint info
final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
return restricted;
}
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
checkPermission(MANAGE_FINGERPRINT);
mHandler.post(new Runnable() {
@Override
public void run() {
stopEnrollment(token, true);
}
});
}
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
Slog.w(TAG, "Can't authenticate non-current user");
return;
}
if (!canUseFingerprint(opPackageName)) {
Slog.w(TAG, "Calling not granted permission to use fingerprint");
return;
}
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default fingerprints for the user.
final int effectiveGroupId = getEffectiveUserId(groupId);
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted);
}
});
}
@Override // Binder call
public void cancelAuthentication(final IBinder token, String opPackageName) {
if (!canUseFingerprint(opPackageName)) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
stopAuthentication(token, true);
}
});
}
@Override // Binder call
public void remove(final IBinder token, final int fingerId, final int groupId,
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
final boolean restricted = isRestricted();
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default fingerprints for the user.
final int effectiveGroupId = getEffectiveUserId(groupId);
mHandler.post(new Runnable() {
@Override
public void run() {
startRemove(token, fingerId, effectiveGroupId, receiver, restricted);
}
});
}
@Override // Binder call
public boolean isHardwareDetected(long deviceId, String opPackageName) {
if (!canUseFingerprint(opPackageName)) {
return false;
}
return mHalDeviceId != 0;
}
@Override // Binder call
public void rename(final int fingerId, final int groupId, final String name) {
checkPermission(MANAGE_FINGERPRINT);
// Group ID is arbitrarily set to parent profile user ID. It just represents
// the default fingerprints for the user.
final int effectiveGroupId = getEffectiveUserId(groupId);
mHandler.post(new Runnable() {
@Override
public void run() {
mFingerprintUtils.renameFingerprintForUser(mContext, fingerId,
effectiveGroupId, name);
}
});
}
@Override // Binder call
public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
if (!canUseFingerprint(opPackageName)) {
return Collections.emptyList();
}
int effectiveUserId = getEffectiveUserId(userId);
return FingerprintService.this.getEnrolledFingerprints(effectiveUserId);
}
@Override // Binder call
public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
if (!canUseFingerprint(opPackageName)) {
return false;
}
int effectiveUserId = getEffectiveUserId(userId);
return FingerprintService.this.hasEnrolledFingerprints(effectiveUserId);
}
@Override // Binder call
public long getAuthenticatorId(String opPackageName) {
// In this method, we're not checking whether the caller is permitted to use fingerprint
// API because current authenticator ID is leaked (in a more contrived way) via Android
// Keystore (android.security.keystore package): the user of that API can create a key
// which requires fingerprint authentication for its use, and then query the key's
// characteristics (hidden API) which returns, among other things, fingerprint
// authenticator ID which was active at key creation time.
//
// Reason: The part of Android Keystore which runs inside an app's process invokes this
// method in certain cases. Those cases are not always where the developer demonstrates
// explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
// unexpected SecurityException this method does not check whether its caller is
// permitted to use fingerprint API.
//
// The permission check should be restored once Android Keystore no longer invokes this
// method from inside app processes.
return FingerprintService.this.getAuthenticatorId();
}
}
@Override
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
IFingerprintDaemon daemon = getFingerprintDaemon();
if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
listenForUserSwitches();
}
private void updateActiveGroup(int userId) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon != null) {
try {
userId = getEffectiveUserId(userId);
final File systemDir = Environment.getUserSystemDirectory(userId);
final File fpDir = new File(systemDir, FP_DATA_DIR);
if (!fpDir.exists()) {
if (!fpDir.mkdir()) {
Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
return;
}
// Calling mkdir() from this process will create a directory with our
// permissions (inherited from the containing dir). This command fixes
// the label.
if (!SELinux.restorecon(fpDir)) {
Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
return;
}
}
daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup():", e);
}
}
}
private void listenForUserSwitches() {
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
new IUserSwitchObserver.Stub() {
@Override
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
.sendToTarget();
}
@Override
public void onUserSwitchComplete(int newUserId) throws RemoteException {
// Ignore.
}
@Override
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
});
} catch (RemoteException e) {
Slog.w(TAG, "Failed to listen for user switching event" ,e);
}
}
public long getAuthenticatorId() {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon != null) {
try {
return daemon.getAuthenticatorId();
} catch (RemoteException e) {
Slog.e(TAG, "getAuthenticatorId failed", e);
}
}
return 0;
}
}
FingerprintsUserState.java
/*
* Copyright (C) 2015 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.fingerprint;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Class managing the set of fingerprint per user across device reboots.
*/
class FingerprintsUserState {
private static final String TAG = "FingerprintState";
private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
private static final String TAG_FINGERPRINTS = "fingerprints";
private static final String TAG_FINGERPRINT = "fingerprint";
private static final String ATTR_NAME = "name";
private static final String ATTR_GROUP_ID = "groupId";
private static final String ATTR_FINGER_ID = "fingerId";
private static final String ATTR_DEVICE_ID = "deviceId";
private final File mFile;
@GuardedBy("this")
private final ArrayList<Fingerprint> mFingerprints = new ArrayList<Fingerprint>();
private final Context mCtx;
public FingerprintsUserState(Context ctx, int userId) {
mFile = getFileForUser(userId);
mCtx = ctx;
synchronized (this) {
readStateSyncLocked();
}
}
public void addFingerprint(int fingerId, int groupId) {
synchronized (this) {
mFingerprints.add(new Fingerprint(getUniqueName(), groupId, fingerId, 0));
scheduleWriteStateLocked();
}
}
public void removeFingerprint(int fingerId) {
synchronized (this) {
for (int i = 0; i < mFingerprints.size(); i++) {
if (mFingerprints.get(i).getFingerId() == fingerId) {
mFingerprints.remove(i);
scheduleWriteStateLocked();
break;
}
}
}
}
public void renameFingerprint(int fingerId, CharSequence name) {
synchronized (this) {
for (int i = 0; i < mFingerprints.size(); i++) {
if (mFingerprints.get(i).getFingerId() == fingerId) {
Fingerprint old = mFingerprints.get(i);
mFingerprints.set(i, new Fingerprint(name, old.getGroupId(), old.getFingerId(),
old.getDeviceId()));
scheduleWriteStateLocked();
break;
}
}
}
}
public List<Fingerprint> getFingerprints() {
synchronized (this) {
return getCopy(mFingerprints);
}
}
/**
* Finds a unique name for the given fingerprint
* @return unique name
*/
private String getUniqueName() {
int guess = 1;
while (true) {
// Not the most efficient algorithm in the world, but there shouldn't be more than 10
String name = mCtx.getString(com.android.internal.R.string.fingerprint_name_template,
guess);
if (isUnique(name)) {
return name;
}
guess++;
}
}
private boolean isUnique(String name) {
for (Fingerprint fp : mFingerprints) {
if (fp.getName().equals(name)) {
return false;
}
}
return true;
}
private static File getFileForUser(int userId) {
return new File(Environment.getUserSystemDirectory(userId), FINGERPRINT_FILE);
}
private final Runnable mWriteStateRunnable = new Runnable() {
@Override
public void run() {
doWriteState();
}
};
private void scheduleWriteStateLocked() {
AsyncTask.execute(mWriteStateRunnable);
}
private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size());
for (int i = 0; i < array.size(); i++) {
Fingerprint fp = array.get(i);
result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),
fp.getDeviceId()));
}
return result;
}
private void doWriteState() {
AtomicFile destination = new AtomicFile(mFile);
ArrayList<Fingerprint> fingerprints;
synchronized (this) {
fingerprints = getCopy(mFingerprints);
}
FileOutputStream out = null;
try {
out = destination.startWrite();
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(out, "utf-8");
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
serializer.startTag(null, TAG_FINGERPRINTS);
final int count = fingerprints.size();
for (int i = 0; i < count; i++) {
Fingerprint fp = fingerprints.get(i);
serializer.startTag(null, TAG_FINGERPRINT);
serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getFingerId()));
serializer.attribute(null, ATTR_NAME, fp.getName().toString());
serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
serializer.endTag(null, TAG_FINGERPRINT);
}
serializer.endTag(null, TAG_FINGERPRINTS);
serializer.endDocument();
destination.finishWrite(out);
// Any error while writing is fatal.
} catch (Throwable t) {
Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
destination.failWrite(out);
throw new IllegalStateException("Failed to write fingerprints", t);
} finally {
IoUtils.closeQuietly(out);
}
}
private void readStateSyncLocked() {
FileInputStream in;
if (!mFile.exists()) {
return;
}
try {
in = new FileInputStream(mFile);
} catch (FileNotFoundException fnfe) {
Slog.i(TAG, "No fingerprint state");
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parseStateLocked(parser);
} catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing settings file: "
+ mFile , e);
} finally {
IoUtils.closeQuietly(in);
}
}
private void parseStateLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals(TAG_FINGERPRINTS)) {
parseFingerprintsLocked(parser);
}
}
}
private void parseFingerprintsLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals(TAG_FINGERPRINT)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId),
Integer.parseInt(fingerId), Integer.parseInt(deviceId)));
}
}
}
}
FingerprintUtils.java
/**
* Copyright (C) 2015 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.fingerprint;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.os.Vibrator;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import java.util.List;
/**
* Utility class for dealing with fingerprints and fingerprint settings.
*/
public class FingerprintUtils {
private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
private static final Object sInstanceLock = new Object();
private static FingerprintUtils sInstance;
@GuardedBy("this")
private final SparseArray<FingerprintsUserState> mUsers = new SparseArray<>();
public static FingerprintUtils getInstance() {
synchronized (sInstanceLock) {
if (sInstance == null) {
sInstance = new FingerprintUtils();
}
}
return sInstance;
}
private FingerprintUtils() {
}
public List<Fingerprint> getFingerprintsForUser(Context ctx, int userId) {
return getStateForUser(ctx, userId).getFingerprints();
}
public void addFingerprintForUser(Context ctx, int fingerId, int userId) {
getStateForUser(ctx, userId).addFingerprint(fingerId, userId);
}
public void removeFingerprintIdForUser(Context ctx, int fingerId, int userId) {
getStateForUser(ctx, userId).removeFingerprint(fingerId);
}
public void renameFingerprintForUser(Context ctx, int fingerId, int userId, CharSequence name) {
getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
}
public static void vibrateFingerprintError(Context context) {
Vibrator vibrator = context.getSystemService(Vibrator.class);
if (vibrator != null) {
vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
}
}
public static void vibrateFingerprintSuccess(Context context) {
Vibrator vibrator = context.getSystemService(Vibrator.class);
if (vibrator != null) {
vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
}
}
private FingerprintsUserState getStateForUser(Context ctx, int userId) {
synchronized (this) {
FingerprintsUserState state = mUsers.get(userId);
if (state == null) {
state = new FingerprintsUserState(ctx, userId);
mUsers.put(userId, state);
}
return state;
}
}
}
/frameworks/base/core/java/android/hardware/fingerprint/
-------------------------------------------------------------------------------------------
Fingerprint.aidl
/*
* Copyright (C) 2015 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 android.hardware.fingerprint;
// @hide
parcelable Fingerprint;
Fingerprint.java
/*
* Copyright (C) 2015 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 android.hardware.fingerprint;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Container for fingerprint metadata.
* @hide
*/
public final class Fingerprint implements Parcelable {
private CharSequence mName;
private int mGroupId;
private int mFingerId;
private long mDeviceId; // physical device this is associated with
public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
mName = name;
mGroupId = groupId;
mFingerId = fingerId;
mDeviceId = deviceId;
}
private Fingerprint(Parcel in) {
mName = in.readString();
mGroupId = in.readInt();
mFingerId = in.readInt();
mDeviceId = in.readLong();
}
/**
* Gets the human-readable name for the given fingerprint.
* @return name given to finger
*/
public CharSequence getName() { return mName; }
/**
* Gets the device-specific finger id. Used by Settings to map a name to a specific
* fingerprint template.
* @return device-specific id for this finger
* @hide
*/
public int getFingerId() { return mFingerId; }
/**
* Gets the group id specified when the fingerprint was enrolled.
* @return group id for the set of fingerprints this one belongs to.
* @hide
*/
public int getGroupId() { return mGroupId; }
/**
* Device this fingerprint belongs to.
* @hide
*/
public long getDeviceId() { return mDeviceId; }
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(mName.toString());
out.writeInt(mGroupId);
out.writeInt(mFingerId);
out.writeLong(mDeviceId);
}
public static final Parcelable.Creator<Fingerprint> CREATOR
= new Parcelable.Creator<Fingerprint>() {
public Fingerprint createFromParcel(Parcel in) {
return new Fingerprint(in);
}
public Fingerprint[] newArray(int size) {
return new Fingerprint[size];
}
};
};
FingerprintManager.java
/**
* Copyright (C) 2014 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 android.hardware.fingerprint;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManagerNative;
import android.content.Context;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.keystore.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
import java.security.Signature;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
/**
* A class that coordinates access to the fingerprint hardware.
* <p>
* Use {@link android.content.Context#getSystemService(java.lang.String)}
* with argument {@link android.content.Context#FINGERPRINT_SERVICE} to get
* an instance of this class.
*/
public class FingerprintManager {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
private static final int MSG_ACQUIRED = 101;
private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
private static final int MSG_AUTHENTICATION_FAILED = 103;
private static final int MSG_ERROR = 104;
private static final int MSG_REMOVED = 105;
//
// Error messages from fingerprint hardware during initilization, enrollment, authentication or
// removal. Must agree with the list in fingerprint.h
//
/**
* The hardware is unavailable. Try again later.
*/
public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
/**
* Error state returned when the sensor was unable to process the current image.
*/
public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
/**
* Error state returned when the current request has been running too long. This is intended to
* prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
* platform and sensor-specific, but is generally on the order of 30 seconds.
*/
public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
/**
* Error state returned for operations like enrollment; the operation cannot be completed
* because there's not enough storage remaining to complete the operation.
*/
public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
/**
* The operation was canceled because the fingerprint sensor is unavailable. For example,
* this may happen when the user is switched, the device is locked or another pending operation
* prevents or disables it.
*/
public static final int FINGERPRINT_ERROR_CANCELED = 5;
/**
* The {@link FingerprintManager#remove(Fingerprint, RemovalCallback)} call failed. Typically
* this will happen when the provided fingerprint id was incorrect.
*
* @hide
*/
public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
/**
* The operation was canceled because the API is locked out due to too many attempts.
*/
public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
* the above categories. Vendors are responsible for providing error strings for these errors.
* @hide
*/
public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
//
// Image acquisition messages. Must agree with those in fingerprint.h
//
/**
* The image acquired was good.
*/
public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
/**
* Only a partial fingerprint image was detected. During enrollment, the user should be
* informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
*/
public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
/**
* The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
* a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
*/
public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
/**
* The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
* For example, it's reasonable return this after multiple
* {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
* (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
* when this is returned.
*/
public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
/**
* The fingerprint image was unreadable due to lack of motion. This is most appropriate for
* linear array sensors that require a swipe motion.
*/
public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
/**
* The fingerprint image was incomplete due to quick motion. While mostly appropriate for
* linear array sensors, this could also happen if the finger was moved during acquisition.
* The user should be asked to move the finger slower (linear) or leave the finger on the sensor
* longer.
*/
public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
* the above categories. Vendors are responsible for providing error strings for these errors.
* @hide
*/
public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
private class OnEnrollCancelListener implements OnCancelListener {
@Override
public void onCancel() {
cancelEnrollment();
}
}
private class OnAuthenticationCancelListener implements OnCancelListener {
private CryptoObject mCrypto;
public OnAuthenticationCancelListener(CryptoObject crypto) {
mCrypto = crypto;
}
@Override
public void onCancel() {
cancelAuthentication(mCrypto);
}
}
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
public static final class CryptoObject {
public CryptoObject(@NonNull Signature signature) {
mCrypto = signature;
}
public CryptoObject(@NonNull Cipher cipher) {
mCrypto = cipher;
}
public CryptoObject(@NonNull Mac mac) {
mCrypto = mac;
}
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
public Signature getSignature() {
return mCrypto instanceof Signature ? (Signature) mCrypto : null;
}
/**
* Get {@link Cipher} object.
* @return {@link Cipher} object or null if this doesn't contain one.
*/
public Cipher getCipher() {
return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
}
/**
* Get {@link Mac} object.
* @return {@link Mac} object or null if this doesn't contain one.
*/
public Mac getMac() {
return mCrypto instanceof Mac ? (Mac) mCrypto : null;
}
/**
* @hide
* @return the opId associated with this object or 0 if none
*/
public long getOpId() {
return mCrypto != null ?
AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
}
private final Object mCrypto;
};
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}.
*/
public static class AuthenticationResult {
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
/**
* Authentication result
*
* @param crypto the crypto object
* @param fingerprint the recognized fingerprint data, if allowed.
* @hide
*/
public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
mCryptoObject = crypto;
mFingerprint = fingerprint;
}
/**
* Obtain the crypto object associated with this transaction
* @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}.
*/
public CryptoObject getCryptoObject() { return mCryptoObject; }
/**
* Obtain the Fingerprint associated with this operation. Applications are strongly
* discouraged from associating specific fingers with specific applications or operations.
*
* @hide
*/
public Fingerprint getFingerprint() { return mFingerprint; }
};
/**
* Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
* FingerprintManager#authenticate(CryptoObject, CancellationSignal,
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
*/
public static abstract class AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errorCode An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
* Called when a recoverable error has been encountered during authentication. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it."
* @param helpCode An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
* Called when a fingerprint is recognized.
* @param result An object containing authentication-related data
*/
public void onAuthenticationSucceeded(AuthenticationResult result) { }
/**
* Called when a fingerprint is valid but not recognized.
*/
public void onAuthenticationFailed() { }
/**
* Called when a fingerprint image has been acquired, but wasn't processed yet.
*
* @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
* @hide
*/
public void onAuthenticationAcquired(int acquireInfo) {}
};
/**
* Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
* CancellationSignal, int). Users of {@link #FingerprintManager()}
* must provide an implementation of this to {@link FingerprintManager#enroll(long,
* CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events.
*
* @hide
*/
public static abstract class EnrollmentCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
* @param errMsgId An integer identifying the error message
* @param errString A human-readable error string that can be shown in UI
*/
public void onEnrollmentError(int errMsgId, CharSequence errString) { }
/**
* Called when a recoverable error has been encountered during enrollment. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it" or what they need to do next, such as
* "Touch sensor again."
* @param helpMsgId An integer identifying the error message
* @param helpString A human-readable string that can be shown in UI
*/
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { }
/**
* Called as each enrollment step progresses. Enrollment is considered complete when
* remaining reaches 0. This function will not be called if enrollment fails. See
* {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
* @param remaining The number of remaining steps
*/
public void onEnrollmentProgress(int remaining) { }
};
/**
* Callback structure provided to {@link FingerprintManager#remove(int). Users of
* {@link #FingerprintManager()} may optionally provide an implementation of this to
* {@link FingerprintManager#remove(int, int, RemovalCallback)} for listening to
* fingerprint template removal events.
*
* @hide
*/
public static abstract class RemovalCallback {
/**
* Called when the given fingerprint can't be removed.
* @param fp The fingerprint that the call attempted to remove
* @param errMsgId An associated error message id
* @param errString An error message indicating why the fingerprint id can't be removed
*/
public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) { }
/**
* Called when a given fingerprint is successfully removed.
* @param fingerprint the fingerprint template that was removed.
*/
public void onRemovalSucceeded(Fingerprint fingerprint) { }
};
/**
* Request authentication of a crypto object. This call warms up the fingerprint hardware
* and starts scanning for a fingerprint. It terminates when
* {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
* {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
*
* @param crypto object associated with the call or null if none required.
* @param cancel an object that can be used to cancel authentication
* @param flags optional flags; should be 0
* @param callback an object to receive authentication events
* @param handler an optional handler to handle callback events
*
* @throws IllegalArgumentException if the crypto operation is not supported or is not backed
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
*/
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
}
/**
* Use the provided handler thread for events.
* @param handler
*/
private void useHandler(Handler handler) {
if (handler != null) {
mHandler = new MyHandler(handler.getLooper());
} else if (mHandler.getLooper() != mContext.getMainLooper()){
mHandler = new MyHandler(mContext.getMainLooper());
}
}
/**
* Per-user version
* @hide
*/
@RequiresPermission(USE_FINGERPRINT)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Log.w(TAG, "authentication already canceled");
return;
} else {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
}
}
if (mService != null) try {
useHandler(handler);
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
}
}
}
/**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
* {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
* {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
* @param token a unique token provided by a recent creation or verification of device
* credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
* @param flags optional flags
* @param callback an object to receive enrollment events
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] token, CancellationSignal cancel, int flags,
EnrollmentCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
if (cancel != null) {
if (cancel.isCanceled()) {
Log.w(TAG, "enrollment already canceled");
return;
} else {
cancel.setOnCancelListener(new OnEnrollCancelListener());
}
}
if (mService != null) try {
mEnrollmentCallback = callback;
mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
// Though this may not be a hardware issue, it will cause apps to give up or try
// again later.
callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
}
}
}
/**
* Requests a pre-enrollment auth token to tie enrollment to the confirmation of
* existing device credentials (e.g. pin/pattern/password).
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public long preEnroll() {
long result = 0;
if (mService != null) try {
result = mService.preEnroll(mToken);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
}
return result;
}
/**
* Finishes enrollment and cancels the current auth token.
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public int postEnroll() {
int result = 0;
if (mService != null) try {
result = mService.postEnroll(mToken);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in post enroll: ", e);
}
return result;
}
/**
* Remove given fingerprint template from fingerprint hardware and/or protected storage.
* @param fp the fingerprint item to remove
* @param callback an optional callback to verify that fingerprint templates have been
* successfully removed. May be null of no callback is required.
*
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void remove(Fingerprint fp, RemovalCallback callback) {
if (mService != null) try {
mRemovalCallback = callback;
mRemovalFingerprint = fp;
mService.remove(mToken, fp.getFingerId(), getCurrentUserId(), mServiceReceiver);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
}
}
}
/**
* Renames the given fingerprint template
* @param fpId the fingerprint id
* @param newName the new name
*
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void rename(int fpId, String newName) {
// Renames the given fpId
if (mService != null) {
try {
mService.rename(fpId, getCurrentUserId(), newName);
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in rename(): ", e);
}
} else {
Log.w(TAG, "rename(): Service not connected!");
}
}
/**
* Obtain the list of enrolled fingerprints templates.
* @return list of current fingerprint items
*
* @hide
*/
@RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints(int userId) {
if (mService != null) try {
return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
}
return null;
}
/**
* Obtain the list of enrolled fingerprints templates.
* @return list of current fingerprint items
*
* @hide
*/
@RequiresPermission(USE_FINGERPRINT)
public List<Fingerprint> getEnrolledFingerprints() {
return getEnrolledFingerprints(UserHandle.myUserId());
}
/**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
*/
@RequiresPermission(USE_FINGERPRINT)
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
return mService.hasEnrolledFingerprints(UserHandle.myUserId(),
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
}
return false;
}
/**
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
*/
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
if (mService != null) {
try {
long deviceId = 0; /* TODO: plumb hardware id to FPMS */
return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
}
} else {
Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
}
return false;
}
/**
* Retrieves the authenticator token for binding keys to the lifecycle
* of the current set of fingerprints. Used only by internal clients.
*
* @hide
*/
public long getAuthenticatorId() {
if (mService != null) {
try {
return mService.getAuthenticatorId(mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
}
} else {
Log.w(TAG, "getAuthenticatorId(): Service not connected!");
}
return 0;
}
private class MyHandler extends Handler {
private MyHandler(Context context) {
super(context.getMainLooper());
}
private MyHandler(Looper looper) {
super(looper);
}
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
case MSG_ENROLL_RESULT:
sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
case MSG_ACQUIRED:
sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
sendAuthenticatedSucceeded((Fingerprint) msg.obj);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
break;
case MSG_ERROR:
sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
break;
case MSG_REMOVED:
sendRemovedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
msg.arg2 /* groupId */);
}
}
private void sendRemovedResult(long deviceId, int fingerId, int groupId) {
if (mRemovalCallback != null) {
int reqFingerId = mRemovalFingerprint.getFingerId();
int reqGroupId = mRemovalFingerprint.getGroupId();
if (fingerId != reqFingerId) {
Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
}
if (fingerId != reqFingerId) {
Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
}
mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint);
}
}
private void sendErrorResult(long deviceId, int errMsgId) {
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentError(errMsgId, getErrorString(errMsgId));
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(errMsgId, getErrorString(errMsgId));
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFingerprint, errMsgId,
getErrorString(errMsgId));
}
}
private void sendEnrollResult(Fingerprint fp, int remaining) {
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentProgress(remaining);
}
}
private void sendAuthenticatedSucceeded(Fingerprint fp) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
private void sendAuthenticatedFailed() {
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationFailed();
}
}
private void sendAcquiredResult(long deviceId, int acquireInfo) {
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
}
final String msg = getAcquiredString(acquireInfo);
if (msg == null) {
return;
}
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentHelp(acquireInfo, msg);
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationHelp(acquireInfo, msg);
}
}
};
/**
* @hide
*/
public FingerprintManager(Context context, IFingerprintService service) {
mContext = context;
mService = service;
if (mService == null) {
Slog.v(TAG, "FingerprintManagerService was null");
}
mHandler = new MyHandler(context);
}
private int getCurrentUserId() {
try {
return ActivityManagerNative.getDefault().getCurrentUser().id;
} catch (RemoteException e) {
Log.w(TAG, "Failed to get current user id\n");
return UserHandle.USER_NULL;
}
}
private void cancelEnrollment() {
if (mService != null) try {
mService.cancelEnrollment(mToken);
} catch (RemoteException e) {
if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment");
}
}
private void cancelAuthentication(CryptoObject cryptoObject) {
if (mService != null) try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment");
}
}
private String getErrorString(int errMsg) {
switch (errMsg) {
case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_unable_to_process);
case FINGERPRINT_ERROR_HW_UNAVAILABLE:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_hw_not_available);
case FINGERPRINT_ERROR_NO_SPACE:
return mContext.getString(
com.android.internal.R.string.fingerprint_error_no_space);
case FINGERPRINT_ERROR_TIMEOUT:
return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout);
case FINGERPRINT_ERROR_CANCELED:
return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
case FINGERPRINT_ERROR_LOCKOUT:
return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
default:
if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) {
int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE;
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
if (msgNumber < msgArray.length) {
return msgArray[msgNumber];
}
}
return null;
}
}
private String getAcquiredString(int acquireInfo) {
switch (acquireInfo) {
case FINGERPRINT_ACQUIRED_GOOD:
return null;
case FINGERPRINT_ACQUIRED_PARTIAL:
return mContext.getString(
com.android.internal.R.string.fingerprint_acquired_partial);
case FINGERPRINT_ACQUIRED_INSUFFICIENT:
return mContext.getString(
com.android.internal.R.string.fingerprint_acquired_insufficient);
case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
return mContext.getString(
com.android.internal.R.string.fingerprint_acquired_imager_dirty);
case FINGERPRINT_ACQUIRED_TOO_SLOW:
return mContext.getString(
com.android.internal.R.string.fingerprint_acquired_too_slow);
case FINGERPRINT_ACQUIRED_TOO_FAST:
return mContext.getString(
com.android.internal.R.string.fingerprint_acquired_too_fast);
default:
if (acquireInfo >= FINGERPRINT_ACQUIRED_VENDOR_BASE) {
int msgNumber = acquireInfo - FINGERPRINT_ACQUIRED_VENDOR_BASE;
String[] msgArray = mContext.getResources().getStringArray(
com.android.internal.R.array.fingerprint_acquired_vendor);
if (msgNumber < msgArray.length) {
return msgArray[msgNumber];
}
}
return null;
}
}
private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
@Override // binder call
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
@Override // binder call
public void onAcquired(long deviceId, int acquireInfo) {
mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
}
@Override // binder call
public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) {
mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget();
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();;
}
@Override // binder call
public void onError(long deviceId, int error) {
mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
}
@Override // binder call
public void onRemoved(long deviceId, int fingerId, int groupId) {
mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
}
};
}
IFingerprintDaemon.aidl
/*
* Copyright (C) 2015 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 android.hardware.fingerprint;
import android.hardware.fingerprint.IFingerprintDaemonCallback;
/**
* Communication channel from FingerprintService to FingerprintDaemon (fingerprintd)
* @hide
*/
interface IFingerprintDaemon {
int authenticate(long sessionId, int groupId);
int cancelAuthentication();
int enroll(in byte [] token, int groupId, int timeout);
int cancelEnrollment();
long preEnroll();
int remove(int fingerId, int groupId);
long getAuthenticatorId();
int setActiveGroup(int groupId, in byte[] path);
long openHal();
int closeHal();
void init(IFingerprintDaemonCallback callback);
int postEnroll();
}
IFingerprintDaemonCallback.aidl
/*
* Copyright (C) 2014 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 android.hardware.fingerprint;
/**
* Communication channel from the fingerprintd back to FingerprintService.
* @hide
*/
interface IFingerprintDaemonCallback {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo);
void onAuthenticated(long deviceId, int fingerId, int groupId);
void onError(long deviceId, int error);
void onRemoved(long deviceId, int fingerId, int groupId);
void onEnumerate(long deviceId, in int [] fingerIds, in int [] groupIds);
}
IFingerprintService.aidl
/*
* Copyright (C) 2014 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 android.hardware.fingerprint;
import android.os.Bundle;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.Fingerprint;
import java.util.List;
/**
* Communication channel from client to the fingerprint service.
* @hide
*/
interface IFingerprintService {
// Authenticate the given sessionId with a fingerprint
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
int flags);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
// Any errors resulting from this call will be returned to the listener
void remove(IBinder token, int fingerId, int groupId, IFingerprintServiceReceiver receiver);
// Rename the fingerprint specified by fingerId and groupId to the given name
void rename(int fingerId, int groupId, String name);
// Get a list of enrolled fingerprints in the given group.
List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName);
// Determine if HAL is loaded and ready
boolean isHardwareDetected(long deviceId, String opPackageName);
// Get a pre-enrollment authentication token
long preEnroll(IBinder token);
// Finish an enrollment sequence and invalidate the authentication token
int postEnroll(IBinder token);
// Determine if a user has at least one enrolled fingerprint
boolean hasEnrolledFingerprints(int groupId, String opPackageName);
// Gets the number of hardware devices
// int getHardwareDeviceCount();
// Gets the unique device id for hardware enumerated at i
// long getHardwareDevice(int i);
// Gets the authenticator ID for fingerprint
long getAuthenticatorId(String opPackageName);
}
IFingerprintServiceReceiver.aidl
/*
* Copyright (C) 2014 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 android.hardware.fingerprint;
import android.hardware.fingerprint.Fingerprint;
import android.os.Bundle;
import android.os.UserHandle;
/**
* Communication channel from the FingerprintService back to FingerprintManager.
* @hide
*/
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo);
void onAuthenticationSucceeded(long deviceId, in Fingerprint fp);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error);
void onRemoved(long deviceId, int fingerId, int groupId);
}
/system/core/fingerprintd/
-------------------------------------------------------------------------------------------
Android.mk
#
# Copyright (C) 2015 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.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#LOCAL_MULTILIB :=32 #test bruce
LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
LOCAL_SRC_FILES := \
FingerprintDaemonProxy.cpp \
IFingerprintDaemon.cpp \
IFingerprintDaemonCallback.cpp \
fingerprintd.cpp
LOCAL_MODULE := fingerprintd
LOCAL_SHARED_LIBRARIES := \
libbinder \
liblog \
libhardware \
libutils \
libkeystore_binder
include $(BUILD_EXECUTABLE)
fingerprintd.cpp
/*
* Copyright (C) 2015 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.
*/
#define LOG_TAG "fingerprintd"
#include <cutils/log.h>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <utils/String16.h>
#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // for error codes
#include <hardware/hardware.h>
#include <hardware/fingerprint.h>
#include <hardware/hw_auth_token.h>
#include "FingerprintDaemonProxy.h"
int main() {
ALOGI("Starting " LOG_TAG);
android::sp<android::IServiceManager> serviceManager = android::defaultServiceManager();
android::sp<android::FingerprintDaemonProxy> proxy =
android::FingerprintDaemonProxy::getInstance();
android::status_t ret = serviceManager->addService(
android::FingerprintDaemonProxy::descriptor, proxy);
if (ret != android::OK) {
ALOGE("Couldn't register " LOG_TAG " binder service!");
return -1;
}
/*
* We're the only thread in existence, so we're just going to process
* Binder transaction as a single-threaded program.
*/
android::IPCThreadState::self()->joinThreadPool();
ALOGI("Done");
return 0;
}
FingerprintDaemonProxy.cpp
/*
* Copyright (C) 2015 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.
*/
#define LOG_TAG "fingerprintd"
#include <binder/IServiceManager.h>
#include <hardware/hardware.h>
#include <hardware/fingerprint.h>
#include <hardware/hw_auth_token.h>
#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // for error codes
#include <utils/Log.h>
#include "FingerprintDaemonProxy.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace android {
FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;
// Supported fingerprint HAL version
static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);
FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {
}
FingerprintDaemonProxy::~FingerprintDaemonProxy() {
closeHal();
}
void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) {
FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance();
const sp<IFingerprintDaemonCallback> callback = instance->mCallback;
if (callback == NULL) {
ALOGE("Invalid callback object");
return;
}
const int64_t device = (int64_t) instance->mDevice;
switch (msg->type) {
case FINGERPRINT_ERROR:
ALOGD("onError(%d)", msg->data.error);
callback->onError(device, msg->data.error);
break;
case FINGERPRINT_ACQUIRED:
ALOGD("onAcquired(%d)", msg->data.acquired.acquired_info);
callback->onAcquired(device, msg->data.acquired.acquired_info);
break;
case FINGERPRINT_AUTHENTICATED:
ALOGD("onAuthenticated(fid=%d, gid=%d)",
msg->data.authenticated.finger.fid,
msg->data.authenticated.finger.gid);
if (msg->data.authenticated.finger.fid != 0) {
const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat);
instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat));
}
callback->onAuthenticated(device,
msg->data.authenticated.finger.fid,
msg->data.authenticated.finger.gid);
break;
case FINGERPRINT_TEMPLATE_ENROLLING:
ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)",
msg->data.enroll.finger.fid,
msg->data.enroll.finger.gid,
msg->data.enroll.samples_remaining);
callback->onEnrollResult(device,
msg->data.enroll.finger.fid,
msg->data.enroll.finger.gid,
msg->data.enroll.samples_remaining);
break;
case FINGERPRINT_TEMPLATE_REMOVED:
ALOGD("onRemove(fid=%d, gid=%d)",
msg->data.removed.finger.fid,
msg->data.removed.finger.gid);
callback->onRemoved(device,
msg->data.removed.finger.fid,
msg->data.removed.finger.gid);
break;
default:
ALOGE("invalid msg type: %d", msg->type);
return;
}
}
void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) {
if (auth_token != NULL && auth_token_length > 0) {
// TODO: cache service?
sp < IServiceManager > sm = defaultServiceManager();
sp < IBinder > binder = sm->getService(String16("android.security.keystore"));
sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder);
if (service != NULL) {
status_t ret = service->addAuthToken(auth_token, auth_token_length);
if (ret != ResponseCode::NO_ERROR) {
ALOGE("Falure sending auth token to KeyStore: %d", ret);
}
} else {
ALOGE("Unable to communicate with KeyStore");
}
}
}
void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) {
if (mCallback != NULL && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) {
IInterface::asBinder(mCallback)->unlinkToDeath(this);
}
IInterface::asBinder(callback)->linkToDeath(this);
mCallback = callback;
}
int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId,
int32_t timeout) {
ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout);
if (tokenSize != sizeof(hw_auth_token_t) ) {
ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zu\n", tokenSize);
return -1;
}
const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token);
return mDevice->enroll(mDevice, authToken, groupId, timeout);
}
uint64_t FingerprintDaemonProxy::preEnroll() {
return mDevice->pre_enroll(mDevice);
}
int32_t FingerprintDaemonProxy::postEnroll() {
return mDevice->post_enroll(mDevice);
}
int32_t FingerprintDaemonProxy::stopEnrollment() {
ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
return mDevice->cancel(mDevice);
}
int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) {
ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
return mDevice->authenticate(mDevice, sessionId, groupId);
}
int32_t FingerprintDaemonProxy::stopAuthentication() {
ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n");
return mDevice->cancel(mDevice);
}
int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) {
ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId);
return mDevice->remove(mDevice, groupId, fingerId);
}
uint64_t FingerprintDaemonProxy::getAuthenticatorId() {
return mDevice->get_authenticator_id(mDevice);
}
int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path,
ssize_t pathlen) {
if (pathlen >= PATH_MAX || pathlen <= 0) {
ALOGE("Bad path length: %zd", pathlen);
return -1;
}
// Convert to null-terminated string
char path_name[PATH_MAX];
memcpy(path_name, path, pathlen);
path_name[pathlen] = '\0';
ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen);
return mDevice->set_active_group(mDevice, groupId, path_name);
}
//add bruce
/*
typedef struct vendor_fingerprint{
const char * vendor;
const char * dev_node;
const char * lib_name;
}vendor_fingerprint_t;
#define VENDOR_LIST_COUNT 2
vendor_fingerprint_t vendor_list[VENDOR_LIST_COUNT]={
{"sunwave","/dev/sunwave_fp","sunwave.fingerprint"},
{"mas","/dev/madev0","fingerprint"},//mas.fingerprint
};
*/
//end bruce
int64_t FingerprintDaemonProxy::openHal() {
ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
int err;
//add bruce
/*
char *sensorId=NULL;
vendor_fingerprint_t *vendor;
for(int i=0;i<VENDOR_LIST_COUNT;i++){
vendor=&vendor_list[i];
int res =access(vendor->dev_node,F_OK);
if(res==0){
sensorId=(char*)vendor->lib_name;
ALOGE("%s find fingerprint vendor %s",__func__,vendor->vendor);
break;
}
}
if(sensorId==NULL){
ALOGE("Cant't find fingerprint vendor");
return 0;
}
*/
//end bruce
const hw_module_t *hw_module = NULL;
// if (0 != (err = hw_get_module(sensorId, &hw_module))) {//FINGERPRINT_HARDWARE_MODULE_ID test bruce
if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
ALOGE("Can't open fingerprint HW Module, error: %d", err);
return 0;
}
if (NULL == hw_module) {
ALOGE("No valid fingerprint module");
return 0;
}
mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module);
if (mModule->common.methods->open == NULL) {
ALOGE("No valid open method");
return 0;
}
hw_device_t *device = NULL;
if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) {
ALOGE("Can't open fingerprint methods, error: %d", err);
return 0;
}
if (kVersion != device->version) {
ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
// return 0; // FIXME
}
mDevice = reinterpret_cast<fingerprint_device_t*>(device);
err = mDevice->set_notify(mDevice, hal_notify_callback);
if (err < 0) {
ALOGE("Failed in call to set_notify(), err=%d", err);
return 0;
}
// Sanity check - remove
if (mDevice->notify != hal_notify_callback) {
ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, hal_notify_callback);
}
ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
return reinterpret_cast<int64_t>(mDevice); // This is just a handle
}
int32_t FingerprintDaemonProxy::closeHal() {
ALOG(LOG_VERBOSE, LOG_TAG, "nativeCloseHal()\n");
if (mDevice == NULL) {
ALOGE("No valid device");
return -ENOSYS;
}
int err;
if (0 != (err = mDevice->common.close(reinterpret_cast<hw_device_t*>(mDevice)))) {
ALOGE("Can't close fingerprint module, error: %d", err);
return err;
}
mDevice = NULL;
return 0;
}
void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) {
ALOGD("binder died");
int err;
if (0 != (err = closeHal())) {
ALOGE("Can't close fingerprint device, error: %d", err);
}
if (IInterface::asBinder(mCallback) == who) {
mCallback = NULL;
}
}
}
FingerprintDaemonRroxy.h
/*
* Copyright (C) 2015 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.
*/
#ifndef FINGERPRINT_DAEMON_PROXY_H_
#define FINGERPRINT_DAEMON_PROXY_H_
#include "IFingerprintDaemon.h"
#include "IFingerprintDaemonCallback.h"
namespace android {
class FingerprintDaemonProxy : public BnFingerprintDaemon {
public:
static FingerprintDaemonProxy* getInstance() {
if (sInstance == NULL) {
sInstance = new FingerprintDaemonProxy();
}
return sInstance;
}
// These reflect binder methods.
virtual void init(const sp<IFingerprintDaemonCallback>& callback);
virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout);
virtual uint64_t preEnroll();
virtual int32_t postEnroll();
virtual int32_t stopEnrollment();
virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId);
virtual int32_t stopAuthentication();
virtual int32_t remove(int32_t fingerId, int32_t groupId);
virtual uint64_t getAuthenticatorId();
virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen);
virtual int64_t openHal();
virtual int32_t closeHal();
private:
FingerprintDaemonProxy();
virtual ~FingerprintDaemonProxy();
void binderDied(const wp<IBinder>& who);
void notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length);
static void hal_notify_callback(const fingerprint_msg_t *msg);
static FingerprintDaemonProxy* sInstance;
fingerprint_module_t const* mModule;
fingerprint_device_t* mDevice;
sp<IFingerprintDaemonCallback> mCallback;
};
} // namespace android
#endif // FINGERPRINT_DAEMON_PROXY_H_
FingerprintDaemon.cpp
/*
* Copyright 2015, 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.
*/
#include <inttypes.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <utils/String16.h>
#include <utils/Looper.h>
#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // for error code
#include <hardware/hardware.h>
#include <hardware/fingerprint.h>
#include <hardware/hw_auth_token.h>
#include "IFingerprintDaemon.h"
#include "IFingerprintDaemonCallback.h"
namespace android {
static const String16 USE_FINGERPRINT_PERMISSION("android.permission.USE_FINGERPRINT");
static const String16 MANAGE_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT");
static const String16 HAL_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); // TODO
static const String16 DUMP_PERMISSION("android.permission.DUMP");
const android::String16
IFingerprintDaemon::descriptor("android.hardware.fingerprint.IFingerprintDaemon");
const android::String16&
IFingerprintDaemon::getInterfaceDescriptor() const {
return IFingerprintDaemon::descriptor;
}
status_t BnFingerprintDaemon::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
switch(code) {
case AUTHENTICATE: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const uint64_t sessionId = data.readInt64();
const uint32_t groupId = data.readInt32();
const int32_t ret = authenticate(sessionId, groupId);
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
};
case CANCEL_AUTHENTICATION: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int32_t ret = stopAuthentication();
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case ENROLL: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const ssize_t tokenSize = data.readInt32();
const uint8_t* token = static_cast<const uint8_t *>(data.readInplace(tokenSize));
const int32_t groupId = data.readInt32();
const int32_t timeout = data.readInt32();
const int32_t ret = enroll(token, tokenSize, groupId, timeout);
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case CANCEL_ENROLLMENT: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int32_t ret = stopEnrollment();
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case PRE_ENROLL: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const uint64_t ret = preEnroll();
reply->writeNoException();
reply->writeInt64(ret);
return NO_ERROR;
}
case POST_ENROLL: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int32_t ret = postEnroll();
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case REMOVE: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int32_t fingerId = data.readInt32();
const int32_t groupId = data.readInt32();
const int32_t ret = remove(fingerId, groupId);
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case GET_AUTHENTICATOR_ID: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const uint64_t ret = getAuthenticatorId();
reply->writeNoException();
reply->writeInt64(ret);
return NO_ERROR;
}
case SET_ACTIVE_GROUP: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int32_t group = data.readInt32();
const ssize_t pathSize = data.readInt32();
const uint8_t* path = static_cast<const uint8_t *>(data.readInplace(pathSize));
const int32_t ret = setActiveGroup(group, path, pathSize);
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case OPEN_HAL: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int64_t ret = openHal();
reply->writeNoException();
reply->writeInt64(ret);
return NO_ERROR;
}
case CLOSE_HAL: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
const int32_t ret = closeHal();
reply->writeNoException();
reply->writeInt32(ret);
return NO_ERROR;
}
case INIT: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
return PERMISSION_DENIED;
}
sp<IFingerprintDaemonCallback> callback =
interface_cast<IFingerprintDaemonCallback>(data.readStrongBinder());
init(callback);
reply->writeNoException();
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
};
bool BnFingerprintDaemon::checkPermission(const String16& permission) {
const IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
return PermissionCache::checkPermission(permission, calling_pid, calling_uid);
}
}; // namespace android
IFingerprintDaemon.h
/*
* Copyright (C) 2015 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.
*/
#ifndef IFINGERPRINT_DAEMON_H_
#define IFINGERPRINT_DAEMON_H_
#include <binder/IInterface.h>
#include <binder/Parcel.h>
namespace android {
class IFingerprintDaemonCallback;
/*
* Abstract base class for native implementation of FingerprintService.
*
* Note: This must be kept manually in sync with IFingerprintDaemon.aidl
*/
class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient {
public:
enum {
AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0,
CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1,
ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2,
CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3,
PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4,
REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5,
GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6,
SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7,
OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8,
CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
};
IFingerprintDaemon() { }
virtual ~IFingerprintDaemon() { }
virtual const android::String16& getInterfaceDescriptor() const;
// Binder interface methods
virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0;
virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId,
int32_t timeout) = 0;
virtual uint64_t preEnroll() = 0;
virtual int32_t postEnroll() = 0;
virtual int32_t stopEnrollment() = 0;
virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
virtual int32_t stopAuthentication() = 0;
virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0;
virtual uint64_t getAuthenticatorId() = 0;
virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0;
virtual int64_t openHal() = 0;
virtual int32_t closeHal() = 0;
// DECLARE_META_INTERFACE - C++ client interface not needed
static const android::String16 descriptor;
static void hal_notify_callback(const fingerprint_msg_t *msg);
};
// ----------------------------------------------------------------------------
class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> {
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags = 0);
private:
bool checkPermission(const String16& permission);
};
} // namespace android
#endif // IFINGERPRINT_DAEMON_H_
IFingerprintDaemonCallback.cpp
/*
* Copyright (C) 2015 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.
*/
#define LOG_TAG "IFingerprintDaemonCallback"
#include <stdint.h>
#include <sys/types.h>
#include <utils/Log.h>
#include <binder/Parcel.h>
#include "IFingerprintDaemonCallback.h"
namespace android {
class BpFingerprintDaemonCallback : public BpInterface<IFingerprintDaemonCallback>
{
public:
BpFingerprintDaemonCallback(const sp<IBinder>& impl) :
BpInterface<IFingerprintDaemonCallback>(impl) {
}
virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
data.writeInt32(fpId);
data.writeInt32(gpId);
data.writeInt32(rem);
return remote()->transact(ON_ENROLL_RESULT, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
data.writeInt32(acquiredInfo);
return remote()->transact(ON_ACQUIRED, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual status_t onAuthenticated(int64_t devId, int32_t fpId, int32_t gpId) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
data.writeInt32(fpId);
data.writeInt32(gpId);
return remote()->transact(ON_AUTHENTICATED, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual status_t onError(int64_t devId, int32_t error) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
data.writeInt32(error);
return remote()->transact(ON_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual status_t onRemoved(int64_t devId, int32_t fpId, int32_t gpId) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
data.writeInt32(fpId);
data.writeInt32(gpId);
return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds,
int32_t sz) {
Parcel data, reply;
data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
data.writeInt64(devId);
data.writeInt32Array(sz, fpIds);
data.writeInt32Array(sz, gpIds);
return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY);
}
};
IMPLEMENT_META_INTERFACE(FingerprintDaemonCallback,
"android.hardware.fingerprint.IFingerprintDaemonCallback");
}; // namespace android
IFingerprintDaemonCallback.h
/*
* Copyright (C) 2015 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.
*/
#ifndef IFINGERPRINT_DAEMON_CALLBACK_H_
#define IFINGERPRINT_DAEMON_CALLBACK_H_
#include <inttypes.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
namespace android {
/*
* Communication channel back to FingerprintService.java
*/
class IFingerprintDaemonCallback : public IInterface {
public:
// must be kept in sync with IFingerprintService.aidl
enum {
ON_ENROLL_RESULT = IBinder::FIRST_CALL_TRANSACTION + 0,
ON_ACQUIRED = IBinder::FIRST_CALL_TRANSACTION + 1,
ON_AUTHENTICATED = IBinder::FIRST_CALL_TRANSACTION + 2,
ON_ERROR = IBinder::FIRST_CALL_TRANSACTION + 3,
ON_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 4,
ON_ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 5,
};
virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) = 0;
virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) = 0;
virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
virtual status_t onError(int64_t devId, int32_t error) = 0;
virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
virtual status_t onEnumerate(int64_t devId, const int32_t* fpIds, const int32_t* gpIds,
int32_t sz) = 0;
DECLARE_META_INTERFACE(FingerprintDaemonCallback);
};
}; // namespace android
#endif // IFINGERPRINT_DAEMON_CALLBACK_H_
/hardware/libhardware/include/hardware/fingerprint.h
-------------------------------------------------------------------------------------------
fingerprint.h
/*
* Copyright (C) 2014 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.
*/
#ifndef ANDROID_INCLUDE_HARDWARE_FINGERPRINT_H
#define ANDROID_INCLUDE_HARDWARE_FINGERPRINT_H
#include <hardware/hw_auth_token.h>
#define FINGERPRINT_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0)
#define FINGERPRINT_MODULE_API_VERSION_2_0 HARDWARE_MODULE_API_VERSION(2, 0)
#define FINGERPRINT_HARDWARE_MODULE_ID "fingerprint"
typedef enum fingerprint_msg_type {
FINGERPRINT_ERROR = -1,
FINGERPRINT_ACQUIRED = 1,
FINGERPRINT_TEMPLATE_ENROLLING = 3,
FINGERPRINT_TEMPLATE_REMOVED = 4,
FINGERPRINT_AUTHENTICATED = 5
} fingerprint_msg_type_t;
/*
* Fingerprint errors are meant to tell the framework to terminate the current operation and ask
* for the user to correct the situation. These will almost always result in messaging and user
* interaction to correct the problem.
*
* For example, FINGERPRINT_ERROR_CANCELED should follow any acquisition message that results in
* a situation where the current operation can't continue without user interaction. For example,
* if the sensor is dirty during enrollment and no further enrollment progress can be made,
* send FINGERPRINT_ACQUIRED_IMAGER_DIRTY followed by FINGERPRINT_ERROR_CANCELED.
*/
typedef enum fingerprint_error {
FINGERPRINT_ERROR_HW_UNAVAILABLE = 1, /* The hardware has an error that can't be resolved. */
FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2, /* Bad data; operation can't continue */
FINGERPRINT_ERROR_TIMEOUT = 3, /* The operation has timed out waiting for user input. */
FINGERPRINT_ERROR_NO_SPACE = 4, /* No space available to store a template */
FINGERPRINT_ERROR_CANCELED = 5, /* The current operation can't proceed. See above. */
FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6, /* fingerprint with given id can't be removed */
FINGERPRINT_ERROR_VENDOR_BASE = 1000 /* vendor-specific error messages start here */
} fingerprint_error_t;
/*
* Fingerprint acquisition info is meant as feedback for the current operation. Anything but
* FINGERPRINT_ACQUIRED_GOOD will be shown to the user as feedback on how to take action on the
* current operation. For example, FINGERPRINT_ACQUIRED_IMAGER_DIRTY can be used to tell the user
* to clean the sensor. If this will cause the current operation to fail, an additional
* FINGERPRINT_ERROR_CANCELED can be sent to stop the operation in progress (e.g. enrollment).
* In general, these messages will result in a "Try again" message.
*/
typedef enum fingerprint_acquired_info {
FINGERPRINT_ACQUIRED_GOOD = 0,
FINGERPRINT_ACQUIRED_PARTIAL = 1, /* sensor needs more data, i.e. longer swipe. */
FINGERPRINT_ACQUIRED_INSUFFICIENT = 2, /* image doesn't contain enough detail for recognition*/
FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3, /* sensor needs to be cleaned */
FINGERPRINT_ACQUIRED_TOO_SLOW = 4, /* mostly swipe-type sensors; not enough data collected */
FINGERPRINT_ACQUIRED_TOO_FAST = 5, /* for swipe and area sensors; tell user to slow down*/
FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000 /* vendor-specific acquisition messages start here */
} fingerprint_acquired_info_t;
typedef struct fingerprint_finger_id {
uint32_t gid;
uint32_t fid;
} fingerprint_finger_id_t;
typedef struct fingerprint_enroll {
fingerprint_finger_id_t finger;
/* samples_remaining goes from N (no data collected, but N scans needed)
* to 0 (no more data is needed to build a template). */
uint32_t samples_remaining;
uint64_t msg; /* Vendor specific message. Used for user guidance */
} fingerprint_enroll_t;
typedef struct fingerprint_removed {
fingerprint_finger_id_t finger;
} fingerprint_removed_t;
typedef struct fingerprint_acquired {
fingerprint_acquired_info_t acquired_info; /* information about the image */
} fingerprint_acquired_t;
typedef struct fingerprint_authenticated {
fingerprint_finger_id_t finger;
hw_auth_token_t hat;
} fingerprint_authenticated_t;
typedef struct fingerprint_msg {
fingerprint_msg_type_t type;
union {
fingerprint_error_t error;
fingerprint_enroll_t enroll;
fingerprint_removed_t removed;
fingerprint_acquired_t acquired;
fingerprint_authenticated_t authenticated;
} data;
} fingerprint_msg_t;
/* Callback function type */
typedef void (*fingerprint_notify_t)(const fingerprint_msg_t *msg);
/* Synchronous operation */
typedef struct fingerprint_device {
/**
* Common methods of the fingerprint device. This *must* be the first member
* of fingerprint_device as users of this structure will cast a hw_device_t
* to fingerprint_device pointer in contexts where it's known
* the hw_device_t references a fingerprint_device.
*/
struct hw_device_t common;
/*
* Client provided callback function to receive notifications.
* Do not set by hand, use the function above instead.
*/
fingerprint_notify_t notify;
/*
* Set notification callback:
* Registers a user function that would receive notifications from the HAL
* The call will block if the HAL state machine is in busy state until HAL
* leaves the busy state.
*
* Function return: 0 if callback function is successfuly registered
* or a negative number in case of error, generally from the errno.h set.
*/
int (*set_notify)(struct fingerprint_device *dev, fingerprint_notify_t notify);
/*
* Fingerprint pre-enroll enroll request:
* Generates a unique token to upper layers to indicate the start of an enrollment transaction.
* This token will be wrapped by security for verification and passed to enroll() for
* verification before enrollment will be allowed. This is to ensure adding a new fingerprint
* template was preceded by some kind of credential confirmation (e.g. device password).
*
* Function return: 0 if function failed
* otherwise, a uint64_t of token
*/
uint64_t (*pre_enroll)(struct fingerprint_device *dev);
/*
* Fingerprint enroll request:
* Switches the HAL state machine to collect and store a new fingerprint
* template. Switches back as soon as enroll is complete
* (fingerprint_msg.type == FINGERPRINT_TEMPLATE_ENROLLING &&
* fingerprint_msg.data.enroll.samples_remaining == 0)
* or after timeout_sec seconds.
* The fingerprint template will be assigned to the group gid. User has a choice
* to supply the gid or set it to 0 in which case a unique group id will be generated.
*
* Function return: 0 if enrollment process can be successfully started
* or a negative number in case of error, generally from the errno.h set.
* A notify() function may be called indicating the error condition.
*/
int (*enroll)(struct fingerprint_device *dev, const hw_auth_token_t *hat,
uint32_t gid, uint32_t timeout_sec);
/*
* Finishes the enroll operation and invalidates the pre_enroll() generated challenge.
* This will be called at the end of a multi-finger enrollment session to indicate
* that no more fingers will be added.
*
* Function return: 0 if the request is accepted
* or a negative number in case of error, generally from the errno.h set.
*/
int (*post_enroll)(struct fingerprint_device *dev);
/*
* get_authenticator_id:
* Returns a token associated with the current fingerprint set. This value will
* change whenever a new fingerprint is enrolled, thus creating a new fingerprint
* set.
*
* Function return: current authenticator id or 0 if function failed.
*/
uint64_t (*get_authenticator_id)(struct fingerprint_device *dev);
/*
* Cancel pending enroll or authenticate, sending FINGERPRINT_ERROR_CANCELED
* to all running clients. Switches the HAL state machine back to the idle state.
* Unlike enroll_done() doesn't invalidate the pre_enroll() challenge.
*
* Function return: 0 if cancel request is accepted
* or a negative number in case of error, generally from the errno.h set.
*/
int (*cancel)(struct fingerprint_device *dev);
/*
* Enumerate all the fingerprint templates found in the directory set by
* set_active_group()
* This is a synchronous call. The function takes:
* - A pointer to an array of fingerprint_finger_id_t.
* - The size of the array provided, in fingerprint_finger_id_t elements.
* Max_size is a bi-directional parameter and returns the actual number
* of elements copied to the caller supplied array.
* In the absence of errors the function returns the total number of templates
* in the user directory.
* If the caller has no good guess on the size of the array he should call this
* function witn *max_size == 0 and use the return value for the array allocation.
* The caller of this function has a complete list of the templates when *max_size
* is the same as the function return.
*
* Function return: Total number of fingerprint templates in the current storage directory.
* or a negative number in case of error, generally from the errno.h set.
*/
int (*enumerate)(struct fingerprint_device *dev, fingerprint_finger_id_t *results,
uint32_t *max_size);
/*
* Fingerprint remove request:
* Deletes a fingerprint template.
* Works only within a path set by set_active_group().
* notify() will be called with details on the template deleted.
* fingerprint_msg.type == FINGERPRINT_TEMPLATE_REMOVED and
* fingerprint_msg.data.removed.id indicating the template id removed.
*
* Function return: 0 if fingerprint template(s) can be successfully deleted
* or a negative number in case of error, generally from the errno.h set.
*/
int (*remove)(struct fingerprint_device *dev, uint32_t gid, uint32_t fid);
/*
* Restricts the HAL operation to a set of fingerprints belonging to a
* group provided.
* The caller must provide a path to a storage location within the user's
* data directory.
*
* Function return: 0 on success
* or a negative number in case of error, generally from the errno.h set.
*/
int (*set_active_group)(struct fingerprint_device *dev, uint32_t gid,
const char *store_path);
/*
* Authenticates an operation identifed by operation_id
*
* Function return: 0 on success
* or a negative number in case of error, generally from the errno.h set.
*/
int (*authenticate)(struct fingerprint_device *dev, uint64_t operation_id, uint32_t gid);
/* Reserved for backward binary compatibility */
void *reserved[4];
} fingerprint_device_t;
typedef struct fingerprint_module {
/**
* Common methods of the fingerprint module. This *must* be the first member
* of fingerprint_module as users of this structure will cast a hw_module_t
* to fingerprint_module pointer in contexts where it's known
* the hw_module_t references a fingerprint_module.
*/
struct hw_module_t common;
} fingerprint_module_t;
#endif /* ANDROID_INCLUDE_HARDWARE_FINGERPRINT_H */
/hardware/libhardware/modules/fingerprint/fingerprint.c
-------------------------------------------------------------------------------------------
/*
* Copyright (C) 2014 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.
*/
#define LOG_TAG "FingerprintHal"
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <cutils/log.h>
#include <hardware/hardware.h>
#include <hardware/fingerprint.h>
static int fingerprint_close(hw_device_t *dev)
{
if (dev) {
free(dev);
return 0;
} else {
return -1;
}
}
static uint64_t fingerprint_pre_enroll(struct fingerprint_device __unused *dev) {
return FINGERPRINT_ERROR;
}
static int fingerprint_enroll(struct fingerprint_device __unused *dev,
const hw_auth_token_t __unused *hat,
uint32_t __unused gid,
uint32_t __unused timeout_sec) {
return FINGERPRINT_ERROR;
}
static uint64_t fingerprint_get_auth_id(struct fingerprint_device __unused *dev) {
return FINGERPRINT_ERROR;
}
static int fingerprint_cancel(struct fingerprint_device __unused *dev) {
return FINGERPRINT_ERROR;
}
static int fingerprint_remove(struct fingerprint_device __unused *dev,
uint32_t __unused gid, uint32_t __unused fid) {
return FINGERPRINT_ERROR;
}
static int fingerprint_set_active_group(struct fingerprint_device __unused *dev,
uint32_t __unused gid, const char __unused *store_path) {
return FINGERPRINT_ERROR;
}
static int fingerprint_authenticate(struct fingerprint_device __unused *dev,
uint64_t __unused operation_id, __unused uint32_t gid) {
return FINGERPRINT_ERROR;
}
static int set_notify_callback(struct fingerprint_device *dev,
fingerprint_notify_t notify) {
/* Decorate with locks */
dev->notify = notify;
return FINGERPRINT_ERROR;
}
static int fingerprint_open(const hw_module_t* module, const char __unused *id,
hw_device_t** device)
{
if (device == NULL) {
ALOGE("NULL device on open");
return -EINVAL;
}
fingerprint_device_t *dev = malloc(sizeof(fingerprint_device_t));
memset(dev, 0, sizeof(fingerprint_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = FINGERPRINT_MODULE_API_VERSION_2_0;
dev->common.module = (struct hw_module_t*) module;
dev->common.close = fingerprint_close;
dev->pre_enroll = fingerprint_pre_enroll;
dev->enroll = fingerprint_enroll;
dev->get_authenticator_id = fingerprint_get_auth_id;
dev->cancel = fingerprint_cancel;
dev->remove = fingerprint_remove;
dev->set_active_group = fingerprint_set_active_group;
dev->authenticate = fingerprint_authenticate;
dev->set_notify = set_notify_callback;
dev->notify = NULL;
*device = (hw_device_t*) dev;
return 0;
}
static struct hw_module_methods_t fingerprint_module_methods = {
.open = fingerprint_open,
};
fingerprint_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = FINGERPRINT_MODULE_API_VERSION_2_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = FINGERPRINT_HARDWARE_MODULE_ID,
.name = "Demo Fingerprint HAL",
.author = "The Android Open Source Project",
.methods = &fingerprint_module_methods,
},
};