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