Merge "Fix bug where fingerprint events can be delivered to the wrong client" into nyc-dev

This commit is contained in:
Jim Miller
2016-04-15 01:33:13 +00:00
committed by Android (Google) Code Review
9 changed files with 937 additions and 409 deletions

View File

@@ -517,7 +517,8 @@ public class FingerprintManager {
if (mService != null) try {
mEnrollmentCallback = callback;
mService.enroll(mToken, token, userId, mServiceReceiver, flags);
mService.enroll(mToken, token, userId, mServiceReceiver, flags,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {

View File

@@ -35,4 +35,6 @@ interface IFingerprintDaemon {
int closeHal();
void init(IFingerprintDaemonCallback callback);
int postEnroll();
int enumerate();
int cancelEnumeration();
}

View File

@@ -35,7 +35,7 @@ interface IFingerprintService {
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
int flags);
int flags, String opPackageName);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);

View File

@@ -0,0 +1,156 @@
/**
* Copyright (C) 2016 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 com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.system.ErrnoException;
import android.util.Slog;
/**
* A class to keep track of the authentication state for a given client.
*/
public abstract class AuthenticationClient extends ClientMonitor {
private long mOpId;
public abstract boolean handleFailedAttempt();
public abstract void resetFailedAttempts();
public AuthenticationClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int userId, int groupId, long opId,
boolean restricted, String owner) {
super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
mOpId = opId;
}
@Override
public boolean onAuthenticated(int fingerId, int groupId) {
boolean result = false;
boolean authenticated = fingerId != 0;
IFingerprintServiceReceiver receiver = getReceiver();
if (receiver != null) {
try {
MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
authenticated);
if (!authenticated) {
receiver.onAuthenticationFailed(getHalDeviceId());
} else {
if (DEBUG) {
Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
+ ", id=" + fingerId + ", gp=" + groupId + ")");
}
Fingerprint fp = !getIsRestricted()
? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
: null;
receiver.onAuthenticationSucceeded(getHalDeviceId(), fp);
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Authenticated:", e);
result = true; // client failed
}
} else {
result = true; // client not listening
}
if (fingerId == 0) {
if (receiver != null) {
FingerprintUtils.vibrateFingerprintError(getContext());
}
// allow system-defined limit of number of attempts before giving up
result |= handleFailedAttempt();
} else {
if (receiver != null) {
FingerprintUtils.vibrateFingerprintSuccess(getContext());
}
result |= true; // we have a valid fingerprint, done
resetFailedAttempts();
}
return result;
}
/**
* Start authentication
*/
@Override
public int start() {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "start authentication: no fingeprintd!");
return ERROR_ESRCH;
}
try {
final int result = daemon.authenticate(mOpId, getGroupId());
if (result != 0) {
Slog.w(TAG, "startAuthentication failed, result=" + result);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
return result;
}
if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
}
@Override
public int stop(boolean initiatedByClient) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopAuthentication: no fingeprintd!");
return ERROR_ESRCH;
}
try {
final int result = daemon.cancelAuthentication();
if (result != 0) {
Slog.w(TAG, "stopAuthentication failed, result=" + result);
return result;
}
if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
} catch (RemoteException e) {
Slog.e(TAG, "stopAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
}
@Override
public boolean onEnrollResult(int fingerId, int groupId, int rem) {
if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
return true; // Invalid for Authenticate
}
@Override
public boolean onRemoved(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
return true; // Invalid for Authenticate
}
@Override
public boolean onEnumerationResult(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
return true; // Invalid for Authenticate
}
}

View File

@@ -0,0 +1,211 @@
/**
* Copyright (C) 2016 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.Manifest;
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import java.util.NoSuchElementException;
/**
* Abstract base class for keeping track and dispatching events from fingerprintd to the
* the current client. Subclasses are responsible for coordinating the interaction with
* fingerprintd for the specific action (e.g. authenticate, enroll, enumerate, etc.).
*/
public abstract class ClientMonitor implements IBinder.DeathRecipient {
protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
protected static final int ERROR_ESRCH = 3; // Likely fingerprintd is dead. See errno.h.
protected static final boolean DEBUG = FingerprintService.DEBUG;
private IBinder mToken;
private IFingerprintServiceReceiver mReceiver;
private int mUserId;
private int mGroupId;
private boolean mIsRestricted; // True if client does not have MANAGE_FINGERPRINT permission
private String mOwner;
private Context mContext;
private long mHalDeviceId;
/**
* @param context context of FingerprintService
* @param halDeviceId the HAL device ID of the associated fingerprint hardware
* @param token a unique token for the client
* @param receiver recipient of related events (e.g. authentication)
* @param userId userId for the fingerprint set
* @param groupId groupId for the fingerprint set
* @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
* permission
* @param owner name of the client that owns this
*/
public ClientMonitor(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
String owner) {
mContext = context;
mHalDeviceId = halDeviceId;
mToken = token;
mReceiver = receiver;
mUserId = userId;
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
}
}
/**
* Contacts fingerprintd to start the client.
* @return 0 on succes, errno from driver on failure
*/
public abstract int start();
/**
* Contacts fingerprintd to stop the client.
* @param initiatedByClient whether the operation is at the request of a client
*/
public abstract int stop(boolean initiatedByClient);
/**
* Method to explicitly poke powermanager on events
*/
public abstract void notifyUserActivity();
/**
* Gets the fingerprint daemon from the cached state in the container class.
*/
public abstract IFingerprintDaemon getFingerprintDaemon();
// Event callbacks from driver. Inappropriate calls is flagged/logged by the
// respective client (e.g. enrolling shouldn't get authenticate events).
// All of these return 'true' if the operation is completed and it's ok to move
// to the next client (e.g. authentication accepts or rejects a fingerprint).
public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
public abstract boolean onAuthenticated(int fingerId, int groupId);
public abstract boolean onRemoved(int fingerId, int groupId);
public abstract boolean onEnumerationResult(int fingerId, int groupId);
/**
* Called when we get notification from fingerprintd that an image has been acquired.
* Common to authenticate and enroll.
* @param acquiredInfo info about the current image acquisition
* @return true if client should be removed
*/
public boolean onAcquired(int acquiredInfo) {
if (mReceiver == null)
return true; // client not connected
try {
mReceiver.onAcquired(getHalDeviceId(), 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 == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
notifyUserActivity();
}
}
}
/**
* Called when we get notification from fingerprintd that an error has occurred with the
* current operation. Common to authenticate, enroll, enumerate and remove.
* @param error
* @return true if client should be removed
*/
public boolean onError(int error) {
if (mReceiver != null) {
try {
mReceiver.onError(getHalDeviceId(), error);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendError:", e);
}
}
return true; // errors always remove current client
}
public void destroy() {
if (mToken != null) {
try {
mToken.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
// TODO: remove when duplicate call bug is found
Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
}
mToken = null;
}
mReceiver = null;
}
@Override
public void binderDied() {
mToken = null;
mReceiver = null;
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
@Override
protected void finalize() throws Throwable {
try {
if (mToken != null) {
if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} finally {
super.finalize();
}
}
public final Context getContext() {
return mContext;
}
public final long getHalDeviceId() {
return mHalDeviceId;
}
public final String getOwnerString() {
return mOwner;
}
public final IFingerprintServiceReceiver getReceiver() {
return mReceiver;
}
public final boolean getIsRestricted() {
return mIsRestricted;
}
public final int getUserId() {
return mUserId;
}
public final int getGroupId() {
return mGroupId;
}
public final IBinder getToken() {
return mToken;
}
}

View File

@@ -0,0 +1,136 @@
/**
* Copyright (C) 2016 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.FingerprintManager;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import java.util.Arrays;
/**
* A class to keep track of the enrollment state for a given client.
*/
public abstract class EnrollClient extends ClientMonitor {
private static final long MS_PER_SEC = 1000;
private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
private byte[] mCryptoToken;
public EnrollClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
boolean restricted, String owner) {
super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
}
@Override
public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
if (remaining == 0) {
FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
getUserId());
}
return sendEnrollResult(fingerId, groupId, remaining);
}
/*
* @return true if we're done.
*/
private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
IFingerprintServiceReceiver receiver = getReceiver();
if (receiver == null)
return true; // client not listening
FingerprintUtils.vibrateFingerprintSuccess(getContext());
MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
try {
receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
return remaining == 0;
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify EnrollResult:", e);
return true;
}
}
@Override
public int start() {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "enroll: no fingeprintd!");
return ERROR_ESRCH;
}
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
if (result != 0) {
Slog.w(TAG, "startEnroll failed, result=" + result);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
return result;
}
} catch (RemoteException e) {
Slog.e(TAG, "startEnroll failed", e);
}
return 0; // success
}
@Override
public int stop(boolean initiatedByClient) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopEnrollment: no fingeprintd!");
return ERROR_ESRCH;
}
try {
final int result = daemon.cancelEnrollment();
if (result != 0) {
Slog.w(TAG, "startEnrollCancel failed, result = " + result);
return result;
}
} catch (RemoteException e) {
Slog.e(TAG, "stopEnrollment failed", e);
}
if (initiatedByClient) {
onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
return 0;
}
@Override
public boolean onRemoved(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
return true; // Invalid for EnrollClient
}
@Override
public boolean onEnumerationResult(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for enroll!");
return true; // Invalid for EnrollClient
}
@Override
public boolean onAuthenticated(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
return true; // Invalid for EnrollClient
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (C) 2016 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.FingerprintManager;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
/**
* A class to keep track of the enumeration state for a given client.
*/
public abstract class EnumerateClient extends ClientMonitor {
public EnumerateClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int userId, int groupId,
boolean restricted, String owner) {
super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
}
@Override
public int start() {
IFingerprintDaemon daemon = getFingerprintDaemon();
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.enumerate();
if (result != 0) {
Slog.w(TAG, "start enumerate for user " + getUserId()
+ " failed, result=" + result);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
return result;
}
} catch (RemoteException e) {
Slog.e(TAG, "startRemove failed", e);
}
return 0;
}
@Override
public int stop(boolean initiatedByClient) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopAuthentication: no fingeprintd!");
return ERROR_ESRCH;
}
try {
final int result = daemon.cancelEnumeration();
if (result != 0) {
Slog.w(TAG, "stop enumeration failed, result=" + result);
return result;
}
} catch (RemoteException e) {
Slog.e(TAG, "stop enumeration failed", e);
return ERROR_ESRCH;
}
// We don't actually stop enumerate, but inform the client that the cancel operation
// succeeded so we can start the next operation.
if (initiatedByClient) {
onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
return 0; // success
}
@Override
public boolean onEnumerationResult(int fingerId, int groupId) {
IFingerprintServiceReceiver receiver = getReceiver();
if (receiver == null)
return true; // client not listening
try {
receiver.onRemoved(getHalDeviceId(), fingerId, groupId);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify enumerated:", e);
}
return fingerId == 0; // done when id hits 0
}
}

View File

@@ -19,7 +19,6 @@ package com.android.server.fingerprint;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.trust.TrustManager;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppOpsManager;
@@ -48,7 +47,6 @@ import android.os.UserManager;
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.server.SystemService;
import org.json.JSONArray;
@@ -75,7 +73,6 @@ 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.
@@ -85,31 +82,36 @@ import java.util.NoSuchElementException;
* @hide
*/
public class FingerprintService extends SystemService implements IBinder.DeathRecipient {
private static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
static final String TAG = "FingerprintService";
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 static final String ACTION_LOCKOUT_RESET =
"com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
private ClientMonitor mAuthClient = null;
private ClientMonitor mEnrollClient = null;
private ClientMonitor mRemoveClient = null;
private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
new ArrayList<>();
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;
private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
private final String mKeyguardPackage;
private int mCurrentUserId = UserHandle.USER_CURRENT;
private int mUserIdForRemove = UserHandle.USER_NULL;
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
private Context mContext;
private long mHalDeviceId;
private int mFailedAttempts;
private IFingerprintDaemon mDaemon;
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final UserManager mUserManager;
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private long mCurrentAuthenticatorId;
Handler mHandler = new Handler() {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
@@ -123,15 +125,6 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
};
private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
private Context mContext;
private long mHalDeviceId;
private int mFailedAttempts;
private IFingerprintDaemon mDaemon;
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final UserManager mUserManager;
private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -148,6 +141,26 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
};
private final Runnable mResetClientState = new Runnable() {
@Override
public void run() {
// Warning: if we get here, the driver never confirmed our call to cancel the current
// operation (authenticate, enroll, remove, enumerate, etc), which is
// really bad. The result will be a 3-second delay in starting each new client.
// If you see this on a device, make certain the driver notifies with
// {@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} in response to cancel()
// once it has successfully switched to the IDLE state in the fingerprint HAL.
// Additionally,{@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} should only be sent
// in response to an actual cancel() call.
Slog.w(TAG, "Client "
+ (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+ " failed to respond to cancel, starting client "
+ (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
mCurrentClient = null;
startClient(mPendingClient, false);
}
};
public FingerprintService(Context context) {
super(context);
mContext = context;
@@ -203,64 +216,49 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
// TODO: update fingerprint/name pairs
}
protected void handleRemoved(long deviceId, int fingerId, int groupId) {
final ClientMonitor client = mRemoveClient;
if (fingerId != 0) {
removeTemplateForUser(mUserIdForRemove, fingerId);
} else {
mUserIdForRemove = UserHandle.USER_NULL;
protected void handleError(long deviceId, int error) {
ClientMonitor client = mCurrentClient;
if (client != null && client.onError(error)) {
removeClient(client);
}
if (client != null && client.sendRemoved(fingerId, groupId)) {
if (DEBUG) Slog.v(TAG, "handleError(client="
+ client != null ? client.getOwnerString() : "null" + ", error = " + error + ")");
// This is the magic code that starts the next client when the old client finishes.
if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
mHandler.removeCallbacks(mResetClientState);
if (mPendingClient != null) {
if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
startClient(mPendingClient, false);
mPendingClient = null;
}
}
}
protected void handleRemoved(long deviceId, int fingerId, int groupId) {
ClientMonitor client = mCurrentClient;
if (client != null && client.onRemoved(fingerId, groupId)) {
removeClient(client);
}
}
protected void handleError(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 handleAuthenticated(long deviceId, int fingerId, int groupId) {
if (mAuthClient != null) {
final IBinder token = mAuthClient.token;
if (mAuthClient.sendAuthenticated(fingerId, groupId)) {
stopAuthentication(token, false);
removeClient(mAuthClient);
}
ClientMonitor client = mCurrentClient;
if (client != null && client.onAuthenticated(fingerId, groupId)) {
removeClient(client);
}
}
protected void handleAcquired(long deviceId, int acquiredInfo) {
if (mEnrollClient != null) {
if (mEnrollClient.sendAcquired(acquiredInfo)) {
removeClient(mEnrollClient);
}
} else if (mAuthClient != null) {
if (mAuthClient.sendAcquired(acquiredInfo)) {
removeClient(mAuthClient);
}
ClientMonitor client = mCurrentClient;
if (client != null && client.onAcquired(acquiredInfo)) {
removeClient(client);
}
}
protected void handleEnrollResult(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);
}
}
ClientMonitor client = mCurrentClient;
if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
removeClient(client);
}
}
@@ -274,14 +272,16 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
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;
if (client != null) {
client.destroy();
if (client != mCurrentClient && mCurrentClient != null) {
Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
+ mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
}
}
if (mCurrentClient != null) {
if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
mCurrentClient = null;
}
}
@@ -303,60 +303,6 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
}
private void resetFailedAttempts() {
if (DEBUG && inLockoutMode()) {
Slog.v(TAG, "Reset fingerprint lockout");
}
mFailedAttempts = 0;
// If we're asked to reset failed attempts externally (i.e. from Keyguard), the alarm might
// still be pending; remove it.
cancelLockoutReset();
notifyLockoutResetMonitors();
}
private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
mFailedAttempts++;
if (inLockoutMode()) {
// Failing multiple times will continue to push out the lockout time.
scheduleLockoutReset();
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(int userId, int fingerId) {
mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, 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, token.toString());
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);
handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startEnroll failed", e);
}
}
public long startPreEnroll(IBinder token) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
@@ -385,123 +331,52 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
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)
* Calls fingerprintd to switch states to the new task. If there's already a current task,
* it calls cancel() and sets mPendingClient to begin when the current task finishes
* ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
* @param newClient the new client that wants to connect
* @param initiatedByClient true for authenticate, remove and enroll
*/
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 realUserId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startAuthentication: no fingeprintd!");
return;
}
stopPendingOperations(true);
updateActiveGroup(groupId, opPackageName);
mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
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);
handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
currentClient.stop(initiatedByClient);
mPendingClient = newClient;
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
} else if (newClient != null) {
mCurrentClient = newClient;
if (DEBUG) Slog.v(TAG, "starting client "
+ newClient.getClass().getSuperclass().getSimpleName()
+ "(" + newClient.getOwnerString() + ")"
+ ", initiatedByClient = " + initiatedByClient + ")");
newClient.start();
}
}
/**
* 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,
void startRemove(IBinder token, int fingerId, int userId, int groupId,
IFingerprintServiceReceiver receiver, boolean restricted) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startRemove: no fingeprintd!");
return;
}
stopPendingOperations(true);
mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
mUserIdForRemove = mCurrentUserId;
// 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);
handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
receiver, userId, groupId, fingerId, restricted, token.toString()) {
@Override
public void notifyUserActivity() {
FingerprintService.this.userActivity();
}
} catch (RemoteException e) {
Slog.e(TAG, "startRemove failed", e);
}
@Override
public IFingerprintDaemon getFingerprintDaemon() {
FingerprintService.this.getFingerprintDaemon();
return null;
}
};
startClient(client, true);
}
public List<Fingerprint> getEnrolledFingerprints(int userId) {
@@ -572,10 +447,9 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
* @param foregroundOnly only allow this call while app is in the foreground
* @return true if caller can use fingerprint API
*/
private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly) {
private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly, int uid,
int pid) {
checkPermission(USE_FINGERPRINT);
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
if (isKeyguard(opPackageName)) {
return true; // Keyguard is always allowed
}
@@ -620,165 +494,83 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
}
private class ClientMonitor implements IBinder.DeathRecipient {
IBinder token;
IFingerprintServiceReceiver receiver;
int userId;
boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
String owner;
private void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName) {
updateActiveGroup(groupId, opPackageName);
public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
boolean restricted, String owner) {
this.token = token;
this.receiver = receiver;
this.userId = userId;
this.restricted = restricted;
this.owner = owner; // name of the client that owns this - for debugging
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
}
}
if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
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"));
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
receiver, realUserId, groupId, opId, restricted, opPackageName) {
@Override
public boolean handleFailedAttempt() {
mFailedAttempts++;
if (inLockoutMode()) {
// Failing multiple times will continue to push out the lockout time.
scheduleLockoutReset();
return true;
}
token = null;
return false;
}
receiver = null;
}
@Override
public void binderDied() {
token = null;
removeClient(this);
receiver = null;
}
@Override
public void resetFailedAttempts() {
FingerprintService.this.resetFailedAttempts();
}
@Override
protected void finalize() throws Throwable {
try {
if (token != null) {
if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
removeClient(this);
}
} finally {
super.finalize();
@Override
public void notifyUserActivity() {
FingerprintService.this.userActivity();
}
}
/*
* @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);
@Override
public IFingerprintDaemon getFingerprintDaemon() {
return FingerprintService.this.getFingerprintDaemon();
}
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());
MetricsLogger.action(mContext, MetricsEvent.ACTION_FINGERPRINT_ENROLL);
try {
receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
return remaining == 0;
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify EnrollResult:", e);
return true;
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
// Don't bother starting the client. Just send the error message.
if (!client.onError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
Slog.w(TAG, "Cannot send timeout message to client");
}
return;
}
startClient(client, true /* initiatedByClient */);
}
/*
* @return true if we're done.
*/
private boolean sendAuthenticated(int fpId, int groupId) {
boolean result = false;
boolean authenticated = fpId != 0;
if (receiver != null) {
try {
MetricsLogger.action(mContext, MetricsEvent.ACTION_FINGERPRINT_AUTH,
authenticated);
if (!authenticated) {
receiver.onAuthenticationFailed(mHalDeviceId);
} else {
if (DEBUG) {
Slog.v(TAG, "onAuthenticated(owner=" + mAuthClient.owner
+ ", id=" + fpId + ", gp=" + groupId + ")");
}
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) {
if (receiver != null) {
FingerprintUtils.vibrateFingerprintError(getContext());
}
result |= handleFailedAttempt(this);
} else {
if (receiver != null) {
FingerprintUtils.vibrateFingerprintSuccess(getContext());
}
result |= true; // we have a valid fingerprint
resetFailedAttempts();
}
return result;
}
private void startEnrollment(IBinder token, byte [] cryptoToken, int userId, int groupId,
IFingerprintServiceReceiver receiver, int flags, boolean restricted,
String opPackageName) {
updateActiveGroup(groupId, opPackageName);
/*
* @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();
}
}
}
EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
userId, groupId, cryptoToken, restricted, opPackageName) {
/*
* @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);
}
@Override
public IFingerprintDaemon getFingerprintDaemon() {
return FingerprintService.this.getFingerprintDaemon();
}
return true; // errors always terminate progress
@Override
public void notifyUserActivity() {
FingerprintService.this.userActivity();
}
};
startClient(client, true /* initiatedByClient */);
}
protected void resetFailedAttempts() {
if (DEBUG && inLockoutMode()) {
Slog.v(TAG, "Reset fingerprint lockout");
}
mFailedAttempts = 0;
// If we're asked to reset failed attempts externally (i.e. from Keyguard),
// the alarm might still be pending; remove it.
cancelLockoutReset();
notifyLockoutResetMonitors();
}
private class FingerprintServiceLockoutResetMonitor {
@@ -876,8 +668,6 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
};
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);
@@ -892,7 +682,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override // Binder call
public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags) {
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
checkPermission(MANAGE_FINGERPRINT);
final int limit = mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
@@ -903,7 +694,6 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
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.
@@ -915,7 +705,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
mHandler.post(new Runnable() {
@Override
public void run() {
startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted);
startEnrollment(token, cryptoToken, userId, groupId, receiver, flags,
restricted, opPackageName);
}
});
}
@@ -932,7 +723,10 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
mHandler.post(new Runnable() {
@Override
public void run() {
stopEnrollment(token, true);
ClientMonitor client = mCurrentClient;
if (client instanceof EnrollClient && client.getToken() == token) {
client.stop(client.getToken() == token);
}
}
});
}
@@ -941,17 +735,18 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) {
if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
final int realUserId = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final boolean restricted = isRestricted();
mHandler.post(new Runnable() {
@Override
public void run() {
MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
realUserId, pid)) {
if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
startAuthentication(token, opId, realUserId, groupId, receiver,
flags, restricted, opPackageName);
}
@@ -959,14 +754,29 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
@Override // Binder call
public void cancelAuthentication(final IBinder token, String opPackageName) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
return;
}
public void cancelAuthentication(final IBinder token, final String opPackageName) {
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
mHandler.post(new Runnable() {
@Override
public void run() {
stopAuthentication(token, true);
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, uid, pid)) {
if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
} else {
ClientMonitor client = mCurrentClient;
if (client instanceof AuthenticationClient) {
if (client.getToken() == token) {
if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
client.stop(client.getToken() == token);
} else {
if (DEBUG) Slog.v(TAG, "can't stop client "
+ client.getOwnerString() + " since tokens don't match");
}
} else if (client != null) {
if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
+ client.getOwnerString());
}
}
}
});
}
@@ -987,10 +797,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
final boolean restricted = isRestricted();
final int realUserId = Binder.getCallingUid();
mHandler.post(new Runnable() {
@Override
public void run() {
startRemove(token, fingerId, groupId, receiver, restricted);
startRemove(token, fingerId, realUserId, groupId, receiver, restricted);
}
});
@@ -998,7 +809,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override // Binder call
public boolean isHardwareDetected(long deviceId, String opPackageName) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid())) {
return false;
}
return mHalDeviceId != 0;
@@ -1021,7 +833,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override // Binder call
public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid())) {
return Collections.emptyList();
}
if (!isCurrentUserOrProfile(userId)) {
@@ -1033,7 +846,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override // Binder call
public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid())) {
return false;
}
@@ -1061,7 +875,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
// The permission check should be restored once Android Keystore no longer invokes this
// method from inside app processes.
return FingerprintService.this.getAuthenticatorId();
return FingerprintService.this.getAuthenticatorId(opPackageName);
}
@Override // Binder call
@@ -1154,6 +968,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
mCurrentUserId = userId;
mCurrentAuthenticatorId = daemon.getAuthenticatorId();
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveGroup():", e);
@@ -1204,14 +1019,12 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
}
public long getAuthenticatorId() {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon != null) {
try {
return daemon.getAuthenticatorId();
} catch (RemoteException e) {
Slog.e(TAG, "getAuthenticatorId failed", e);
}
public long getAuthenticatorId(String opPackageName) {
if (canUseFingerprint(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid())) {
return mCurrentAuthenticatorId;
} else {
Slog.w(TAG, "Client isn't current, returning authenticator_id=0");
}
return 0;
}

View File

@@ -0,0 +1,117 @@
/**
* Copyright (C) 2016 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.FingerprintManager;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
/**
* A class to keep track of the remove state for a given client.
*/
public abstract class RemovalClient extends ClientMonitor {
private int mFingerId;
private int mUserIdForRemove;
public RemovalClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int userId, int groupId, int fingerId,
boolean restricted, String owner) {
super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
mFingerId = fingerId;
mUserIdForRemove = userId;
}
@Override
public int start() {
IFingerprintDaemon daemon = getFingerprintDaemon();
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(mFingerId, getUserId());
if (result != 0) {
Slog.w(TAG, "startRemove with id = " + mFingerId + " failed, result=" + result);
onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
return result;
}
} catch (RemoteException e) {
Slog.e(TAG, "startRemove failed", e);
}
return 0;
}
@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);
}
return 0;
}
/*
* @return true if we're done.
*/
private boolean sendRemoved(int fingerId, int groupId) {
IFingerprintServiceReceiver receiver = getReceiver();
if (receiver == null)
return true; // client not listening
try {
receiver.onRemoved(getHalDeviceId(), fingerId, groupId);
return fingerId == 0;
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Removed:", e);
}
return false;
}
@Override
public boolean onRemoved(int fingerId, int groupId) {
if (fingerId != 0) {
if (fingerId != mFingerId)
FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
mUserIdForRemove);
} else {
mUserIdForRemove = UserHandle.USER_NULL;
}
return sendRemoved(fingerId, getGroupId());
}
@Override
public boolean onEnrollResult(int fingerId, int groupId, int rem) {
if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
return true; // Invalid for Remove
}
@Override
public boolean onAuthenticated(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
return true; // Invalid for Remove.
}
@Override
public boolean onEnumerationResult(int fingerId, int groupId) {
if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for remove!");
return false; // Invalid for Remove.
}
}