· 7 years ago · May 16, 2018, 09:00 AM
1public class FingerprintLogin extends UseCase<FingerprintLogin.FingerprintResult, FingerprintLogin.Params> {
2 private final Context context;
3 private final AccountsDataSource accountLocalDataSource;
4 private CancellationSignal cancellationSignal;
5 private KeyStore mKeyStore;
6 private Cipher mCipher;
7
8
9 @Inject
10 FingerprintLogin(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread, Context context, AccountsDataSource accountLocalDataSource) {
11 super(threadExecutor, postExecutionThread);
12 this.context = context;
13 this.accountLocalDataSource = accountLocalDataSource;
14 }
15
16 @Override
17 @DebugLog
18 protected Observable<FingerprintResult> buildUseCaseObservable(Params params) {
19 return Observable.create(subscriber -> {
20 if (accountLocalDataSource.init(params.activity)) {
21 if (params.user != null)
22 accountLocalDataSource.updateAccountRx(params.user);
23 }
24
25 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
26 KeyguardManager mKeyguardManager = context.getSystemService(KeyguardManager.class);
27 android.hardware.fingerprint.FingerprintManager mFingerprintManager = context.getSystemService(android.hardware.fingerprint.FingerprintManager.class);
28
29 SpassFingerprint.IdentifyListener identifyListener = new SpassFingerprint.IdentifyListener() {
30 @Override
31 @DebugLog
32 public void onFinished(int eventStatus) {
33 if (!subscriber.isDisposed()) {
34 switch (eventStatus) {
35 case SpassFingerprint.STATUS_AUTHENTIFICATION_SUCCESS:
36 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationSucceeded));
37 break;
38 case SpassFingerprint.STATUS_AUTHENTIFICATION_FAILED:
39 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationFailed));
40 break;
41 }
42 }
43
44 }
45
46 @Override
47 @DebugLog
48 public void onReady() {
49
50 }
51
52 @Override
53 @DebugLog
54 public void onStarted() {
55 }
56
57 @Override
58 @DebugLog
59 public void onCompleted() {
60
61 }
62 };
63
64 android.hardware.fingerprint.FingerprintManager.AuthenticationCallback authenticationCallback = new android.hardware.fingerprint.FingerprintManager.AuthenticationCallback() {
65 @Override
66 @DebugLog
67 public void onAuthenticationError(int errMsgId, CharSequence errString) {
68 if (!subscriber.isDisposed()) {
69 if (errMsgId == android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_LOCKOUT ||
70 errMsgId == android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_TIMEOUT) {
71 try {
72 if (accountLocalDataSource.init(params.activity))
73 accountLocalDataSource.removeAccounts();
74 } catch (AccountManagerNotInitException e) {
75 subscriber.onError(e);
76 }
77 subscriber.onNext(new FingerprintResult(FingerprintStatus.onSignOut));
78 }
79 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationError, errString.toString()));
80 }
81
82 }
83
84 @Override
85 @DebugLog
86 public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
87 if (!subscriber.isDisposed()) {
88 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationHelp, helpString.toString()));
89 }
90 }
91
92 @Override
93 @DebugLog
94 public void onAuthenticationFailed() {
95 if (!subscriber.isDisposed()) {
96 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationFailed));
97 }
98 }
99
100 @Override
101 @DebugLog
102 public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintManager.AuthenticationResult result) {
103 if (!subscriber.isDisposed()) {
104 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationSucceeded));
105 subscriber.onComplete();
106 }
107 stopListening();
108 }
109
110 };
111
112 if (!mKeyguardManager.isKeyguardSecure()) {
113 if (!subscriber.isDisposed()) {
114 subscriber.onNext(new FingerprintResult(FingerprintStatus.onLockScreenNotEnabled));
115 }
116 }
117
118 if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
119 if (!subscriber.isDisposed()) {
120 subscriber.onNext(new FingerprintResult(FingerprintStatus.onFingerprintNotEnabledPermission));
121
122 }
123 }
124
125 if (!mFingerprintManager.isHardwareDetected()) {
126 if (!initializeSpassFingerprint(context, identifyListener)) {
127 if (!mFingerprintManager.hasEnrolledFingerprints()) {
128 if (!subscriber.isDisposed()) {
129 subscriber.onNext(new FingerprintResult(FingerprintStatus.onNoFingerprintsAreRegistered));
130
131 }
132 }
133 }
134 } else {
135
136 if (!subscriber.isDisposed()) {
137 subscriber.onNext(new FingerprintResult(FingerprintStatus.onFingerpintInit));
138 }
139
140 if (params.user != null && params.user.getLowerJidString() != null) {
141 cancellationSignal = new CancellationSignal();
142 if (fingerKey(params.user.getLowerJidString())) {
143 mFingerprintManager.authenticate(new android.hardware.fingerprint.FingerprintManager.CryptoObject(mCipher), cancellationSignal, 0, authenticationCallback, null);
144 if (!subscriber.isDisposed()) {
145 subscriber.onNext(new FingerprintResult(FingerprintStatus.onStartListening));
146 }
147 } else {
148 if (!subscriber.isDisposed()) {
149 subscriber.onNext(new FingerprintResult(FingerprintStatus.onAuthenticationFailed));
150 subscriber.onComplete();
151 }
152 }
153 } else {
154 if (!subscriber.isDisposed()) {
155 subscriber.onComplete();
156 }
157 }
158 }
159 } else {
160 if (!subscriber.isDisposed()) {
161 subscriber.onNext(new FingerprintResult(FingerprintStatus.onNoFingerprintsAreRegistered));
162 subscriber.onComplete();
163 }
164 }
165 });
166 }
167
168 @DebugLog
169 private boolean initializeSpassFingerprint(Context context, SpassFingerprint.IdentifyListener mSpassIdentifyListener) {
170 Spass mSpass = new Spass();
171 try {
172 mSpass.initialize(context);
173 if (mSpass.isFeatureEnabled(Spass.DEVICE_FINGERPRINT)) {
174 SpassFingerprint mSpassFingerprint = new SpassFingerprint(context);
175 mSpassFingerprint.startIdentifyWithDialog(context, mSpassIdentifyListener, false);
176 return true;
177 } else {
178 return false;
179 }
180 } catch (SsdkUnsupportedException e) {
181 return false;
182 }
183 }
184
185
186 /**
187 * Checks existence of the fingerKey key by its name.
188 *
189 * @param keyName the fingerprint key name
190 * @return true if fingerprint key found, else false
191 */
192 @DebugLog
193 private boolean fingerKey(String keyName) {
194 if (Build.VERSION.SDK_INT < 23) {
195 return false;
196 }
197 try {
198 mKeyStore = KeyStore.getInstance("AndroidKeyStore");
199 KeyGenerator mKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
200
201 //Initialize the KeyGenerator
202 mKeyGenerator.init(new
203 //Specify the operation(s) this key can be used for//
204 KeyGenParameterSpec.Builder(keyName,
205 KeyProperties.PURPOSE_ENCRYPT |
206 KeyProperties.PURPOSE_DECRYPT)
207 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
208 //Configure this key so that the user has to confirm their identity with a fingerprint each time they want to use it//
209 .setUserAuthenticationRequired(true)
210 .setEncryptionPaddings(
211 KeyProperties.ENCRYPTION_PADDING_PKCS7)
212 .build());
213
214 mKeyGenerator.generateKey();
215
216 mCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
217 + KeyProperties.BLOCK_MODE_CBC + "/"
218 + KeyProperties.ENCRYPTION_PADDING_PKCS7);
219 mKeyStore.load(null);
220 return initCipher(mCipher, keyName);
221 } catch (NoSuchAlgorithmException | NoSuchProviderException | KeyStoreException | CertificateException | NoSuchPaddingException | InvalidAlgorithmParameterException | IOException e) {
222 return false;
223 }
224 }
225
226
227 @DebugLog
228 private boolean initCipher(Cipher cipher, String keyName) {
229 if (Build.VERSION.SDK_INT >= 23) {
230 try {
231 mKeyStore.load(null);
232 SecretKey key = (SecretKey) mKeyStore.getKey(keyName, null);
233 cipher.init(Cipher.ENCRYPT_MODE, key);
234 return true;
235 } catch (KeyPermanentlyInvalidatedException e) {
236 return false;
237 } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
238 | NoSuchAlgorithmException | InvalidKeyException e) {
239 throw new RuntimeException("Failed to start Cipher", e);
240 }
241 }
242 return false;
243 }
244
245 @DebugLog
246 private void stopListening() {
247 if (cancellationSignal != null) {
248 cancellationSignal.cancel();
249 cancellationSignal = null;
250 }
251 }
252
253
254 public enum FingerprintStatus {
255 onAuthenticationFailed,
256 onAuthenticationSucceeded,
257 onAuthenticationError,
258 onStartListening,
259 onSignOut,
260 onAuthenticationHelp,
261 onFingerpintInit,
262 onLockScreenNotEnabled,
263 onFingerprintNotEnabledPermission,
264 onNoFingerprintsAreRegistered,
265 onFingerprintNoHardwareDetected,
266 onFingerprintNotSet,
267 onFinish
268 }
269
270 public static final class Params {
271 private final User user;
272 private final BaseActivity activity;
273
274 Params(BaseActivity activity) {
275 this.user = null;
276 this.activity = activity;
277 }
278
279 Params(User user, BaseActivity activity) {
280 this.user = user;
281 this.activity = activity;
282 }
283
284 public @DebugLog
285 static Params forFingerpintLogin(BaseActivity activity) {
286 return new Params(activity);
287 }
288
289 public @DebugLog
290 static Params forFingerpintLogin(User user, BaseActivity activity) {
291 return new Params(user, activity);
292 }
293 }
294
295 public class FingerprintResult {
296 FingerprintStatus status;
297 String message;
298
299 @DebugLog
300 public FingerprintResult(FingerprintStatus status, String message) {
301 this.status = status;
302 this.message = message;
303 }
304
305 @DebugLog
306 public FingerprintResult(FingerprintStatus status) {
307 this.status = status;
308 }
309
310 @DebugLog
311 public FingerprintStatus getStatus() {
312 return status;
313 }
314
315 @DebugLog
316 public void setStatus(FingerprintStatus status) {
317 this.status = status;
318 }
319
320 @DebugLog
321 public String getMessage() {
322 return message;
323 }
324
325 @DebugLog
326 public void setMessage(String message) {
327 this.message = message;
328 }
329 }
330}