FingerprintManager.java revision c50aded51d02477b29cdfff269e96ed91928eab1
1/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.fingerprint;
18
19import static android.Manifest.permission.INTERACT_ACROSS_USERS;
20import static android.Manifest.permission.MANAGE_FINGERPRINT;
21import static android.Manifest.permission.USE_BIOMETRIC;
22import static android.Manifest.permission.USE_FINGERPRINT;
23
24import android.annotation.CallbackExecutor;
25import android.annotation.NonNull;
26import android.annotation.Nullable;
27import android.annotation.RequiresFeature;
28import android.annotation.RequiresPermission;
29import android.annotation.SystemService;
30import android.app.ActivityManager;
31import android.content.Context;
32import android.content.pm.PackageManager;
33import android.hardware.biometrics.BiometricAuthenticator;
34import android.hardware.biometrics.BiometricDialog;
35import android.hardware.biometrics.BiometricFingerprintConstants;
36import android.hardware.biometrics.IBiometricDialogReceiver;
37import android.os.Binder;
38import android.os.Bundle;
39import android.os.CancellationSignal;
40import android.os.CancellationSignal.OnCancelListener;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.IRemoteCallback;
44import android.os.Looper;
45import android.os.PowerManager;
46import android.os.RemoteException;
47import android.os.UserHandle;
48import android.util.Log;
49import android.util.Slog;
50
51import java.security.Signature;
52import java.util.List;
53import java.util.concurrent.Executor;
54
55import javax.crypto.Cipher;
56import javax.crypto.Mac;
57
58/**
59 * A class that coordinates access to the fingerprint hardware.
60 * @deprecated See {@link BiometricDialog} which shows a system-provided dialog upon starting
61 * authentication. In a world where devices may have different types of biometric authentication,
62 * it's much more realistic to have a system-provided authentication dialog since the method may
63 * vary by vendor/device.
64 */
65@Deprecated
66@SystemService(Context.FINGERPRINT_SERVICE)
67@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
68public class FingerprintManager implements BiometricFingerprintConstants {
69    private static final String TAG = "FingerprintManager";
70    private static final boolean DEBUG = true;
71    private static final int MSG_ENROLL_RESULT = 100;
72    private static final int MSG_ACQUIRED = 101;
73    private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
74    private static final int MSG_AUTHENTICATION_FAILED = 103;
75    private static final int MSG_ERROR = 104;
76    private static final int MSG_REMOVED = 105;
77    private static final int MSG_ENUMERATED = 106;
78
79    private IFingerprintService mService;
80    private Context mContext;
81    private IBinder mToken = new Binder();
82    private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
83    private EnrollmentCallback mEnrollmentCallback;
84    private RemovalCallback mRemovalCallback;
85    private EnumerateCallback mEnumerateCallback;
86    private android.hardware.biometrics.CryptoObject mCryptoObject;
87    private Fingerprint mRemovalFingerprint;
88    private Handler mHandler;
89    private Executor mExecutor;
90
91    private class OnEnrollCancelListener implements OnCancelListener {
92        @Override
93        public void onCancel() {
94            cancelEnrollment();
95        }
96    }
97
98    private class OnAuthenticationCancelListener implements OnCancelListener {
99        private android.hardware.biometrics.CryptoObject mCrypto;
100
101        public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
102            mCrypto = crypto;
103        }
104
105        @Override
106        public void onCancel() {
107            cancelAuthentication(mCrypto);
108        }
109    }
110
111    /**
112     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
113     * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
114     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.CryptoObject}
115     */
116    @Deprecated
117    public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
118        public CryptoObject(@NonNull Signature signature) {
119            super(signature);
120        }
121
122        public CryptoObject(@NonNull Cipher cipher) {
123            super(cipher);
124        }
125
126        public CryptoObject(@NonNull Mac mac) {
127            super(mac);
128        }
129
130        /**
131         * Get {@link Signature} object.
132         * @return {@link Signature} object or null if this doesn't contain one.
133         */
134        public Signature getSignature() {
135            return super.getSignature();
136        }
137
138        /**
139         * Get {@link Cipher} object.
140         * @return {@link Cipher} object or null if this doesn't contain one.
141         */
142        public Cipher getCipher() {
143            return super.getCipher();
144        }
145
146        /**
147         * Get {@link Mac} object.
148         * @return {@link Mac} object or null if this doesn't contain one.
149         */
150        public Mac getMac() {
151            return super.getMac();
152        }
153    }
154
155    /**
156     * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
157     *     CancellationSignal, int, AuthenticationCallback, Handler)}.
158     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationResult}
159     */
160    @Deprecated
161    public static class AuthenticationResult {
162        private Fingerprint mFingerprint;
163        private CryptoObject mCryptoObject;
164        private int mUserId;
165
166        /**
167         * Authentication result
168         *
169         * @param crypto the crypto object
170         * @param fingerprint the recognized fingerprint data, if allowed.
171         * @hide
172         */
173        public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId) {
174            mCryptoObject = crypto;
175            mFingerprint = fingerprint;
176            mUserId = userId;
177        }
178
179        /**
180         * Obtain the crypto object associated with this transaction
181         * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
182         *     CancellationSignal, int, AuthenticationCallback, Handler)}.
183         */
184        public CryptoObject getCryptoObject() { return mCryptoObject; }
185
186        /**
187         * Obtain the Fingerprint associated with this operation. Applications are strongly
188         * discouraged from associating specific fingers with specific applications or operations.
189         *
190         * @hide
191         */
192        public Fingerprint getFingerprint() { return mFingerprint; }
193
194        /**
195         * Obtain the userId for which this fingerprint was authenticated.
196         * @hide
197         */
198        public int getUserId() { return mUserId; }
199    };
200
201    /**
202     * Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
203     * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
204     * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
205     * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
206     * fingerprint events.
207     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationCallback}
208     */
209    @Deprecated
210    public static abstract class AuthenticationCallback
211            extends BiometricAuthenticator.AuthenticationCallback {
212        /**
213         * Called when an unrecoverable error has been encountered and the operation is complete.
214         * No further callbacks will be made on this object.
215         * @param errorCode An integer identifying the error message
216         * @param errString A human-readable error string that can be shown in UI
217         */
218        @Override
219        public void onAuthenticationError(int errorCode, CharSequence errString) { }
220
221        /**
222         * Called when a recoverable error has been encountered during authentication. The help
223         * string is provided to give the user guidance for what went wrong, such as
224         * "Sensor dirty, please clean it."
225         * @param helpCode An integer identifying the error message
226         * @param helpString A human-readable string that can be shown in UI
227         */
228        @Override
229        public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
230
231        /**
232         * Called when a fingerprint is recognized.
233         * @param result An object containing authentication-related data
234         */
235        public void onAuthenticationSucceeded(AuthenticationResult result) { }
236
237        /**
238         * Called when a fingerprint is valid but not recognized.
239         */
240        @Override
241        public void onAuthenticationFailed() { }
242
243        /**
244         * Called when a fingerprint image has been acquired, but wasn't processed yet.
245         *
246         * @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
247         * @hide
248         */
249        @Override
250        public void onAuthenticationAcquired(int acquireInfo) {}
251
252        /**
253         * @hide
254         * @param result
255         */
256        @Override
257        public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
258            onAuthenticationSucceeded(new AuthenticationResult(
259                    (CryptoObject) result.getCryptoObject(),
260                    (Fingerprint) result.getId(), result.getUserId()));
261        }
262    };
263
264    /**
265     * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
266     * CancellationSignal, int). Users of {@link #FingerprintManager()}
267     * must provide an implementation of this to {@link FingerprintManager#enroll(long,
268     * CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events.
269     *
270     * @hide
271     */
272    public static abstract class EnrollmentCallback {
273        /**
274         * Called when an unrecoverable error has been encountered and the operation is complete.
275         * No further callbacks will be made on this object.
276         * @param errMsgId An integer identifying the error message
277         * @param errString A human-readable error string that can be shown in UI
278         */
279        public void onEnrollmentError(int errMsgId, CharSequence errString) { }
280
281        /**
282         * Called when a recoverable error has been encountered during enrollment. The help
283         * string is provided to give the user guidance for what went wrong, such as
284         * "Sensor dirty, please clean it" or what they need to do next, such as
285         * "Touch sensor again."
286         * @param helpMsgId An integer identifying the error message
287         * @param helpString A human-readable string that can be shown in UI
288         */
289        public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { }
290
291        /**
292         * Called as each enrollment step progresses. Enrollment is considered complete when
293         * remaining reaches 0. This function will not be called if enrollment fails. See
294         * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
295         * @param remaining The number of remaining steps
296         */
297        public void onEnrollmentProgress(int remaining) { }
298    };
299
300    /**
301     * Callback structure provided to {@link #remove}. Users of {@link FingerprintManager} may
302     * optionally provide an implementation of this to
303     * {@link #remove(Fingerprint, int, RemovalCallback)} for listening to fingerprint template
304     * removal events.
305     *
306     * @hide
307     */
308    public static abstract class RemovalCallback {
309        /**
310         * Called when the given fingerprint can't be removed.
311         * @param fp The fingerprint that the call attempted to remove
312         * @param errMsgId An associated error message id
313         * @param errString An error message indicating why the fingerprint id can't be removed
314         */
315        public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) { }
316
317        /**
318         * Called when a given fingerprint is successfully removed.
319         * @param fp The fingerprint template that was removed.
320         * @param remaining The number of fingerprints yet to be removed in this operation. If
321         *         {@link #remove} is called on one fingerprint, this should be 0. If
322         *         {@link #remove} is called on a group, this should be the number of remaining
323         *         fingerprints in the group, and 0 after the last fingerprint is removed.
324         */
325        public void onRemovalSucceeded(Fingerprint fp, int remaining) { }
326    };
327
328    /**
329     * Callback structure provided to {@link FingerprintManager#enumerate(int). Users of
330     * {@link #FingerprintManager()} may optionally provide an implementation of this to
331     * {@link FingerprintManager#enumerate(int, int, EnumerateCallback)} for listening to
332     * fingerprint template removal events.
333     *
334     * @hide
335     */
336    public static abstract class EnumerateCallback {
337        /**
338         * Called when the given fingerprint can't be removed.
339         * @param errMsgId An associated error message id
340         * @param errString An error message indicating why the fingerprint id can't be removed
341         */
342        public void onEnumerateError(int errMsgId, CharSequence errString) { }
343
344        /**
345         * Called when a given fingerprint is successfully removed.
346         * @param fingerprint the fingerprint template that was removed.
347         */
348        public void onEnumerate(Fingerprint fingerprint) { }
349    };
350
351    /**
352     * @hide
353     */
354    public static abstract class LockoutResetCallback {
355
356        /**
357         * Called when lockout period expired and clients are allowed to listen for fingerprint
358         * again.
359         */
360        public void onLockoutReset() { }
361    };
362
363    /**
364     * Request authentication of a crypto object. This call warms up the fingerprint hardware
365     * and starts scanning for a fingerprint. It terminates when
366     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
367     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
368     * which point the object is no longer valid. The operation can be canceled by using the
369     * provided cancel object.
370     *
371     * @param crypto object associated with the call or null if none required.
372     * @param cancel an object that can be used to cancel authentication
373     * @param flags optional flags; should be 0
374     * @param callback an object to receive authentication events
375     * @param handler an optional handler to handle callback events
376     *
377     * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
378     *         by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
379     *         facility</a>.
380     * @throws IllegalStateException if the crypto primitive is not initialized.
381     * @deprecated See {@link BiometricDialog#authenticate(CancellationSignal, Executor,
382     * BiometricDialog.AuthenticationCallback)} and {@link BiometricDialog#authenticate(
383     * BiometricDialog.CryptoObject, CancellationSignal, Executor,
384     * BiometricDialog.AuthenticationCallback)}
385     */
386    @Deprecated
387    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
388    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
389            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
390        authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
391    }
392
393    /**
394     * Use the provided handler thread for events.
395     * @param handler
396     */
397    private void useHandler(Handler handler) {
398        if (handler != null) {
399            mHandler = new MyHandler(handler.getLooper());
400        } else if (mHandler.getLooper() != mContext.getMainLooper()){
401            mHandler = new MyHandler(mContext.getMainLooper());
402        }
403    }
404
405    /**
406     * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
407     * CancellationSignal, int, AuthenticationCallback, Handler)}
408     * @param userId the user ID that the fingerprint hardware will authenticate for.
409     * @hide
410     */
411    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
412    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
413            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
414        if (callback == null) {
415            throw new IllegalArgumentException("Must supply an authentication callback");
416        }
417
418        if (cancel != null) {
419            if (cancel.isCanceled()) {
420                Log.w(TAG, "authentication already canceled");
421                return;
422            } else {
423                cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
424            }
425        }
426
427        if (mService != null) try {
428            useHandler(handler);
429            mAuthenticationCallback = callback;
430            mCryptoObject = crypto;
431            long sessionId = crypto != null ? crypto.getOpId() : 0;
432            mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
433                    mContext.getOpPackageName(), null /* bundle */, null /* receiver */);
434        } catch (RemoteException e) {
435            Log.w(TAG, "Remote exception while authenticating: ", e);
436            if (callback != null) {
437                // Though this may not be a hardware issue, it will cause apps to give up or try
438                // again later.
439                callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
440                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
441            }
442        }
443    }
444
445    /**
446     * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
447     * CancellationSignal, Bundle, Executor, IBiometricDialogReceiver, AuthenticationCallback)}
448     * @param userId the user ID that the fingerprint hardware will authenticate for.
449     */
450    private void authenticate(int userId,
451            @Nullable android.hardware.biometrics.CryptoObject crypto,
452            @NonNull CancellationSignal cancel,
453            @NonNull Bundle bundle,
454            @NonNull @CallbackExecutor Executor executor,
455            @NonNull IBiometricDialogReceiver receiver,
456            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
457        mCryptoObject = crypto;
458        if (cancel.isCanceled()) {
459            Log.w(TAG, "authentication already canceled");
460            return;
461        } else {
462            cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
463        }
464
465        if (mService != null) {
466            try {
467                mExecutor = executor;
468                mAuthenticationCallback = callback;
469                final long sessionId = crypto != null ? crypto.getOpId() : 0;
470                mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
471                        0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
472            } catch (RemoteException e) {
473                Log.w(TAG, "Remote exception while authenticating", e);
474                mExecutor.execute(() -> {
475                    callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
476                            getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
477                });
478            }
479        }
480    }
481
482    /**
483     * Private method, see {@link BiometricDialog#authenticate(CancellationSignal, Executor,
484     * BiometricDialog.AuthenticationCallback)}
485     * @param cancel
486     * @param executor
487     * @param callback
488     * @hide
489     */
490    public void authenticate(
491            @NonNull CancellationSignal cancel,
492            @NonNull Bundle bundle,
493            @NonNull @CallbackExecutor Executor executor,
494            @NonNull IBiometricDialogReceiver receiver,
495            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
496        if (cancel == null) {
497            throw new IllegalArgumentException("Must supply a cancellation signal");
498        }
499        if (bundle == null) {
500            throw new IllegalArgumentException("Must supply a bundle");
501        }
502        if (executor == null) {
503            throw new IllegalArgumentException("Must supply an executor");
504        }
505        if (receiver == null) {
506            throw new IllegalArgumentException("Must supply a receiver");
507        }
508        if (callback == null) {
509            throw new IllegalArgumentException("Must supply a calback");
510        }
511        authenticate(mContext.getUserId(), null, cancel, bundle, executor, receiver, callback);
512    }
513
514    /**
515     * Private method, see {@link BiometricDialog#authenticate(BiometricDialog.CryptoObject,
516     * CancellationSignal, Executor, BiometricDialog.AuthenticationCallback)}
517     * @param crypto
518     * @param cancel
519     * @param executor
520     * @param callback
521     * @hide
522     */
523    public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
524            @NonNull CancellationSignal cancel,
525            @NonNull Bundle bundle,
526            @NonNull @CallbackExecutor Executor executor,
527            @NonNull IBiometricDialogReceiver receiver,
528            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
529        if (crypto == null) {
530            throw new IllegalArgumentException("Must supply a crypto object");
531        }
532        if (cancel == null) {
533            throw new IllegalArgumentException("Must supply a cancellation signal");
534        }
535        if (bundle == null) {
536            throw new IllegalArgumentException("Must supply a bundle");
537        }
538        if (executor == null) {
539            throw new IllegalArgumentException("Must supply an executor");
540        }
541        if (receiver == null) {
542            throw new IllegalArgumentException("Must supply a receiver");
543        }
544        if (callback == null) {
545            throw new IllegalArgumentException("Must supply a callback");
546        }
547        authenticate(mContext.getUserId(), crypto, cancel,
548                bundle, executor, receiver, callback);
549    }
550
551    /**
552     * Request fingerprint enrollment. This call warms up the fingerprint hardware
553     * and starts scanning for fingerprints. Progress will be indicated by callbacks to the
554     * {@link EnrollmentCallback} object. It terminates when
555     * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
556     * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
557     * which point the object is no longer valid. The operation can be canceled by using the
558     * provided cancel object.
559     * @param token a unique token provided by a recent creation or verification of device
560     * credentials (e.g. pin, pattern or password).
561     * @param cancel an object that can be used to cancel enrollment
562     * @param flags optional flags
563     * @param userId the user to whom this fingerprint will belong to
564     * @param callback an object to receive enrollment events
565     * @hide
566     */
567    @RequiresPermission(MANAGE_FINGERPRINT)
568    public void enroll(byte [] token, CancellationSignal cancel, int flags,
569            int userId, EnrollmentCallback callback) {
570        if (userId == UserHandle.USER_CURRENT) {
571            userId = getCurrentUserId();
572        }
573        if (callback == null) {
574            throw new IllegalArgumentException("Must supply an enrollment callback");
575        }
576
577        if (cancel != null) {
578            if (cancel.isCanceled()) {
579                Log.w(TAG, "enrollment already canceled");
580                return;
581            } else {
582                cancel.setOnCancelListener(new OnEnrollCancelListener());
583            }
584        }
585
586        if (mService != null) try {
587            mEnrollmentCallback = callback;
588            mService.enroll(mToken, token, userId, mServiceReceiver, flags,
589                    mContext.getOpPackageName());
590        } catch (RemoteException e) {
591            Log.w(TAG, "Remote exception in enroll: ", e);
592            if (callback != null) {
593                // Though this may not be a hardware issue, it will cause apps to give up or try
594                // again later.
595                callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
596                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
597            }
598        }
599    }
600
601    /**
602     * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
603     * existing device credentials (e.g. pin/pattern/password).
604     * @hide
605     */
606    @RequiresPermission(MANAGE_FINGERPRINT)
607    public long preEnroll() {
608        long result = 0;
609        if (mService != null) try {
610            result = mService.preEnroll(mToken);
611        } catch (RemoteException e) {
612            throw e.rethrowFromSystemServer();
613        }
614        return result;
615    }
616
617    /**
618     * Finishes enrollment and cancels the current auth token.
619     * @hide
620     */
621    @RequiresPermission(MANAGE_FINGERPRINT)
622    public int postEnroll() {
623        int result = 0;
624        if (mService != null) try {
625            result = mService.postEnroll(mToken);
626        } catch (RemoteException e) {
627            throw e.rethrowFromSystemServer();
628        }
629        return result;
630    }
631
632    /**
633     * Sets the active user. This is meant to be used to select the current profile for enrollment
634     * to allow separate enrolled fingers for a work profile
635     * @param userId
636     * @hide
637     */
638    @RequiresPermission(MANAGE_FINGERPRINT)
639    public void setActiveUser(int userId) {
640        if (mService != null) try {
641            mService.setActiveUser(userId);
642        } catch (RemoteException e) {
643            throw e.rethrowFromSystemServer();
644        }
645    }
646
647    /**
648     * Remove given fingerprint template from fingerprint hardware and/or protected storage.
649     * @param fp the fingerprint item to remove
650     * @param userId the user who this fingerprint belongs to
651     * @param callback an optional callback to verify that fingerprint templates have been
652     * successfully removed. May be null of no callback is required.
653     *
654     * @hide
655     */
656    @RequiresPermission(MANAGE_FINGERPRINT)
657    public void remove(Fingerprint fp, int userId, RemovalCallback callback) {
658        if (mService != null) try {
659            mRemovalCallback = callback;
660            mRemovalFingerprint = fp;
661            mService.remove(mToken, fp.getFingerId(), fp.getGroupId(), userId, mServiceReceiver);
662        } catch (RemoteException e) {
663            Log.w(TAG, "Remote exception in remove: ", e);
664            if (callback != null) {
665                callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
666                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
667            }
668        }
669    }
670
671    /**
672     * Enumerate all fingerprint templates stored in hardware and/or protected storage.
673     * @param userId the user who this fingerprint belongs to
674     * @param callback an optional callback to verify that fingerprint templates have been
675     * successfully removed. May be null of no callback is required.
676     *
677     * @hide
678     */
679    @RequiresPermission(MANAGE_FINGERPRINT)
680    public void enumerate(int userId, @NonNull EnumerateCallback callback) {
681        if (mService != null) try {
682            mEnumerateCallback = callback;
683            mService.enumerate(mToken, userId, mServiceReceiver);
684        } catch (RemoteException e) {
685            Log.w(TAG, "Remote exception in enumerate: ", e);
686            if (callback != null) {
687                callback.onEnumerateError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
688                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
689            }
690        }
691    }
692
693    /**
694     * Renames the given fingerprint template
695     * @param fpId the fingerprint id
696     * @param userId the user who this fingerprint belongs to
697     * @param newName the new name
698     *
699     * @hide
700     */
701    @RequiresPermission(MANAGE_FINGERPRINT)
702    public void rename(int fpId, int userId, String newName) {
703        // Renames the given fpId
704        if (mService != null) {
705            try {
706                mService.rename(fpId, userId, newName);
707            } catch (RemoteException e) {
708                throw e.rethrowFromSystemServer();
709            }
710        } else {
711            Log.w(TAG, "rename(): Service not connected!");
712        }
713    }
714
715    /**
716     * Obtain the list of enrolled fingerprints templates.
717     * @return list of current fingerprint items
718     *
719     * @hide
720     */
721    @RequiresPermission(USE_FINGERPRINT)
722    public List<Fingerprint> getEnrolledFingerprints(int userId) {
723        if (mService != null) try {
724            return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
725        } catch (RemoteException e) {
726            throw e.rethrowFromSystemServer();
727        }
728        return null;
729    }
730
731    /**
732     * Obtain the list of enrolled fingerprints templates.
733     * @return list of current fingerprint items
734     *
735     * @hide
736     */
737    @RequiresPermission(USE_FINGERPRINT)
738    public List<Fingerprint> getEnrolledFingerprints() {
739        return getEnrolledFingerprints(mContext.getUserId());
740    }
741
742    /**
743     * Determine if there is at least one fingerprint enrolled.
744     *
745     * @return true if at least one fingerprint is enrolled, false otherwise
746     * @deprecated See {@link BiometricDialog} and
747     * {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
748     */
749    @Deprecated
750    @RequiresPermission(USE_FINGERPRINT)
751    public boolean hasEnrolledFingerprints() {
752        if (mService != null) try {
753            return mService.hasEnrolledFingerprints(
754                    mContext.getUserId(), mContext.getOpPackageName());
755        } catch (RemoteException e) {
756            throw e.rethrowFromSystemServer();
757        }
758        return false;
759    }
760
761    /**
762     * @hide
763     */
764    @RequiresPermission(allOf = {
765            USE_FINGERPRINT,
766            INTERACT_ACROSS_USERS})
767    public boolean hasEnrolledFingerprints(int userId) {
768        if (mService != null) try {
769            return mService.hasEnrolledFingerprints(userId, mContext.getOpPackageName());
770        } catch (RemoteException e) {
771            throw e.rethrowFromSystemServer();
772        }
773        return false;
774    }
775
776    /**
777     * Determine if fingerprint hardware is present and functional.
778     *
779     * @return true if hardware is present and functional, false otherwise.
780     * @deprecated See {@link BiometricDialog} and
781     * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
782     */
783    @Deprecated
784    @RequiresPermission(USE_FINGERPRINT)
785    public boolean isHardwareDetected() {
786        if (mService != null) {
787            try {
788                long deviceId = 0; /* TODO: plumb hardware id to FPMS */
789                return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
790            } catch (RemoteException e) {
791                throw e.rethrowFromSystemServer();
792            }
793        } else {
794            Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
795        }
796        return false;
797    }
798
799    /**
800     * Retrieves the authenticator token for binding keys to the lifecycle
801     * of the calling user's fingerprints. Used only by internal clients.
802     *
803     * @hide
804     */
805    public long getAuthenticatorId() {
806        if (mService != null) {
807            try {
808                return mService.getAuthenticatorId(mContext.getOpPackageName());
809            } catch (RemoteException e) {
810                throw e.rethrowFromSystemServer();
811            }
812        } else {
813            Log.w(TAG, "getAuthenticatorId(): Service not connected!");
814        }
815        return 0;
816    }
817
818    /**
819     * Reset the lockout timer when asked to do so by keyguard.
820     *
821     * @param token an opaque token returned by password confirmation.
822     *
823     * @hide
824     */
825    public void resetTimeout(byte[] token) {
826        if (mService != null) {
827            try {
828                mService.resetTimeout(token);
829            } catch (RemoteException e) {
830                throw e.rethrowFromSystemServer();
831            }
832        } else {
833            Log.w(TAG, "resetTimeout(): Service not connected!");
834        }
835    }
836
837    /**
838     * @hide
839     */
840    public void addLockoutResetCallback(final LockoutResetCallback callback) {
841        if (mService != null) {
842            try {
843                final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
844                mService.addLockoutResetCallback(
845                        new IFingerprintServiceLockoutResetCallback.Stub() {
846
847                    @Override
848                    public void onLockoutReset(long deviceId, IRemoteCallback serverCallback)
849                            throws RemoteException {
850                        try {
851                            final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
852                                    PowerManager.PARTIAL_WAKE_LOCK, "lockoutResetCallback");
853                            wakeLock.acquire();
854                            mHandler.post(() -> {
855                                try {
856                                    callback.onLockoutReset();
857                                } finally {
858                                    wakeLock.release();
859                                }
860                            });
861                        } finally {
862                            serverCallback.sendResult(null /* data */);
863                        }
864                    }
865                });
866            } catch (RemoteException e) {
867                throw e.rethrowFromSystemServer();
868            }
869        } else {
870            Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
871        }
872    }
873
874    private class MyHandler extends Handler {
875        private MyHandler(Context context) {
876            super(context.getMainLooper());
877        }
878
879        private MyHandler(Looper looper) {
880            super(looper);
881        }
882
883        @Override
884        public void handleMessage(android.os.Message msg) {
885            switch(msg.what) {
886                case MSG_ENROLL_RESULT:
887                    sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
888                    break;
889                case MSG_ACQUIRED:
890                    sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
891                            msg.arg2 /* vendorCode */);
892                    break;
893                case MSG_AUTHENTICATION_SUCCEEDED:
894                    sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
895                    break;
896                case MSG_AUTHENTICATION_FAILED:
897                    sendAuthenticatedFailed();
898                    break;
899                case MSG_ERROR:
900                    sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
901                            msg.arg2 /* vendorCode */);
902                    break;
903                case MSG_REMOVED:
904                    sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
905                    break;
906                case MSG_ENUMERATED:
907                    sendEnumeratedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
908                            msg.arg2 /* groupId */);
909                    break;
910            }
911        }
912
913        private void sendRemovedResult(Fingerprint fingerprint, int remaining) {
914            if (mRemovalCallback == null) {
915                return;
916            }
917            if (fingerprint == null) {
918                Log.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
919                return;
920            }
921
922            int fingerId = fingerprint.getFingerId();
923            int reqFingerId = mRemovalFingerprint.getFingerId();
924            if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
925                Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
926                return;
927            }
928            int groupId = fingerprint.getGroupId();
929            int reqGroupId = mRemovalFingerprint.getGroupId();
930            if (groupId != reqGroupId) {
931                Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
932                return;
933            }
934
935            mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
936        }
937
938        private void sendEnumeratedResult(long deviceId, int fingerId, int groupId) {
939            if (mEnumerateCallback != null) {
940                mEnumerateCallback.onEnumerate(new Fingerprint(null, groupId, fingerId, deviceId));
941            }
942        }
943
944        private void sendEnrollResult(Fingerprint fp, int remaining) {
945            if (mEnrollmentCallback != null) {
946                mEnrollmentCallback.onEnrollmentProgress(remaining);
947            }
948        }
949    };
950
951    private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
952        if (mAuthenticationCallback != null) {
953            final BiometricAuthenticator.AuthenticationResult result =
954                    new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
955            mAuthenticationCallback.onAuthenticationSucceeded(result);
956        }
957    }
958
959    private void sendAuthenticatedFailed() {
960        if (mAuthenticationCallback != null) {
961            mAuthenticationCallback.onAuthenticationFailed();
962        }
963    }
964
965    private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
966        if (mAuthenticationCallback != null) {
967            mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
968        }
969        final String msg = getAcquiredString(acquireInfo, vendorCode);
970        if (msg == null) {
971            return;
972        }
973        // emulate HAL 2.1 behavior and send real acquiredInfo
974        final int clientInfo = acquireInfo == FINGERPRINT_ACQUIRED_VENDOR
975                ? (vendorCode + FINGERPRINT_ACQUIRED_VENDOR_BASE) : acquireInfo;
976        if (mEnrollmentCallback != null) {
977            mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
978        } else if (mAuthenticationCallback != null) {
979            mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
980        }
981    }
982
983    private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
984        // emulate HAL 2.1 behavior and send real errMsgId
985        final int clientErrMsgId = errMsgId == FINGERPRINT_ERROR_VENDOR
986                ? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId;
987        if (mEnrollmentCallback != null) {
988            mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
989                    getErrorString(errMsgId, vendorCode));
990        } else if (mAuthenticationCallback != null) {
991            mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
992                    getErrorString(errMsgId, vendorCode));
993        } else if (mRemovalCallback != null) {
994            mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
995                    getErrorString(errMsgId, vendorCode));
996        } else if (mEnumerateCallback != null) {
997            mEnumerateCallback.onEnumerateError(clientErrMsgId,
998                    getErrorString(errMsgId, vendorCode));
999        }
1000    }
1001
1002    /**
1003     * @hide
1004     */
1005    public FingerprintManager(Context context, IFingerprintService service) {
1006        mContext = context;
1007        mService = service;
1008        if (mService == null) {
1009            Slog.v(TAG, "FingerprintManagerService was null");
1010        }
1011        mHandler = new MyHandler(context);
1012    }
1013
1014    private int getCurrentUserId() {
1015        try {
1016            return ActivityManager.getService().getCurrentUser().id;
1017        } catch (RemoteException e) {
1018            throw e.rethrowFromSystemServer();
1019        }
1020    }
1021
1022    private void cancelEnrollment() {
1023        if (mService != null) try {
1024            mService.cancelEnrollment(mToken);
1025        } catch (RemoteException e) {
1026            throw e.rethrowFromSystemServer();
1027        }
1028    }
1029
1030    private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
1031        if (mService != null) try {
1032            mService.cancelAuthentication(mToken, mContext.getOpPackageName());
1033        } catch (RemoteException e) {
1034            throw e.rethrowFromSystemServer();
1035        }
1036    }
1037
1038    /**
1039     * @hide
1040     */
1041    public String getErrorString(int errMsg, int vendorCode) {
1042        switch (errMsg) {
1043            case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
1044                return mContext.getString(
1045                    com.android.internal.R.string.fingerprint_error_unable_to_process);
1046            case FINGERPRINT_ERROR_HW_UNAVAILABLE:
1047                return mContext.getString(
1048                    com.android.internal.R.string.fingerprint_error_hw_not_available);
1049            case FINGERPRINT_ERROR_NO_SPACE:
1050                return mContext.getString(
1051                    com.android.internal.R.string.fingerprint_error_no_space);
1052            case FINGERPRINT_ERROR_TIMEOUT:
1053                return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout);
1054            case FINGERPRINT_ERROR_CANCELED:
1055                return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
1056            case FINGERPRINT_ERROR_LOCKOUT:
1057                return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
1058            case FINGERPRINT_ERROR_LOCKOUT_PERMANENT:
1059                return mContext.getString(
1060                        com.android.internal.R.string.fingerprint_error_lockout_permanent);
1061            case FINGERPRINT_ERROR_USER_CANCELED:
1062                return mContext.getString(
1063                        com.android.internal.R.string.fingerprint_error_user_canceled);
1064            case FINGERPRINT_ERROR_NO_FINGERPRINTS:
1065                return mContext.getString(
1066                        com.android.internal.R.string.fingerprint_error_no_fingerprints);
1067            case FINGERPRINT_ERROR_HW_NOT_PRESENT:
1068                return mContext.getString(
1069                        com.android.internal.R.string.fingerprint_error_hw_not_present);
1070            case FINGERPRINT_ERROR_VENDOR: {
1071                    String[] msgArray = mContext.getResources().getStringArray(
1072                            com.android.internal.R.array.fingerprint_error_vendor);
1073                    if (vendorCode < msgArray.length) {
1074                        return msgArray[vendorCode];
1075                    }
1076                }
1077        }
1078        Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
1079        return null;
1080    }
1081
1082    /**
1083     * @hide
1084     */
1085    public String getAcquiredString(int acquireInfo, int vendorCode) {
1086        switch (acquireInfo) {
1087            case FINGERPRINT_ACQUIRED_GOOD:
1088                return null;
1089            case FINGERPRINT_ACQUIRED_PARTIAL:
1090                return mContext.getString(
1091                    com.android.internal.R.string.fingerprint_acquired_partial);
1092            case FINGERPRINT_ACQUIRED_INSUFFICIENT:
1093                return mContext.getString(
1094                    com.android.internal.R.string.fingerprint_acquired_insufficient);
1095            case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
1096                return mContext.getString(
1097                    com.android.internal.R.string.fingerprint_acquired_imager_dirty);
1098            case FINGERPRINT_ACQUIRED_TOO_SLOW:
1099                return mContext.getString(
1100                    com.android.internal.R.string.fingerprint_acquired_too_slow);
1101            case FINGERPRINT_ACQUIRED_TOO_FAST:
1102                return mContext.getString(
1103                    com.android.internal.R.string.fingerprint_acquired_too_fast);
1104            case FINGERPRINT_ACQUIRED_VENDOR: {
1105                    String[] msgArray = mContext.getResources().getStringArray(
1106                            com.android.internal.R.array.fingerprint_acquired_vendor);
1107                    if (vendorCode < msgArray.length) {
1108                        return msgArray[vendorCode];
1109                    }
1110                }
1111        }
1112        Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
1113        return null;
1114    }
1115
1116    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
1117
1118        @Override // binder call
1119        public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
1120            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
1121                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
1122        }
1123
1124        @Override // binder call
1125        public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
1126            if (mExecutor != null) {
1127                mExecutor.execute(() -> {
1128                    sendAcquiredResult(deviceId, acquireInfo, vendorCode);
1129                });
1130            } else {
1131                mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode,
1132                        deviceId).sendToTarget();
1133            }
1134        }
1135
1136        @Override // binder call
1137        public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
1138            if (mExecutor != null) {
1139                mExecutor.execute(() -> {
1140                    sendAuthenticatedSucceeded(fp, userId);
1141                });
1142            } else {
1143                mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
1144            }
1145        }
1146
1147        @Override // binder call
1148        public void onAuthenticationFailed(long deviceId) {
1149            if (mExecutor != null) {
1150                mExecutor.execute(() -> {
1151                    sendAuthenticatedFailed();
1152                });
1153            } else {
1154                mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
1155            }
1156        }
1157
1158        @Override // binder call
1159        public void onError(long deviceId, int error, int vendorCode) {
1160            if (mExecutor != null) {
1161                // BiometricDialog case
1162                if (error == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) {
1163                    // User tapped somewhere to cancel, the biometric dialog is already dismissed.
1164                    mExecutor.execute(() -> {
1165                        sendErrorResult(deviceId, error, vendorCode);
1166                    });
1167                } else {
1168                    // User got an error that needs to be displayed on the dialog, post a delayed
1169                    // runnable on the FingerprintManager handler that sends the error message after
1170                    // FingerprintDialog.HIDE_DIALOG_DELAY to send the error to the application.
1171                    mHandler.postDelayed(() -> {
1172                        mExecutor.execute(() -> {
1173                            sendErrorResult(deviceId, error, vendorCode);
1174                        });
1175                    }, BiometricDialog.HIDE_DIALOG_DELAY);
1176                }
1177            } else {
1178                mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
1179            }
1180        }
1181
1182        @Override // binder call
1183        public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
1184            mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
1185                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
1186        }
1187
1188        @Override // binder call
1189        public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
1190            // TODO: propagate remaining
1191            mHandler.obtainMessage(MSG_ENUMERATED, fingerId, groupId, deviceId).sendToTarget();
1192        }
1193    };
1194
1195}
1196