Merge "Implement enumerate in FingerprintService" into oc-dev

This commit is contained in:
TreeHugger Robot
2017-04-12 09:35:12 +00:00
committed by Android (Google) Code Review
5 changed files with 330 additions and 41 deletions

View File

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

View File

@@ -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 */);
}
});
}

View File

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

View File

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

View File

@@ -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
}
/*