diff options
10 files changed, 270 insertions, 185 deletions
diff --git a/api/current.txt b/api/current.txt index 5346895..b8968df 100644 --- a/api/current.txt +++ b/api/current.txt @@ -13774,6 +13774,7 @@ package android.hardware.fingerprint { field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8 field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5 field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 + field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7 field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2 diff --git a/api/system-current.txt b/api/system-current.txt index c7db72b..b5b04d3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -14070,6 +14070,7 @@ package android.hardware.fingerprint { field public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; // 0x3e8 field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5 field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 + field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7 field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2 diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 5f85e7d..b757a9a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -56,7 +56,7 @@ public class 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_PROCESSED = 102; + private static final int MSG_AUTHENTICATED = 102; private static final int MSG_ERROR = 103; private static final int MSG_REMOVED = 104; @@ -103,6 +103,11 @@ public class FingerprintManager { */ 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. @@ -169,15 +174,9 @@ public class FingerprintManager { private Fingerprint mRemovalFingerprint; private class OnEnrollCancelListener implements OnCancelListener { - private long mChallenge; - - public OnEnrollCancelListener(long challenge) { - mChallenge = challenge; - } - @Override public void onCancel() { - cancelEnrollment(mChallenge); + cancelEnrollment(); } } @@ -437,14 +436,14 @@ public class FingerprintManager { * {@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 challenge a unique id provided by a recent verification of device credentials - * (e.g. pin, pattern or password). + * @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 callback an object to receive enrollment events * @param flags optional flags * @hide */ - public void enroll(long challenge, CancellationSignal cancel, EnrollmentCallback callback, + public void enroll(byte [] token, CancellationSignal cancel, EnrollmentCallback callback, int flags) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); @@ -455,13 +454,13 @@ public class FingerprintManager { Log.w(TAG, "enrollment already canceled"); return; } else { - cancel.setOnCancelListener(new OnEnrollCancelListener(challenge)); + cancel.setOnCancelListener(new OnEnrollCancelListener()); } } if (mService != null) try { mEnrollmentCallback = callback; - mService.enroll(mToken, challenge, getCurrentUserId(), mServiceReceiver, flags); + mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); if (callback != null) { @@ -574,8 +573,8 @@ public class FingerprintManager { case MSG_ACQUIRED: sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */); break; - case MSG_PROCESSED: - sendProcessedResult((Fingerprint) msg.obj); + case MSG_AUTHENTICATED: + sendAuthenticatedResult((Fingerprint) msg.obj); break; case MSG_ERROR: sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */); @@ -617,7 +616,7 @@ public class FingerprintManager { } } - private void sendProcessedResult(Fingerprint fp) { + private void sendAuthenticatedResult(Fingerprint fp) { if (mAuthenticationCallback != null) { if (fp.getFingerId() == 0 && fp.getGroupId() == 0) { // Fingerprint template valid but doesn't match one in database @@ -667,7 +666,7 @@ public class FingerprintManager { mRemovalCallback = null; } - private void cancelEnrollment(long challenge) { + private void cancelEnrollment() { if (mService != null) try { mService.cancelEnrollment(mToken); } catch (RemoteException e) { @@ -695,8 +694,11 @@ public class FingerprintManager { 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); + 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; @@ -753,8 +755,8 @@ public class FingerprintManager { mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget(); } - public void onProcessed(long deviceId, int fingerId, int groupId) { - mHandler.obtainMessage(MSG_PROCESSED, + public void onAuthenticated(long deviceId, int fingerId, int groupId) { + mHandler.obtainMessage(MSG_AUTHENTICATED, new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 2fcb20e..6fe72d5 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -33,7 +33,7 @@ interface IFingerprintService { void cancelAuthentication(IBinder token); // Start fingerprint enrollment - void enroll(IBinder token, long challenge, int groupId, IFingerprintServiceReceiver receiver, + void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver, int flags); // Cancel enrollment in progress diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index e82395f..a2d74b8 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -25,7 +25,7 @@ import android.os.UserHandle; oneway interface IFingerprintServiceReceiver { void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining); void onAcquired(long deviceId, int acquiredInfo); - void onProcessed(long deviceId, int fingerId, int groupId); + void onAuthenticated(long deviceId, int fingerId, int groupId); void onError(long deviceId, int error); void onRemoved(long deviceId, int fingerId, int groupId); } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d6886bd..9ec5221 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1242,23 +1242,26 @@ <!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning --> <string name="fingerprint_acquired_imager_dirty">Fingerprint sensor is dirty. Please clean and try again.</string> <!-- Message shown during fingerprint acquisision when the user removes their finger from the sensor too quickly --> - <string name="fingerprint_acquired_too_fast">Finger moved to fast. Please try again.</string> + <string name="fingerprint_acquired_too_fast">Finger moved too fast. Please try again.</string> <!-- Message shown during fingerprint acquisision when the user moves their finger too slowly --> <string name="fingerprint_acquired_too_slow">Finger moved to slow. Please try again.</string> <!-- Array containing custom messages shown during fingerprint acquisision from vendor. Vendor is expected to add and translate these strings --> <string-array name="fingerprint_acquired_vendor"> </string-array> - <!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint --> - <string name="fingerprint_error_unable_to_process">Unable to process. Try again.</string> <!-- Error message shown when the fingerprint hardware can't be accessed --> - <string name="fingerprint_error_hw_not_available">Hardware not available.</string> + <string name="fingerprint_error_hw_not_available">Fingerprint hardware not available.</string> <!-- Error message shown when the fingerprint hardware has run out of room for storing fingerprints --> <string name="fingerprint_error_no_space">Fingerprint can\'t be stored. Please remove an existing fingerprint.</string> <!-- Error message shown when the fingerprint hardware timer has expired and the user needs to restart the operation. --> <string name="fingerprint_error_timeout">Fingerprint time out reached. Try again.</string> - <!-- Error message shown when the fingerprint hardware timer has expired and the user needs to restart the operation. --> - <string name="fingerprint_error_vendor">Fingerprint time out reached. Try again.</string> + <!-- Generic error message shown when the fingerprint operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user--> + <string name="fingerprint_error_canceled">Fingerprint operation canceled.</string> + <!-- Generic error message shown when the fingerprint operation fails because too many attempts have been made. --> + <string name="fingerprint_error_lockout">Too many attempts. Try again later.</string> + <!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint --> + <string name="fingerprint_error_unable_to_process">Try again.</string> + <!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings --> <string-array name="fingerprint_error_vendor"> </string-array> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fd75d01..e5b1cb5 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2091,6 +2091,8 @@ <java-symbol type="string" name="fingerprint_acquired_too_slow" /> <java-symbol type="string" name="fingerprint_acquired_too_fast" /> <java-symbol type="array" name="fingerprint_acquired_vendor" /> + <java-symbol type="string" name="fingerprint_error_canceled" /> + <java-symbol type="string" name="fingerprint_error_lockout" /> <!-- From various Material changes --> <java-symbol type="attr" name="titleTextAppearance" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 410a7e4..094cd1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -28,6 +28,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; +import android.hardware.fingerprint.FingerprintManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.RemoteException; @@ -625,6 +626,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public void onFingerprintError(int msgId, String errString) { // TODO: Go to bouncer if this is "too many attempts" (lockout) error. Log.i(TAG, "FP Error: " + errString); + updateLockIcon(); } }; diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index c150b60..90e69d7 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -39,6 +39,7 @@ import static android.Manifest.permission.USE_FINGERPRINT; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -51,9 +52,9 @@ import java.util.List; public class FingerprintService extends SystemService { private static final String TAG = "FingerprintService"; private static final boolean DEBUG = true; - private ClientData mAuthClient = null; - private ClientData mEnrollClient = null; - private ClientData mRemoveClient = null; + private ClientMonitor mAuthClient = null; + private ClientMonitor mEnrollClient = null; + private ClientMonitor mRemoveClient = null; private static final int MSG_NOTIFY = 10; @@ -63,7 +64,6 @@ public class FingerprintService extends SystemService { // Must agree with the list in fingerprint.h private static final int FINGERPRINT_ERROR = -1; private static final int FINGERPRINT_ACQUIRED = 1; - private static final int FINGERPRINT_PROCESSED = 2; private static final int FINGERPRINT_TEMPLATE_ENROLLING = 3; private static final int FINGERPRINT_TEMPLATE_REMOVED = 4; private static final int FINGERPRINT_AUTHENTICATED = 5; @@ -83,80 +83,21 @@ public class FingerprintService extends SystemService { }; private Context mContext; private int mHalDeviceId; + private int mFailedAttempts; + private final Runnable mLockoutReset = new Runnable() { + @Override + public void run() { + resetFailedAttempts(); + } + }; private static final int STATE_IDLE = 0; private static final int STATE_AUTHENTICATING = 1; private static final int STATE_ENROLLING = 2; private static final int STATE_REMOVING = 3; private static final long MS_PER_SEC = 1000; - - private class ClientData { - IBinder token; - IFingerprintServiceReceiver receiver; - int userId; - long opId; - private TokenWatcher tokenWatcher; - public ClientData(IBinder token, long opId, IFingerprintServiceReceiver receiver, - int userId) { - this.token = token; - this.opId = opId; - this.receiver = receiver; - this.userId = userId; - tokenWatcher = new TokenWatcher(token); - try { - token.linkToDeath(tokenWatcher, 0); - } catch (RemoteException e) { - Slog.w(TAG, "caught remote exception in linkToDeath: ", e); - } - } - - IBinder getToken() { - return tokenWatcher.getToken(); - } - - public void destroy() { - token.unlinkToDeath(tokenWatcher, 0); - tokenWatcher.token = null; - } - } - - private class TokenWatcher implements IBinder.DeathRecipient { - WeakReference<IBinder> token; - - TokenWatcher(IBinder token) { - this.token = new WeakReference<IBinder>(token); - } - - IBinder getToken() { - return token.get(); - } - - public void binderDied() { - if (mAuthClient != null & mAuthClient.token == token) - mAuthClient = null; - if (mEnrollClient != null && mEnrollClient.token == token) - mEnrollClient = null; - this.token = null; - } - - protected void finalize() throws Throwable { - try { - if (token != null) { - if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token); - if (mAuthClient != null && mAuthClient.token == token) { - mAuthClient.destroy(); - mAuthClient = null; - } - if (mEnrollClient != null && mEnrollClient.token == token) { - mAuthClient.destroy(); - mEnrollClient = null; - } - } - } finally { - super.finalize(); - } - } - } + private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000; + private static final int MAX_FAILED_ATTEMPTS = 5; public FingerprintService(Context context) { super(context); @@ -166,11 +107,11 @@ public class FingerprintService extends SystemService { // TODO: Move these into separate process // JNI methods to communicate from FingerprintService to HAL - static native int nativeEnroll(long challenge, int groupId, int timeout); + static native int nativeEnroll(byte [] token, int groupId, int timeout); static native long nativePreEnroll(); static native int nativeStopEnrollment(); static native int nativeAuthenticate(long sessionId, int groupId); - static native int nativeStopAuthentication(long sessionId); + static native int nativeStopAuthentication(); static native int nativeRemove(int fingerId, int groupId); static native int nativeOpenHal(); static native int nativeCloseHal(); @@ -201,82 +142,62 @@ public class FingerprintService extends SystemService { Slog.v(TAG, "handleNotify(type=" + type + ", arg1=" + arg1 + ", arg2=" + arg2 + ")" + ", mAuthClients = " + mAuthClient + ", mEnrollClient = " + mEnrollClient); if (mEnrollClient != null) { - try { - final IBinder token = mEnrollClient.token; - if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) { - stopEnrollment(token); - } - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to mEnrollClient. Did it die?", e); - mEnrollClient.destroy(); - mEnrollClient = null; + final IBinder token = mEnrollClient.token; + if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) { + stopEnrollment(token); } } if (mAuthClient != null) { - try { - final IBinder token = mAuthClient.getToken(); - if (doNotify(mAuthClient, type, arg1, arg2, arg3)) { - stopAuthentication(token); - } - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to mAuthClient. Did it die?", e); - mAuthClient.destroy(); - mAuthClient = null; + final IBinder token = mAuthClient.token; + if (doNotify(mAuthClient, type, arg1, arg2, arg3)) { + stopAuthentication(token); } } if (mRemoveClient != null) { - try { - if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) { - mRemoveClient.destroy(); - mRemoveClient = null; - } - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to mRemoveClient. Did it die?", e); - mRemoveClient.destroy(); - mRemoveClient = null; + if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) { + removeClient(mRemoveClient); } } } // Returns true if the operation is done, i.e. authentication completed - boolean doNotify(ClientData clientData, int type, int arg1, int arg2, int arg3) - throws RemoteException { - if (clientData.receiver == null) { - if (DEBUG) Slog.v(TAG, "receiver not registered!!"); - return false; - } + boolean doNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) { ContentResolver contentResolver = mContext.getContentResolver(); boolean operationCompleted = false; switch (type) { case FINGERPRINT_ERROR: - clientData.receiver.onError(mHalDeviceId, arg1 /* error */); - if (arg1 == FingerprintManager.FINGERPRINT_ERROR_CANCELED) { - if (mEnrollClient != null) { - mEnrollClient.destroy(); - mEnrollClient = null; - } - if (mAuthClient != null) { - mAuthClient.destroy(); - mAuthClient = null; - } + { + final int error = arg1; + clientMonitor.sendError(error); + removeClient(clientMonitor); + operationCompleted = true; // any error means the operation is done } - operationCompleted = true; // any error means the operation is done break; case FINGERPRINT_ACQUIRED: - clientData.receiver.onAcquired(mHalDeviceId, arg1 /* acquireInfo */); + clientMonitor.sendAcquired(arg1 /* acquireInfo */); break; - case FINGERPRINT_PROCESSED: - clientData.receiver.onProcessed(mHalDeviceId, arg1 /* fpId */, arg2 /* gpId */); - operationCompleted = true; // we either got a positive or negative match + case FINGERPRINT_AUTHENTICATED: + { + final int fpId = arg1; + final int groupId = arg2; + clientMonitor.sendAuthenticated(fpId, groupId); + if (fpId == 0) { + if (clientMonitor == mAuthClient) { + operationCompleted = handleFailedAttempt(clientMonitor); + } + } else { + mLockoutReset.run(); // a valid fingerprint resets lockout + } + } break; case FINGERPRINT_TEMPLATE_ENROLLING: { final int fpId = arg1; final int groupId = arg2; final int remaining = arg3; - clientData.receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining); + clientMonitor.sendEnrollResult(fpId, groupId, remaining); if (remaining == 0) { - addTemplateForUser(clientData, contentResolver, fpId); + addTemplateForUser(clientMonitor, contentResolver, fpId); operationCompleted = true; // enroll completed } } @@ -285,11 +206,11 @@ public class FingerprintService extends SystemService { { final int fingerId = arg1; final int groupId = arg2; - removeTemplateForUser(clientData, contentResolver, fingerId); + removeTemplateForUser(clientMonitor, contentResolver, fingerId); if (fingerId == 0) { operationCompleted = true; // remove completed } else { - clientData.receiver.onRemoved(mHalDeviceId, fingerId, groupId); + clientMonitor.sendRemoved(fingerId, groupId); } } break; @@ -297,24 +218,60 @@ public class FingerprintService extends SystemService { return operationCompleted; } - private void removeTemplateForUser(ClientData clientData, ContentResolver contentResolver, + private void removeClient(ClientMonitor clientMonitor) { + if (clientMonitor == null) return; + clientMonitor.destroy(); + if (clientMonitor == mAuthClient) { + mAuthClient = null; + } else if (clientMonitor == mEnrollClient) { + mEnrollClient = null; + } else if (clientMonitor == mRemoveClient) { + mRemoveClient = null; + } + } + + private boolean inLockoutMode() { + return mFailedAttempts > MAX_FAILED_ATTEMPTS; + } + + private void resetFailedAttempts() { + if (DEBUG) 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, ContentResolver contentResolver, final int fingerId) { FingerprintUtils.removeFingerprintIdForUser(fingerId, contentResolver, - clientData.userId); + clientMonitor.userId); } - private void addTemplateForUser(ClientData clientData, ContentResolver contentResolver, + private void addTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver, final int fingerId) { FingerprintUtils.addFingerprintIdForUser(contentResolver, fingerId, - clientData.userId); + clientMonitor.userId); } - void startEnrollment(IBinder token, long opId, - int groupId, IFingerprintServiceReceiver receiver, int flags) { + void startEnrollment(IBinder token, byte[] cryptoToken, int groupId, + IFingerprintServiceReceiver receiver, int flags) { stopPendingOperations(); - mEnrollClient = new ClientData(token, opId, receiver, groupId); + mEnrollClient = new ClientMonitor(token, receiver, groupId); final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC); - final int result = nativeEnroll(opId, groupId, timeout); + final int result = nativeEnroll(cryptoToken, groupId, timeout); if (result != 0) { Slog.w(TAG, "startEnroll failed, result=" + result); } @@ -335,8 +292,11 @@ public class FingerprintService extends SystemService { } void stopEnrollment(IBinder token) { - if (mEnrollClient == null || mEnrollClient.token != token) return; + final ClientMonitor client = mEnrollClient; + if (client == null || client.token != token) return; int result = nativeStopEnrollment(); + client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED); + removeClient(mEnrollClient); if (result != 0) { Slog.w(TAG, "startEnrollCancel failed, result=" + result); } @@ -345,7 +305,15 @@ public class FingerprintService extends SystemService { void startAuthentication(IBinder token, long opId, int groupId, IFingerprintServiceReceiver receiver, int flags) { stopPendingOperations(); - mAuthClient = new ClientData(token, opId, receiver, groupId); + mAuthClient = new ClientMonitor(token, receiver, groupId); + 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; + } final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC); final int result = nativeAuthenticate(opId, groupId); if (result != 0) { @@ -354,8 +322,11 @@ public class FingerprintService extends SystemService { } void stopAuthentication(IBinder token) { - if (mAuthClient == null || mAuthClient.token != token) return; - int result = nativeStopAuthentication(mAuthClient.opId); + final ClientMonitor client = mAuthClient; + if (client == null || client.token != token) return; + int result = nativeStopAuthentication(); + client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED); + removeClient(mAuthClient); if (result != 0) { Slog.w(TAG, "stopAuthentication failed, result=" + result); } @@ -363,7 +334,7 @@ public class FingerprintService extends SystemService { void startRemove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver) { - mRemoveClient = new ClientData(token, 0, receiver, userId); + mRemoveClient = new ClientMonitor(token, receiver, userId); // The fingerprint template ids will be removed when we get confirmation from the HAL final int result = nativeRemove(fingerId, userId); if (result != 0) { @@ -392,17 +363,114 @@ public class FingerprintService extends SystemService { "Must have " + permission + " permission."); } - private static final class Message { + private class ClientMonitor implements IBinder.DeathRecipient { IBinder token; - long opId; - int groupId; - int flags; + WeakReference<IFingerprintServiceReceiver> receiver; + int userId; - public Message(IBinder token, long challenge, int groupId, int flags) { + public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId) { this.token = token; - this.opId = challenge; - this.groupId = groupId; - this.flags = flags; + this.receiver = new WeakReference<IFingerprintServiceReceiver>(receiver); + this.userId = userId; + try { + token.linkToDeath(this, 0); + } catch (RemoteException e) { + Slog.w(TAG, "caught remote exception in linkToDeath: ", e); + } + } + + public void destroy() { + if (token != null) { + token.unlinkToDeath(this, 0); + token = null; + } + receiver = null; + } + + public void binderDied() { + token = null; + removeClient(this); + } + + protected void finalize() throws Throwable { + try { + if (token != null) { + if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token); + removeClient(this); + } + } finally { + super.finalize(); + } + } + + private boolean sendRemoved(int fingerId, int groupId) { + IFingerprintServiceReceiver rx = receiver.get(); + if (rx != null) { + try { + rx.onRemoved(mHalDeviceId, fingerId, groupId); + return true; + } catch (RemoteException e) { + if (DEBUG) Slog.v(TAG, "Failed to invoke sendRemoved:", e); + } + } + removeClient(this); + return false; + } + + private boolean sendEnrollResult(int fpId, int groupId, int remaining) { + IFingerprintServiceReceiver rx = receiver.get(); + if (rx != null) { + try { + rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining); + return true; + } catch (RemoteException e) { + if (DEBUG) Slog.v(TAG, "Failed to invoke sendEnrollResult:", e); + } + } + removeClient(this); + return false; + } + + private boolean sendAuthenticated(int fpId, int groupId) { + IFingerprintServiceReceiver rx = receiver.get(); + if (rx != null) { + try { + rx.onAuthenticated(mHalDeviceId, fpId, groupId); + return true; + } catch (RemoteException e) { + if (DEBUG) Slog.v(TAG, "Failed to invoke sendProcessed:", e); + } + } + removeClient(this); + return false; + } + + private boolean sendAcquired(int acquiredInfo) { + IFingerprintServiceReceiver rx = receiver.get(); + if (rx != null) { + try { + rx.onAcquired(mHalDeviceId, acquiredInfo); + return true; + } catch (RemoteException e) { + if (DEBUG) Slog.v(TAG, "Failed to invoke sendAcquired:", e); + } + } + removeClient(this); + return false; + } + + private boolean sendError(int error) { + IFingerprintServiceReceiver rx = receiver.get(); + if (rx != null) { + try { + rx.onError(mHalDeviceId, error); + return true; + } catch (RemoteException e) { + if (DEBUG) Slog.v(TAG, "Failed to invoke sendError:", e); + } + } + removeClient(this); + return false; } } @@ -415,13 +483,14 @@ public class FingerprintService extends SystemService { @Override // Binder call - public void enroll(final IBinder token, final long opid, final int groupId, + public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId, final IFingerprintServiceReceiver receiver, final int flags) { checkPermission(MANAGE_FINGERPRINT); + final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length); mHandler.post(new Runnable() { @Override public void run() { - startEnrollment(token, opid, groupId, receiver, flags); + startEnrollment(token, cryptoClone, groupId, receiver, flags); } }); } diff --git a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp index a6cdbc4..17f86ca 100644 --- a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp +++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp @@ -128,11 +128,16 @@ static void nativeInit(JNIEnv *env, jobject clazz, jobject mQueue, jobject callb gLooper = android_os_MessageQueue_getMessageQueue(env, mQueue)->getLooper(); } -static jint nativeEnroll(JNIEnv* env, jobject clazz, jint groupId, jint timeout) { - hw_auth_token_t *hat = NULL; // This is here as a placeholder, - // please figure out your favorite way to send the hat struct through JNI +static jint nativeEnroll(JNIEnv* env, jobject clazz, jbyteArray token, jint groupId, jint timeout) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll(gid=%d, timeout=%d)\n", groupId, timeout); - int ret = gContext.device->enroll(gContext.device, hat, groupId, timeout); + const int tokenSize = env->GetArrayLength(token); + jbyte* tokenData = env->GetByteArrayElements(token, 0); + if (tokenSize != sizeof(hw_auth_token_t)) { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll() : invalid token size %d\n", tokenSize); + return -1; + } + int ret = gContext.device->enroll(gContext.device, (hw_auth_token_t*) tokenData, groupId, timeout); + env->ReleaseByteArrayElements(token, tokenData, 0); return reinterpret_cast<jint>(ret); } @@ -154,7 +159,7 @@ static jint nativeAuthenticate(JNIEnv* env, jobject clazz, jlong sessionId, jint return reinterpret_cast<jint>(ret); } -static jint nativeStopAuthentication(JNIEnv* env, jobject clazz, jlong sessionId) { +static jint nativeStopAuthentication(JNIEnv* env, jobject clazz) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeStopAuthentication()\n"); int ret = gContext.device->cancel(gContext.device); return reinterpret_cast<jint>(ret); @@ -227,8 +232,8 @@ static jint nativeCloseHal(JNIEnv* env, jobject clazz) { // TODO: clean up void methods static const JNINativeMethod g_methods[] = { { "nativeAuthenticate", "(JI)I", (void*)nativeAuthenticate }, - { "nativeStopAuthentication", "(J)I", (void*)nativeStopAuthentication }, - { "nativeEnroll", "(JII)I", (void*)nativeEnroll }, + { "nativeStopAuthentication", "()I", (void*)nativeStopAuthentication }, + { "nativeEnroll", "([BII)I", (void*)nativeEnroll }, { "nativePreEnroll", "()J", (void*)nativePreEnroll }, { "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment }, { "nativeRemove", "(II)I", (void*)nativeRemove }, |