Merge "Implement enumerate in FingerprintService" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
c2f8949b32
@@ -58,7 +58,7 @@ public abstract class EnumerateClient extends ClientMonitor {
|
||||
public int stop(boolean initiatedByClient) {
|
||||
IBiometricsFingerprint daemon = getFingerprintDaemon();
|
||||
if (daemon == null) {
|
||||
Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
|
||||
Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
|
||||
return ERROR_ESRCH;
|
||||
}
|
||||
try {
|
||||
@@ -102,12 +102,12 @@ public abstract class EnumerateClient extends ClientMonitor {
|
||||
@Override
|
||||
public boolean onEnrollResult(int fingerId, int groupId, int rem) {
|
||||
if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
|
||||
return true; // Invalid for Remove
|
||||
return true; // Invalid for Enumerate.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRemoved(int fingerId, int groupId, int remaining) {
|
||||
if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
|
||||
return true; // Invalid for Authenticate
|
||||
return true; // Invalid for Enumerate.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* A service to manage multiple clients that want to access the fingerprint HAL API.
|
||||
@@ -96,6 +97,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient {
|
||||
static final String TAG = "FingerprintService";
|
||||
static final boolean DEBUG = true;
|
||||
private static final boolean CLEANUP_UNUSED_FP = false;
|
||||
private static final String FP_DATA_DIR = "fpdata";
|
||||
private static final int MSG_USER_SWITCHING = 10;
|
||||
private static final String ACTION_LOCKOUT_RESET =
|
||||
@@ -134,6 +136,20 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
private ClientMonitor mPendingClient;
|
||||
private PerformanceStats mPerformanceStats;
|
||||
|
||||
|
||||
private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
|
||||
private LinkedList<Integer> mEnumeratingUserIds = new LinkedList<>();
|
||||
private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw finterprints
|
||||
|
||||
private class UserFingerprint {
|
||||
Fingerprint f;
|
||||
int userId;
|
||||
public UserFingerprint(Fingerprint f, int userId) {
|
||||
this.f = f;
|
||||
this.userId = userId;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal fingerprint authentications are tracked by mPerformanceMap.
|
||||
private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
|
||||
|
||||
@@ -185,6 +201,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
+ (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
|
||||
+ " failed to respond to cancel, starting client "
|
||||
+ (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
|
||||
|
||||
mCurrentClient = null;
|
||||
startClient(mPendingClient, false);
|
||||
}
|
||||
@@ -239,6 +256,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
if (mHalDeviceId != 0) {
|
||||
loadAuthenticatorIds();
|
||||
updateActiveGroup(ActivityManager.getCurrentUser(), null);
|
||||
doFingerprintCleanup(ActivityManager.getCurrentUser());
|
||||
} else {
|
||||
Slog.w(TAG, "Failed to open Fingerprint HAL!");
|
||||
MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
|
||||
@@ -253,7 +271,6 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
// This operation can be expensive, so keep track of the elapsed time. Might need to move to
|
||||
// background if it takes too long.
|
||||
long t = System.currentTimeMillis();
|
||||
|
||||
mAuthenticatorIds.clear();
|
||||
for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
|
||||
int userId = getUserOrWorkProfileId(null, user.id);
|
||||
@@ -268,14 +285,88 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
}
|
||||
}
|
||||
|
||||
private void doFingerprintCleanup(int userId) {
|
||||
if (CLEANUP_UNUSED_FP) {
|
||||
resetEnumerateState();
|
||||
mEnumeratingUserIds.push(userId);
|
||||
enumerateNextUser();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetEnumerateState() {
|
||||
if (DEBUG) Slog.v(TAG, "Enumerate cleaning up");
|
||||
mEnumeratingUserIds.clear();
|
||||
mUnknownFingerprints.clear();
|
||||
}
|
||||
|
||||
private void enumerateNextUser() {
|
||||
int nextUser = mEnumeratingUserIds.getFirst();
|
||||
updateActiveGroup(nextUser, null);
|
||||
boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
|
||||
|
||||
if (DEBUG) Slog.v(TAG, "Enumerating user id " + nextUser + " of "
|
||||
+ mEnumeratingUserIds.size() + " remaining users");
|
||||
|
||||
startEnumerate(mToken, nextUser, null, restricted, true /* internal */);
|
||||
}
|
||||
|
||||
// Remove unknown fingerprints from hardware
|
||||
private void cleanupUnknownFingerprints() {
|
||||
if (!mUnknownFingerprints.isEmpty()) {
|
||||
Slog.w(TAG, "unknown fingerprint size: " + mUnknownFingerprints.size());
|
||||
UserFingerprint uf = mUnknownFingerprints.get(0);
|
||||
mUnknownFingerprints.remove(uf);
|
||||
boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
|
||||
updateActiveGroup(uf.userId, null);
|
||||
startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
|
||||
restricted, true /* internal */);
|
||||
} else {
|
||||
resetEnumerateState();
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
|
||||
if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId + ", gid="
|
||||
+ groupId + "rem=" + remaining);
|
||||
// TODO: coordinate names with framework
|
||||
if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId
|
||||
+ ", gid=" + groupId
|
||||
+ ", dev=" + deviceId
|
||||
+ ", rem=" + remaining);
|
||||
|
||||
ClientMonitor client = mCurrentClient;
|
||||
|
||||
if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
|
||||
return;
|
||||
}
|
||||
client.onEnumerationResult(fingerId, groupId, remaining);
|
||||
|
||||
// All fingerprints in hardware for this user were enumerated
|
||||
if (remaining == 0) {
|
||||
mEnumeratingUserIds.poll();
|
||||
|
||||
if (client instanceof InternalEnumerateClient) {
|
||||
List<Fingerprint> enrolled = ((InternalEnumerateClient) client).getEnumeratedList();
|
||||
Slog.w(TAG, "Added " + enrolled.size() + " enumerated fingerprints for deletion");
|
||||
for (Fingerprint f : enrolled) {
|
||||
mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
|
||||
}
|
||||
}
|
||||
|
||||
removeClient(client);
|
||||
|
||||
if (!mEnumeratingUserIds.isEmpty()) {
|
||||
enumerateNextUser();
|
||||
} else if (client instanceof InternalEnumerateClient) {
|
||||
if (DEBUG) Slog.v(TAG, "Finished enumerating all users");
|
||||
// This will start a chain of InternalRemovalClients
|
||||
cleanupUnknownFingerprints();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleError(long deviceId, int error, int vendorCode) {
|
||||
ClientMonitor client = mCurrentClient;
|
||||
if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) {
|
||||
resetEnumerateState();
|
||||
}
|
||||
if (client != null && client.onError(error, vendorCode)) {
|
||||
removeClient(client);
|
||||
}
|
||||
@@ -301,10 +392,20 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
}
|
||||
|
||||
protected void handleRemoved(long deviceId, int fingerId, int groupId, int remaining) {
|
||||
if (DEBUG) Slog.w(TAG, "Removed: fid=" + fingerId
|
||||
+ ", gid=" + groupId
|
||||
+ ", dev=" + deviceId
|
||||
+ ", rem=" + remaining);
|
||||
|
||||
ClientMonitor client = mCurrentClient;
|
||||
if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
|
||||
removeClient(client);
|
||||
}
|
||||
if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
|
||||
cleanupUnknownFingerprints();
|
||||
} else if (client instanceof InternalRemovalClient){
|
||||
resetEnumerateState();
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
|
||||
@@ -355,6 +456,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
|
||||
void handleUserSwitching(int userId) {
|
||||
updateActiveGroup(userId, null);
|
||||
doFingerprintCleanup(userId);
|
||||
}
|
||||
|
||||
private void removeClient(ClientMonitor client) {
|
||||
@@ -431,7 +533,15 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
ClientMonitor currentClient = mCurrentClient;
|
||||
if (currentClient != null) {
|
||||
if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
|
||||
currentClient.stop(initiatedByClient);
|
||||
if (currentClient instanceof InternalEnumerateClient ||
|
||||
currentClient instanceof InternalRemovalClient) {
|
||||
// This condition means we're currently running internal diagnostics to
|
||||
// remove extra fingerprints in the hardware and/or the software
|
||||
// TODO: design an escape hatch in case client never finishes
|
||||
}
|
||||
else {
|
||||
currentClient.stop(initiatedByClient);
|
||||
}
|
||||
mPendingClient = newClient;
|
||||
mHandler.removeCallbacks(mResetClientState);
|
||||
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
|
||||
@@ -448,47 +558,86 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
}
|
||||
|
||||
void startRemove(IBinder token, int fingerId, int groupId, int userId,
|
||||
IFingerprintServiceReceiver receiver, boolean restricted) {
|
||||
IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
|
||||
IBiometricsFingerprint daemon = getFingerprintDaemon();
|
||||
if (daemon == null) {
|
||||
Slog.w(TAG, "startRemove: no fingerprint HAL!");
|
||||
return;
|
||||
}
|
||||
RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
|
||||
receiver, fingerId, groupId, userId, restricted, token.toString()) {
|
||||
@Override
|
||||
public void notifyUserActivity() {
|
||||
FingerprintService.this.userActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiometricsFingerprint getFingerprintDaemon() {
|
||||
return FingerprintService.this.getFingerprintDaemon();
|
||||
}
|
||||
};
|
||||
startClient(client, true);
|
||||
if (internal) {
|
||||
Context context = getContext();
|
||||
InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
|
||||
token, receiver, fingerId, groupId, userId, restricted,
|
||||
context.getOpPackageName()) {
|
||||
@Override
|
||||
public void notifyUserActivity() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public IBiometricsFingerprint getFingerprintDaemon() {
|
||||
return FingerprintService.this.getFingerprintDaemon();
|
||||
}
|
||||
};
|
||||
startClient(client, true);
|
||||
}
|
||||
else {
|
||||
RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
|
||||
receiver, fingerId, groupId, userId, restricted, token.toString()) {
|
||||
@Override
|
||||
public void notifyUserActivity() {
|
||||
FingerprintService.this.userActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiometricsFingerprint getFingerprintDaemon() {
|
||||
return FingerprintService.this.getFingerprintDaemon();
|
||||
}
|
||||
};
|
||||
startClient(client, true);
|
||||
}
|
||||
}
|
||||
|
||||
void startEnumerate(IBinder token, int userId,
|
||||
IFingerprintServiceReceiver receiver, boolean restricted) {
|
||||
IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
|
||||
IBiometricsFingerprint daemon = getFingerprintDaemon();
|
||||
if (daemon == null) {
|
||||
Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
|
||||
return;
|
||||
}
|
||||
EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
|
||||
receiver, userId, userId, restricted, token.toString()) {
|
||||
@Override
|
||||
public void notifyUserActivity() {
|
||||
FingerprintService.this.userActivity();
|
||||
}
|
||||
if (internal) {
|
||||
List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
|
||||
Context context = getContext();
|
||||
InternalEnumerateClient client = new InternalEnumerateClient(context, mHalDeviceId,
|
||||
token, receiver, userId, userId, restricted, context.getOpPackageName(),
|
||||
enrolledList) {
|
||||
@Override
|
||||
public void notifyUserActivity() {
|
||||
|
||||
@Override
|
||||
public IBiometricsFingerprint getFingerprintDaemon() {
|
||||
return FingerprintService.this.getFingerprintDaemon();
|
||||
}
|
||||
};
|
||||
startClient(client, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiometricsFingerprint getFingerprintDaemon() {
|
||||
return FingerprintService.this.getFingerprintDaemon();
|
||||
}
|
||||
};
|
||||
startClient(client, true);
|
||||
}
|
||||
else {
|
||||
EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
|
||||
receiver, userId, userId, restricted, token.toString()) {
|
||||
@Override
|
||||
public void notifyUserActivity() {
|
||||
FingerprintService.this.userActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBiometricsFingerprint getFingerprintDaemon() {
|
||||
return FingerprintService.this.getFingerprintDaemon();
|
||||
}
|
||||
};
|
||||
startClient(client, true);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Fingerprint> getEnrolledFingerprints(int userId) {
|
||||
@@ -978,11 +1127,13 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startRemove(token, fingerId, groupId, userId, receiver, restricted);
|
||||
startRemove(token, fingerId, groupId, userId, receiver,
|
||||
restricted, false /* internal */);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void enumerate(final IBinder token, final int userId,
|
||||
final IFingerprintServiceReceiver receiver) {
|
||||
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
|
||||
@@ -990,7 +1141,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startEnumerate(token, userId, receiver, restricted);
|
||||
startEnumerate(token, userId, receiver, restricted, false /* internal */);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.hardware.fingerprint.IFingerprintServiceReceiver;
|
||||
import android.os.IBinder;
|
||||
import android.util.Slog;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An internal class to help clean up unknown fingerprints in the hardware and software
|
||||
*/
|
||||
public abstract class InternalEnumerateClient extends EnumerateClient {
|
||||
|
||||
private List<Fingerprint> mEnrolledList;
|
||||
private List<Fingerprint> mEnumeratedList = new ArrayList<>(); // list of fp to delete
|
||||
|
||||
public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
|
||||
IFingerprintServiceReceiver receiver, int groupId, int userId,
|
||||
boolean restricted, String owner, List<Fingerprint> enrolledList) {
|
||||
|
||||
super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
|
||||
mEnrolledList = enrolledList;
|
||||
}
|
||||
|
||||
private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {
|
||||
|
||||
boolean matched = false;
|
||||
for (int i=0; i<mEnrolledList.size(); i++) {
|
||||
if (mEnrolledList.get(i).getFingerId() == fingerId) {
|
||||
mEnrolledList.remove(i);
|
||||
matched = true;
|
||||
Slog.e(TAG, "Matched fingerprint fid=" + fingerId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fingerId 0 means no fingerprints are in hardware
|
||||
if (!matched && fingerId != 0) {
|
||||
Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
|
||||
mEnumeratedList.add(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
private void doFingerprintCleanup() {
|
||||
|
||||
if (mEnrolledList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Fingerprint f : mEnrolledList) {
|
||||
Slog.e(TAG, "Internal Enumerate: Removing dangling enrolled fingerprint: "
|
||||
+ f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
|
||||
+ " " + f.getDeviceId());
|
||||
|
||||
FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
|
||||
f.getFingerId(), getTargetUserId());
|
||||
}
|
||||
mEnrolledList.clear();
|
||||
}
|
||||
|
||||
public List<Fingerprint> getEnumeratedList() {
|
||||
return mEnumeratedList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
|
||||
|
||||
handleEnumeratedFingerprint(fingerId, groupId, remaining);
|
||||
if (remaining == 0) {
|
||||
doFingerprintCleanup();
|
||||
}
|
||||
|
||||
return fingerId == 0; // done when id hits 0
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.os.IBinder;
|
||||
import android.hardware.fingerprint.IFingerprintServiceReceiver;
|
||||
import com.android.server.fingerprint.RemovalClient;
|
||||
|
||||
public abstract class InternalRemovalClient extends RemovalClient {
|
||||
|
||||
public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
|
||||
IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
|
||||
boolean restricted, String owner) {
|
||||
|
||||
super(context, halDeviceId, token, receiver, fingerId, groupId, userId, restricted, owner);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -59,12 +59,23 @@ public abstract class RemovalClient extends ClientMonitor {
|
||||
|
||||
@Override
|
||||
public int stop(boolean initiatedByClient) {
|
||||
// We don't actually stop remove, but inform the client that the cancel operation succeeded
|
||||
// so we can start the next operation.
|
||||
if (initiatedByClient) {
|
||||
onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
|
||||
IBiometricsFingerprint daemon = getFingerprintDaemon();
|
||||
if (daemon == null) {
|
||||
Slog.w(TAG, "stopRemoval: no fingerprint HAL!");
|
||||
return ERROR_ESRCH;
|
||||
}
|
||||
return 0;
|
||||
try {
|
||||
final int result = daemon.cancel();
|
||||
if (result != 0) {
|
||||
Slog.w(TAG, "stopRemoval failed, result=" + result);
|
||||
return result;
|
||||
}
|
||||
if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "stopRemoval failed", e);
|
||||
return ERROR_ESRCH;
|
||||
}
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user