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.os.Handler; 22import android.support.annotation.NonNull; 23import android.support.annotation.Nullable; 24import android.support.annotation.RequiresApi; 25import android.support.v4.os.CancellationSignal; 26 27import java.security.Signature; 28 29import javax.crypto.Cipher; 30import javax.crypto.Mac; 31 32/** 33 * A class that coordinates access to the fingerprint hardware. 34 * <p> 35 * On platforms before {@link android.os.Build.VERSION_CODES#M}, this class behaves as there would 36 * be no fingerprint hardware available. 37 */ 38public final class FingerprintManagerCompat { 39 40 private Context mContext; 41 42 /** Get a {@link FingerprintManagerCompat} instance for a provided context. */ 43 public static FingerprintManagerCompat from(Context context) { 44 return new FingerprintManagerCompat(context); 45 } 46 47 private FingerprintManagerCompat(Context context) { 48 mContext = context; 49 } 50 51 static final FingerprintManagerCompatImpl IMPL; 52 static { 53 if (Build.VERSION.SDK_INT >= 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 */ 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 */ 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 */ 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 108 public CryptoObject(Signature signature) { 109 mSignature = signature; 110 mCipher = null; 111 mMac = null; 112 113 } 114 115 public CryptoObject(Cipher cipher) { 116 mCipher = cipher; 117 mSignature = null; 118 mMac = null; 119 } 120 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 194 public void onAuthenticationSucceeded(AuthenticationResult result) { } 195 196 /** 197 * Called when a fingerprint is valid but not recognized. 198 */ 199 public void onAuthenticationFailed() { } 200 } 201 202 private interface FingerprintManagerCompatImpl { 203 boolean hasEnrolledFingerprints(Context context); 204 boolean isHardwareDetected(Context context); 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 212 public LegacyFingerprintManagerCompatImpl() { 213 } 214 215 @Override 216 public boolean hasEnrolledFingerprints(Context context) { 217 return false; 218 } 219 220 @Override 221 public boolean isHardwareDetected(Context context) { 222 return false; 223 } 224 225 @Override 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 @RequiresApi(23) 233 private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl { 234 235 public Api23FingerprintManagerCompatImpl() { 236 } 237 238 @Override 239 public boolean hasEnrolledFingerprints(Context context) { 240 return FingerprintManagerCompatApi23.hasEnrolledFingerprints(context); 241 } 242 243 @Override 244 public boolean isHardwareDetected(Context context) { 245 return FingerprintManagerCompatApi23.isHardwareDetected(context); 246 } 247 248 @Override 249 public void authenticate(Context context, CryptoObject crypto, int flags, 250 CancellationSignal cancel, AuthenticationCallback callback, Handler handler) { 251 FingerprintManagerCompatApi23.authenticate(context, wrapCryptoObject(crypto), flags, 252 cancel != null ? cancel.getCancellationSignalObject() : null, 253 wrapCallback(callback), handler); 254 } 255 256 private static FingerprintManagerCompatApi23.CryptoObject wrapCryptoObject( 257 CryptoObject cryptoObject) { 258 if (cryptoObject == null) { 259 return null; 260 } else if (cryptoObject.getCipher() != null) { 261 return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getCipher()); 262 } else if (cryptoObject.getSignature() != null) { 263 return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getSignature()); 264 } else if (cryptoObject.getMac() != null) { 265 return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getMac()); 266 } else { 267 return null; 268 } 269 } 270 271 static CryptoObject unwrapCryptoObject( 272 FingerprintManagerCompatApi23.CryptoObject cryptoObject) { 273 if (cryptoObject == null) { 274 return null; 275 } else if (cryptoObject.getCipher() != null) { 276 return new CryptoObject(cryptoObject.getCipher()); 277 } else if (cryptoObject.getSignature() != null) { 278 return new CryptoObject(cryptoObject.getSignature()); 279 } else if (cryptoObject.getMac() != null) { 280 return new CryptoObject(cryptoObject.getMac()); 281 } else { 282 return null; 283 } 284 } 285 286 private static FingerprintManagerCompatApi23.AuthenticationCallback wrapCallback( 287 final AuthenticationCallback callback) { 288 return new FingerprintManagerCompatApi23.AuthenticationCallback() { 289 @Override 290 public void onAuthenticationError(int errMsgId, CharSequence errString) { 291 callback.onAuthenticationError(errMsgId, errString); 292 } 293 294 @Override 295 public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { 296 callback.onAuthenticationHelp(helpMsgId, helpString); 297 } 298 299 @Override 300 public void onAuthenticationSucceeded( 301 FingerprintManagerCompatApi23.AuthenticationResultInternal result) { 302 callback.onAuthenticationSucceeded(new AuthenticationResult( 303 unwrapCryptoObject(result.getCryptoObject()))); 304 } 305 306 @Override 307 public void onAuthenticationFailed() { 308 callback.onAuthenticationFailed(); 309 } 310 }; 311 } 312 } 313} 314