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