/** * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.fingerprint; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.ActivityManagerNative; import android.content.Context; import android.os.Binder; import android.os.CancellationSignal; import android.os.CancellationSignal.OnCancelListener; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.security.keystore.AndroidKeyStoreProvider; import android.util.Log; import android.util.Slog; import java.security.Signature; import java.util.List; import javax.crypto.Cipher; import javax.crypto.Mac; import static android.Manifest.permission.USE_FINGERPRINT; import static android.Manifest.permission.MANAGE_FINGERPRINT; /** * A class that coordinates access to the fingerprint hardware. *

* Use {@link android.content.Context#getSystemService(java.lang.String)} * with argument {@link android.content.Context#FINGERPRINT_SERVICE} to get * an instance of this class. */ public class FingerprintManager { private static final String TAG = "FingerprintManager"; private static final boolean DEBUG = true; private static final int MSG_ENROLL_RESULT = 100; private static final int MSG_ACQUIRED = 101; private static final int MSG_AUTHENTICATION_SUCCEEDED = 102; private static final int MSG_AUTHENTICATION_FAILED = 103; private static final int MSG_ERROR = 104; private static final int MSG_REMOVED = 105; // // Error messages from fingerprint hardware during initilization, enrollment, authentication or // removal. Must agree with the list in fingerprint.h // /** * The hardware is unavailable. Try again later. */ public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; /** * Error state returned when the sensor was unable to process the current image. */ public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; /** * Error state returned when the current request has been running too long. This is intended to * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is * platform and sensor-specific, but is generally on the order of 30 seconds. */ public static final int FINGERPRINT_ERROR_TIMEOUT = 3; /** * Error state returned for operations like enrollment; the operation cannot be completed * because there's not enough storage remaining to complete the operation. */ public static final int FINGERPRINT_ERROR_NO_SPACE = 4; /** * The operation was canceled because the fingerprint sensor is unavailable. For example, * this may happen when the user is switched, the device is locked or another pending operation * prevents or disables it. */ public static final int FINGERPRINT_ERROR_CANCELED = 5; /** * The {@link FingerprintManager#remove(Fingerprint, RemovalCallback)} call failed. Typically * this will happen when the provided fingerprint id was incorrect. * * @hide */ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; /** * The operation was canceled because the API is locked out due to too many attempts. */ public static final int FINGERPRINT_ERROR_LOCKOUT = 7; /** * Hardware vendors may extend this list if there are conditions that do not fall under one of * the above categories. Vendors are responsible for providing error strings for these errors. * @hide */ public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; // // Image acquisition messages. Must agree with those in fingerprint.h // /** * The image acquired was good. */ public static final int FINGERPRINT_ACQUIRED_GOOD = 0; /** * Only a partial fingerprint image was detected. During enrollment, the user should be * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor." */ public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; /** * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}). */ public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; /** * The fingerprint image was too noisy due to suspected or detected dirt on the sensor. * For example, it's reasonable return this after multiple * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor * when this is returned. */ public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; /** * The fingerprint image was unreadable due to lack of motion. This is most appropriate for * linear array sensors that require a swipe motion. */ public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; /** * The fingerprint image was incomplete due to quick motion. While mostly appropriate for * linear array sensors, this could also happen if the finger was moved during acquisition. * The user should be asked to move the finger slower (linear) or leave the finger on the sensor * longer. */ public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; /** * Hardware vendors may extend this list if there are conditions that do not fall under one of * the above categories. Vendors are responsible for providing error strings for these errors. * @hide */ public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; private IFingerprintService mService; private Context mContext; private IBinder mToken = new Binder(); private AuthenticationCallback mAuthenticationCallback; private EnrollmentCallback mEnrollmentCallback; private RemovalCallback mRemovalCallback; private CryptoObject mCryptoObject; private Fingerprint mRemovalFingerprint; private Handler mHandler; private class OnEnrollCancelListener implements OnCancelListener { @Override public void onCancel() { cancelEnrollment(); } } private class OnAuthenticationCancelListener implements OnCancelListener { private CryptoObject mCrypto; public OnAuthenticationCancelListener(CryptoObject crypto) { mCrypto = crypto; } @Override public void onCancel() { cancelAuthentication(mCrypto); } } /** * A wrapper class for the crypto objects supported by FingerprintManager. Currently the * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects. */ public static final class CryptoObject { public CryptoObject(@NonNull Signature signature) { mCrypto = signature; } public CryptoObject(@NonNull Cipher cipher) { mCrypto = cipher; } public CryptoObject(@NonNull Mac mac) { mCrypto = mac; } /** * Get {@link Signature} object. * @return {@link Signature} object or null if this doesn't contain one. */ public Signature getSignature() { return mCrypto instanceof Signature ? (Signature) mCrypto : null; } /** * Get {@link Cipher} object. * @return {@link Cipher} object or null if this doesn't contain one. */ public Cipher getCipher() { return mCrypto instanceof Cipher ? (Cipher) mCrypto : null; } /** * Get {@link Mac} object. * @return {@link Mac} object or null if this doesn't contain one. */ public Mac getMac() { return mCrypto instanceof Mac ? (Mac) mCrypto : null; } /** * @hide * @return the opId associated with this object or 0 if none */ public long getOpId() { return mCrypto != null ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0; } private final Object mCrypto; }; /** * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. */ public static class AuthenticationResult { private Fingerprint mFingerprint; private CryptoObject mCryptoObject; /** * Authentication result * * @param crypto the crypto object * @param fingerprint the recognized fingerprint data, if allowed. * @hide */ public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) { mCryptoObject = crypto; mFingerprint = fingerprint; } /** * Obtain the crypto object associated with this transaction * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. */ public CryptoObject getCryptoObject() { return mCryptoObject; } /** * Obtain the Fingerprint associated with this operation. Applications are strongly * discouraged from associating specific fingers with specific applications or operations. * * @hide */ public Fingerprint getFingerprint() { return mFingerprint; } }; /** * Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link * FingerprintManager#authenticate(CryptoObject, CancellationSignal, * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to * fingerprint events. */ public static abstract class AuthenticationCallback { /** * Called when an unrecoverable error has been encountered and the operation is complete. * No further callbacks will be made on this object. * @param errorCode An integer identifying the error message * @param errString A human-readable error string that can be shown in UI */ public void onAuthenticationError(int errorCode, CharSequence errString) { } /** * Called when a recoverable error has been encountered during authentication. The help * string is provided to give the user guidance for what went wrong, such as * "Sensor dirty, please clean it." * @param helpCode An integer identifying the error message * @param helpString A human-readable string that can be shown in UI */ public void onAuthenticationHelp(int helpCode, CharSequence helpString) { } /** * Called when a fingerprint is recognized. * @param result An object containing authentication-related data */ public void onAuthenticationSucceeded(AuthenticationResult result) { } /** * Called when a fingerprint is valid but not recognized. */ public void onAuthenticationFailed() { } /** * Called when a fingerprint image has been acquired, but wasn't processed yet. * * @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants * @hide */ public void onAuthenticationAcquired(int acquireInfo) {} }; /** * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback, * CancellationSignal, int). Users of {@link #FingerprintManager()} * must provide an implementation of this to {@link FingerprintManager#enroll(long, * CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events. * * @hide */ public static abstract class EnrollmentCallback { /** * Called when an unrecoverable error has been encountered and the operation is complete. * No further callbacks will be made on this object. * @param errMsgId An integer identifying the error message * @param errString A human-readable error string that can be shown in UI */ public void onEnrollmentError(int errMsgId, CharSequence errString) { } /** * Called when a recoverable error has been encountered during enrollment. The help * string is provided to give the user guidance for what went wrong, such as * "Sensor dirty, please clean it" or what they need to do next, such as * "Touch sensor again." * @param helpMsgId An integer identifying the error message * @param helpString A human-readable string that can be shown in UI */ public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { } /** * Called as each enrollment step progresses. Enrollment is considered complete when * remaining reaches 0. This function will not be called if enrollment fails. See * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} * @param remaining The number of remaining steps */ public void onEnrollmentProgress(int remaining) { } }; /** * Callback structure provided to {@link FingerprintManager#remove(int). Users of * {@link #FingerprintManager()} may optionally provide an implementation of this to * {@link FingerprintManager#remove(int, int, RemovalCallback)} for listening to * fingerprint template removal events. * * @hide */ public static abstract class RemovalCallback { /** * Called when the given fingerprint can't be removed. * @param fp The fingerprint that the call attempted to remove * @param errMsgId An associated error message id * @param errString An error message indicating why the fingerprint id can't be removed */ public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) { } /** * Called when a given fingerprint is successfully removed. * @param fingerprint the fingerprint template that was removed. */ public void onRemovalSucceeded(Fingerprint fingerprint) { } }; /** * Request authentication of a crypto object. This call warms up the fingerprint hardware * and starts scanning for a fingerprint. It terminates when * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at * which point the object is no longer valid. The operation can be canceled by using the * provided cancel object. * * @param crypto object associated with the call or null if none required. * @param cancel an object that can be used to cancel authentication * @param flags optional flags; should be 0 * @param callback an object to receive authentication events * @param handler an optional handler to handle callback events * * @throws IllegalArgumentException if the crypto operation is not supported or is not backed * by Android Keystore * facility. * @throws IllegalStateException if the crypto primitive is not initialized. */ @RequiresPermission(USE_FINGERPRINT) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) { authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId()); } /** * Use the provided handler thread for events. * @param handler */ private void useHandler(Handler handler) { if (handler != null) { mHandler = new MyHandler(handler.getLooper()); } else if (mHandler.getLooper() != mContext.getMainLooper()){ mHandler = new MyHandler(mContext.getMainLooper()); } } /** * Per-user version * @hide */ @RequiresPermission(USE_FINGERPRINT) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) { if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } if (cancel != null) { if (cancel.isCanceled()) { Log.w(TAG, "authentication already canceled"); return; } else { cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto)); } } if (mService != null) try { useHandler(handler); mAuthenticationCallback = callback; mCryptoObject = crypto; long sessionId = crypto != null ? crypto.getOpId() : 0; mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags, mContext.getOpPackageName()); } catch (RemoteException e) { Log.w(TAG, "Remote exception while authenticating: ", e); if (callback != null) { // Though this may not be a hardware issue, it will cause apps to give up or try // again later. callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE)); } } } /** * Request fingerprint enrollment. This call warms up the fingerprint hardware * and starts scanning for fingerprints. Progress will be indicated by callbacks to the * {@link EnrollmentCallback} object. It terminates when * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at * which point the object is no longer valid. The operation can be canceled by using the * provided cancel object. * @param token a unique token provided by a recent creation or verification of device * credentials (e.g. pin, pattern or password). * @param cancel an object that can be used to cancel enrollment * @param flags optional flags * @param callback an object to receive enrollment events * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] token, CancellationSignal cancel, int flags, EnrollmentCallback callback) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); } if (cancel != null) { if (cancel.isCanceled()) { Log.w(TAG, "enrollment already canceled"); return; } else { cancel.setOnCancelListener(new OnEnrollCancelListener()); } } if (mService != null) try { mEnrollmentCallback = callback; mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); if (callback != null) { // Though this may not be a hardware issue, it will cause apps to give up or try // again later. callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE)); } } } /** * Requests a pre-enrollment auth token to tie enrollment to the confirmation of * existing device credentials (e.g. pin/pattern/password). * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) public long preEnroll() { long result = 0; if (mService != null) try { result = mService.preEnroll(mToken); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); } return result; } /** * Finishes enrollment and cancels the current auth token. * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) public int postEnroll() { int result = 0; if (mService != null) try { result = mService.postEnroll(mToken); } catch (RemoteException e) { Log.w(TAG, "Remote exception in post enroll: ", e); } return result; } /** * Remove given fingerprint template from fingerprint hardware and/or protected storage. * @param fp the fingerprint item to remove * @param callback an optional callback to verify that fingerprint templates have been * successfully removed. May be null of no callback is required. * * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) public void remove(Fingerprint fp, RemovalCallback callback) { if (mService != null) try { mRemovalCallback = callback; mRemovalFingerprint = fp; mService.remove(mToken, fp.getFingerId(), getCurrentUserId(), mServiceReceiver); } catch (RemoteException e) { Log.w(TAG, "Remote exception in remove: ", e); if (callback != null) { callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE)); } } } /** * Renames the given fingerprint template * @param fpId the fingerprint id * @param newName the new name * * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) public void rename(int fpId, String newName) { // Renames the given fpId if (mService != null) { try { mService.rename(fpId, getCurrentUserId(), newName); } catch (RemoteException e) { Log.v(TAG, "Remote exception in rename(): ", e); } } else { Log.w(TAG, "rename(): Service not connected!"); } } /** * Obtain the list of enrolled fingerprints templates. * @return list of current fingerprint items * * @hide */ @RequiresPermission(USE_FINGERPRINT) public List getEnrolledFingerprints(int userId) { if (mService != null) try { return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName()); } catch (RemoteException e) { Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e); } return null; } /** * Obtain the list of enrolled fingerprints templates. * @return list of current fingerprint items * * @hide */ @RequiresPermission(USE_FINGERPRINT) public List getEnrolledFingerprints() { return getEnrolledFingerprints(UserHandle.myUserId()); } /** * Determine if there is at least one fingerprint enrolled. * * @return true if at least one fingerprint is enrolled, false otherwise */ @RequiresPermission(USE_FINGERPRINT) public boolean hasEnrolledFingerprints() { if (mService != null) try { return mService.hasEnrolledFingerprints(UserHandle.myUserId(), mContext.getOpPackageName()); } catch (RemoteException e) { Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e); } return false; } /** * Determine if fingerprint hardware is present and functional. * * @return true if hardware is present and functional, false otherwise. */ @RequiresPermission(USE_FINGERPRINT) public boolean isHardwareDetected() { if (mService != null) { try { long deviceId = 0; /* TODO: plumb hardware id to FPMS */ return mService.isHardwareDetected(deviceId, mContext.getOpPackageName()); } catch (RemoteException e) { Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e); } } else { Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!"); } return false; } /** * Retrieves the authenticator token for binding keys to the lifecycle * of the current set of fingerprints. Used only by internal clients. * * @hide */ public long getAuthenticatorId() { if (mService != null) { try { return mService.getAuthenticatorId(mContext.getOpPackageName()); } catch (RemoteException e) { Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e); } } else { Log.w(TAG, "getAuthenticatorId(): Service not connected!"); } return 0; } private class MyHandler extends Handler { private MyHandler(Context context) { super(context.getMainLooper()); } private MyHandler(Looper looper) { super(looper); } public void handleMessage(android.os.Message msg) { switch(msg.what) { case MSG_ENROLL_RESULT: sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */); break; case MSG_ACQUIRED: sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */); break; case MSG_AUTHENTICATION_SUCCEEDED: sendAuthenticatedSucceeded((Fingerprint) msg.obj); break; case MSG_AUTHENTICATION_FAILED: sendAuthenticatedFailed(); break; case MSG_ERROR: sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */); break; case MSG_REMOVED: sendRemovedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */, msg.arg2 /* groupId */); } } private void sendRemovedResult(long deviceId, int fingerId, int groupId) { if (mRemovalCallback != null) { int reqFingerId = mRemovalFingerprint.getFingerId(); int reqGroupId = mRemovalFingerprint.getGroupId(); if (fingerId != reqFingerId) { Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId); } if (fingerId != reqFingerId) { Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId); } mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint); } } private void sendErrorResult(long deviceId, int errMsgId) { if (mEnrollmentCallback != null) { mEnrollmentCallback.onEnrollmentError(errMsgId, getErrorString(errMsgId)); } else if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationError(errMsgId, getErrorString(errMsgId)); } else if (mRemovalCallback != null) { mRemovalCallback.onRemovalError(mRemovalFingerprint, errMsgId, getErrorString(errMsgId)); } } private void sendEnrollResult(Fingerprint fp, int remaining) { if (mEnrollmentCallback != null) { mEnrollmentCallback.onEnrollmentProgress(remaining); } } private void sendAuthenticatedSucceeded(Fingerprint fp) { if (mAuthenticationCallback != null) { final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp); mAuthenticationCallback.onAuthenticationSucceeded(result); } } private void sendAuthenticatedFailed() { if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationFailed(); } } private void sendAcquiredResult(long deviceId, int acquireInfo) { if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); } final String msg = getAcquiredString(acquireInfo); if (msg == null) { return; } if (mEnrollmentCallback != null) { mEnrollmentCallback.onEnrollmentHelp(acquireInfo, msg); } else if (mAuthenticationCallback != null) { mAuthenticationCallback.onAuthenticationHelp(acquireInfo, msg); } } }; /** * @hide */ public FingerprintManager(Context context, IFingerprintService service) { mContext = context; mService = service; if (mService == null) { Slog.v(TAG, "FingerprintManagerService was null"); } mHandler = new MyHandler(context); } private int getCurrentUserId() { try { return ActivityManagerNative.getDefault().getCurrentUser().id; } catch (RemoteException e) { Log.w(TAG, "Failed to get current user id\n"); return UserHandle.USER_NULL; } } private void cancelEnrollment() { if (mService != null) try { mService.cancelEnrollment(mToken); } catch (RemoteException e) { if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment"); } } private void cancelAuthentication(CryptoObject cryptoObject) { if (mService != null) try { mService.cancelAuthentication(mToken, mContext.getOpPackageName()); } catch (RemoteException e) { if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment"); } } private String getErrorString(int errMsg) { switch (errMsg) { case FINGERPRINT_ERROR_UNABLE_TO_PROCESS: return mContext.getString( com.android.internal.R.string.fingerprint_error_unable_to_process); case FINGERPRINT_ERROR_HW_UNAVAILABLE: return mContext.getString( com.android.internal.R.string.fingerprint_error_hw_not_available); case FINGERPRINT_ERROR_NO_SPACE: return mContext.getString( com.android.internal.R.string.fingerprint_error_no_space); case FINGERPRINT_ERROR_TIMEOUT: return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout); case FINGERPRINT_ERROR_CANCELED: return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled); case FINGERPRINT_ERROR_LOCKOUT: return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout); default: if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) { int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE; String[] msgArray = mContext.getResources().getStringArray( com.android.internal.R.array.fingerprint_error_vendor); if (msgNumber < msgArray.length) { return msgArray[msgNumber]; } } return null; } } private String getAcquiredString(int acquireInfo) { switch (acquireInfo) { case FINGERPRINT_ACQUIRED_GOOD: return null; case FINGERPRINT_ACQUIRED_PARTIAL: return mContext.getString( com.android.internal.R.string.fingerprint_acquired_partial); case FINGERPRINT_ACQUIRED_INSUFFICIENT: return mContext.getString( com.android.internal.R.string.fingerprint_acquired_insufficient); case FINGERPRINT_ACQUIRED_IMAGER_DIRTY: return mContext.getString( com.android.internal.R.string.fingerprint_acquired_imager_dirty); case FINGERPRINT_ACQUIRED_TOO_SLOW: return mContext.getString( com.android.internal.R.string.fingerprint_acquired_too_slow); case FINGERPRINT_ACQUIRED_TOO_FAST: return mContext.getString( com.android.internal.R.string.fingerprint_acquired_too_fast); default: if (acquireInfo >= FINGERPRINT_ACQUIRED_VENDOR_BASE) { int msgNumber = acquireInfo - FINGERPRINT_ACQUIRED_VENDOR_BASE; String[] msgArray = mContext.getResources().getStringArray( com.android.internal.R.array.fingerprint_acquired_vendor); if (msgNumber < msgArray.length) { return msgArray[msgNumber]; } } return null; } } private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() { @Override // binder call public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) { mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0, new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget(); } @Override // binder call public void onAcquired(long deviceId, int acquireInfo) { mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget(); } @Override // binder call public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) { mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget(); } @Override // binder call public void onAuthenticationFailed(long deviceId) { mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();; } @Override // binder call public void onError(long deviceId, int error) { mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget(); } @Override // binder call public void onRemoved(long deviceId, int fingerId, int groupId) { mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget(); } }; }