FingerprintManagerCompat.java revision d019e1cc3c5cbd5af21cd6bbf4e6d4593a5a6c65
1/*
2 * Copyright (C) 2015 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.support.v4.hardware.fingerprint;
18
19import android.content.Context;
20import android.os.Build;
21import android.support.annotation.NonNull;
22import android.support.annotation.Nullable;
23import android.support.v4.os.CancellationSignal;
24
25import java.security.Signature;
26
27import javax.crypto.Cipher;
28
29/**
30 * A class that coordinates access to the fingerprint hardware.
31 * <p>
32 * On platforms before MNC, this class behaves as there would be no fingerprint hardware available.
33 */
34public class FingerprintManagerCompat {
35
36    private Context mContext;
37
38    /** Get a {@link FingerprintManagerCompat} instance for a provided context. */
39    public static FingerprintManagerCompat from(Context context) {
40        return new FingerprintManagerCompat(context);
41    }
42
43    private FingerprintManagerCompat(Context context) {
44        mContext = context;
45    }
46
47    static final FingerprintManagerCompatImpl IMPL;
48    static {
49        final int version = Build.VERSION.SDK_INT;
50        // STOPSHIP: Remove "MNC" check once the API's are final for MNC
51        if (version >= 23 || "MNC".equals(Build.VERSION.CODENAME)) {
52            IMPL = new Api23FingerprintManagerCompatImpl();
53        } else {
54            IMPL = new LegacyFingerprintManagerCompatImpl();
55        }
56    }
57
58    /**
59     * Determine if there is at least one fingerprint enrolled.
60     *
61     * @return true if at least one fingerprint is enrolled, false otherwise
62     */
63    public boolean hasEnrolledFingerprints() {
64        return IMPL.hasEnrolledFingerprints(mContext);
65    }
66
67    /**
68     * Determine if fingerprint hardware is present and functional.
69     *
70     * @return true if hardware is present and functional, false otherwise.
71     */
72    public boolean isHardwareDetected() {
73        return IMPL.isHardwareDetected(mContext);
74    }
75
76    /**
77     * Request authentication of a crypto object. This call warms up the fingerprint hardware
78     * and starts scanning for a fingerprint. It terminates when
79     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
80     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
81     * which point the object is no longer valid. The operation can be canceled by using the
82     * provided cancel object.
83     *
84     * @param crypto object associated with the call or null if none required.
85     * @param cancel an object that can be used to cancel authentication
86     * @param callback an object to receive authentication events
87     * @param flags optional flags; should be 0
88     */
89    public void authenticate(@Nullable CryptoObject crypto,
90            @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback,
91            int flags) {
92        IMPL.authenticate(mContext, crypto, cancel, callback, flags);
93    }
94
95    /**
96     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
97     * framework supports {@link Signature} and {@link Cipher} objects.
98     */
99    public static class CryptoObject {
100
101        private final Signature mSignature;
102        private final Cipher mCipher;
103
104        public CryptoObject(Signature signature) {
105            mSignature = signature;
106            mCipher = null;
107        }
108
109        public CryptoObject(Cipher cipher) {
110            mCipher = cipher;
111            mSignature = null;
112        }
113
114        /**
115         * Get {@link Signature} object.
116         * @return {@link Signature} object or null if this doesn't contain one.
117         */
118        public Signature getSignature() { return mSignature; }
119
120        /**
121         * Get {@link Cipher} object.
122         * @return {@link Cipher} object or null if this doesn't contain one.
123         */
124        public Cipher getCipher() { return mCipher; }
125    }
126
127    /**
128     * Container for callback data from {@link FingerprintManagerCompat#authenticate(CryptoObject,
129     *     CancellationSignal, AuthenticationCallback, int)}.
130     */
131    public static final class AuthenticationResult {
132        private CryptoObject mCryptoObject;
133
134        public AuthenticationResult(CryptoObject crypto) {
135            mCryptoObject = crypto;
136        }
137
138        /**
139         * Obtain the crypto object associated with this transaction
140         * @return crypto object provided to {@link FingerprintManagerCompat#authenticate(
141         *         CryptoObject, CancellationSignal, AuthenticationCallback, int)}.
142         */
143        public CryptoObject getCryptoObject() { return mCryptoObject; }
144    }
145
146    /**
147     * Callback structure provided to {@link FingerprintManagerCompat#authenticate(CryptoObject,
148     * CancellationSignal, AuthenticationCallback, int)}. Users of {@link
149     * FingerprintManagerCompat#authenticate(CryptoObject, CancellationSignal,
150     * AuthenticationCallback, int) } must provide an implementation of this for listening to
151     * fingerprint events.
152     */
153    public static abstract class AuthenticationCallback {
154        /**
155         * Called when an unrecoverable error has been encountered and the operation is complete.
156         * No further callbacks will be made on this object.
157         * @param errMsgId An integer identifying the error message
158         * @param errString A human-readable error string that can be shown in UI
159         */
160        public void onAuthenticationError(int errMsgId, CharSequence errString) { }
161
162        /**
163         * Called when a recoverable error has been encountered during authentication. The help
164         * string is provided to give the user guidance for what went wrong, such as
165         * "Sensor dirty, please clean it."
166         * @param helpMsgId An integer identifying the error message
167         * @param helpString A human-readable string that can be shown in UI
168         */
169        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
170
171        /**
172         * Called when a fingerprint is recognized.
173         * @param result An object containing authentication-related data
174         */
175        public void onAuthenticationSucceeded(AuthenticationResult result) { }
176
177        /**
178         * Called when a fingerprint is valid but not recognized.
179         */
180        public void onAuthenticationFailed() { }
181    }
182
183    private interface FingerprintManagerCompatImpl {
184        boolean hasEnrolledFingerprints(Context context);
185        boolean isHardwareDetected(Context context);
186        void authenticate(Context context, CryptoObject crypto, CancellationSignal cancel,
187                AuthenticationCallback callback, int flags);
188    }
189
190    private static class LegacyFingerprintManagerCompatImpl
191            implements FingerprintManagerCompatImpl {
192
193        public LegacyFingerprintManagerCompatImpl() {
194        }
195
196        @Override
197        public boolean hasEnrolledFingerprints(Context context) {
198            return false;
199        }
200
201        @Override
202        public boolean isHardwareDetected(Context context) {
203            return false;
204        }
205
206        @Override
207        public void authenticate(Context context, CryptoObject crypto, CancellationSignal cancel,
208                AuthenticationCallback callback, int flags) {
209            // TODO: Figure out behavior when there is no fingerprint hardware available
210        }
211    }
212
213    private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl {
214
215        public Api23FingerprintManagerCompatImpl() {
216        }
217
218        @Override
219        public boolean hasEnrolledFingerprints(Context context) {
220            return FingerprintManagerCompatApi23.hasEnrolledFingerprints(context);
221        }
222
223        @Override
224        public boolean isHardwareDetected(Context context) {
225            return FingerprintManagerCompatApi23.isHardwareDetected(context);
226        }
227
228        @Override
229        public void authenticate(Context context, CryptoObject crypto, CancellationSignal cancel,
230                AuthenticationCallback callback, int flags) {
231            FingerprintManagerCompatApi23.authenticate(context, wrapCryptoObject(crypto),
232                    cancel != null ? cancel.getCancellationSignalObject() : null,
233                    wrapCallback(callback), flags);
234        }
235
236        private static FingerprintManagerCompatApi23.CryptoObject wrapCryptoObject(
237                CryptoObject cryptoObject) {
238            if (cryptoObject.getCipher() != null) {
239                return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getCipher());
240            } else {
241                return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getSignature());
242            }
243        }
244
245        private static CryptoObject unwrapCryptoObject(
246                FingerprintManagerCompatApi23.CryptoObject cryptoObject) {
247            if (cryptoObject.getCipher() != null) {
248                return new CryptoObject(cryptoObject.getCipher());
249            } else {
250                return new CryptoObject(cryptoObject.getSignature());
251            }
252        }
253
254        private static FingerprintManagerCompatApi23.AuthenticationCallback wrapCallback(
255                final AuthenticationCallback callback) {
256            return new FingerprintManagerCompatApi23.AuthenticationCallback() {
257                @Override
258                public void onAuthenticationError(int errMsgId, CharSequence errString) {
259                    callback.onAuthenticationError(errMsgId, errString);
260                }
261
262                @Override
263                public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
264                    callback.onAuthenticationHelp(helpMsgId, helpString);
265                }
266
267                @Override
268                public void onAuthenticationSucceeded(
269                        FingerprintManagerCompatApi23.AuthenticationResultInternal result) {
270                    callback.onAuthenticationSucceeded(new AuthenticationResult(
271                            unwrapCryptoObject(result.getCryptoObject())));
272                }
273
274                @Override
275                public void onAuthenticationFailed() {
276                    callback.onAuthenticationFailed();
277                }
278            };
279        }
280    }
281}
282