From 33414fdcc6b3723a33b60c7ee792988cb0e71d7b Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sun, 18 Aug 2019 14:59:57 +0200 Subject: [PATCH] Fix fingerprint APIs on android 9 and switch to android X --- app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 4 +- .../java/com/termux/api/DialogActivity.java | 10 +- .../java/com/termux/api/FingerprintAPI.java | 125 ++++++------------ .../java/com/termux/api/JobSchedulerAPI.java | 2 +- .../main/java/com/termux/api/KeystoreAPI.java | 2 +- app/src/main/res/values/styles.xml | 14 +- gradle.properties | 2 + 8 files changed, 63 insertions(+), 99 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0c8b3b1..70e632d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,7 +25,8 @@ android { } dependencies { - implementation 'com.android.support:design:28.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.biometric:biometric:1.0.0-alpha04' } task versionName { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d06c059..029fbbd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ - + @@ -48,7 +48,7 @@ tools:ignore="GoogleAppIndexingWarning"> - diff --git a/app/src/main/java/com/termux/api/DialogActivity.java b/app/src/main/java/com/termux/api/DialogActivity.java index a07df4e..2cabc15 100644 --- a/app/src/main/java/com/termux/api/DialogActivity.java +++ b/app/src/main/java/com/termux/api/DialogActivity.java @@ -14,11 +14,11 @@ import android.os.Bundle; import android.speech.RecognitionListener; import android.speech.RecognizerIntent; import android.speech.SpeechRecognizer; -import android.support.annotation.NonNull; -import android.support.design.widget.BottomSheetDialog; -import android.support.design.widget.BottomSheetDialogFragment; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; +import androidx.core.widget.NestedScrollView; +import androidx.appcompat.app.AppCompatActivity; import android.text.InputType; import android.util.JsonWriter; import android.view.View; diff --git a/app/src/main/java/com/termux/api/FingerprintAPI.java b/app/src/main/java/com/termux/api/FingerprintAPI.java index ab0cc2c..f0bc6ae 100644 --- a/app/src/main/java/com/termux/api/FingerprintAPI.java +++ b/app/src/main/java/com/termux/api/FingerprintAPI.java @@ -1,30 +1,30 @@ package com.termux.api; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; import android.util.JsonWriter; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.biometric.BiometricConstants; +import androidx.biometric.BiometricPrompt; +import androidx.core.hardware.fingerprint.FingerprintManagerCompat; +import androidx.fragment.app.FragmentActivity; + import com.termux.api.util.ResultReturner; import com.termux.api.util.TermuxApiLogger; -import java.security.KeyStore; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; /** * This API allows users to use device fingerprint sensor as an authentication mechanism @@ -71,12 +71,12 @@ public class FingerprintAPI { resetFingerprintResult(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - FingerprintManager fingerprintManager = (FingerprintManager)context.getSystemService(Context.FINGERPRINT_SERVICE); - + FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(context); // make sure we have a valid fingerprint sensor before attempting to launch Fingerprint activity - if (validateFingerprintSensor(context, fingerprintManager)) { + if (validateFingerprintSensor(context, fingerprintManagerCompat)) { Intent fingerprintIntent = new Intent(context, FingerprintActivity.class); fingerprintIntent.putExtras(intent.getExtras()); + fingerprintIntent.setFlags(FLAG_ACTIVITY_NEW_TASK); context.startActivity(fingerprintIntent); } else { postFingerprintResult(context, intent, fingerprintResult); @@ -120,16 +120,16 @@ public class FingerprintAPI { * Ensure that we have a fingerprint sensor and that the user has already enrolled fingerprints */ @TargetApi(Build.VERSION_CODES.M) - protected static boolean validateFingerprintSensor(Context context, FingerprintManager fingerprintManager) { + protected static boolean validateFingerprintSensor(Context context, FingerprintManagerCompat fingerprintManagerCompat) { boolean result = true; - if (!fingerprintManager.isHardwareDetected()) { + if (!fingerprintManagerCompat.isHardwareDetected()) { Toast.makeText(context, "No fingerprint scanner found!", Toast.LENGTH_SHORT).show(); appendFingerprintError(ERROR_NO_HARDWARE); result = false; } - if (!fingerprintManager.hasEnrolledFingerprints()) { + if (!fingerprintManagerCompat.hasEnrolledFingerprints()) { Toast.makeText(context, "No fingerprints enrolled", Toast.LENGTH_SHORT).show(); appendFingerprintError(ERROR_NO_ENROLLED_FINGERPRINTS); result = false; @@ -143,48 +143,30 @@ public class FingerprintAPI { * Activity that is necessary for authenticating w/ fingerprint sensor */ @TargetApi(Build.VERSION_CODES.M) - public static class FingerprintActivity extends Activity { + public static class FingerprintActivity extends FragmentActivity{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handleFingerprint(); - finish(); } /** * Handle setup and listening of fingerprint sensor */ protected void handleFingerprint() { - FingerprintManager fingerprintManager = (FingerprintManager)getSystemService(Context.FINGERPRINT_SERVICE); - Cipher cipher = null; - boolean hasError = false; - - try { - KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME); - generateKey(keyStore); - cipher = getCipher(); - keyStore.load(null); - SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME, null); - cipher.init(Cipher.ENCRYPT_MODE, key); - } catch (Exception e) { - TermuxApiLogger.error(TAG, e); - hasError = true; - } - - if (cipher != null && !hasError) { - authenticateWithFingerprint(this, getIntent(), fingerprintManager, cipher); - } + Executor executor = Executors.newSingleThreadExecutor(); + authenticateWithFingerprint(this, getIntent(), executor); } /** * Handles authentication callback from our fingerprint sensor */ - protected static void authenticateWithFingerprint(final Context context, final Intent intent, final FingerprintManager fingerprintManager, Cipher cipher) { - FingerprintManager.AuthenticationCallback authenticationCallback = new FingerprintManager.AuthenticationCallback() { + protected static void authenticateWithFingerprint(final FragmentActivity context, final Intent intent, final Executor executor) { + BiometricPrompt biometricPrompt = new BiometricPrompt(context, executor, new BiometricPrompt.AuthenticationCallback() { @Override - public void onAuthenticationError(int errorCode, CharSequence errString) { - if (errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) { + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + if (errorCode == BiometricConstants.ERROR_LOCKOUT) { appendFingerprintError(ERROR_LOCKOUT); // first time locked out, subsequent auth attempts will fail immediately for a bit @@ -198,7 +180,7 @@ public class FingerprintAPI { } @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { setAuthResult(AUTH_RESULT_SUCCESS); postFingerprintResult(context, intent, fingerprintResult); } @@ -207,71 +189,38 @@ public class FingerprintAPI { public void onAuthenticationFailed() { addFailedAttempt(); } + }); - // unused - @Override - public void onAuthenticationHelp(int helpCode, CharSequence helpString) { } - }; - - Toast.makeText(context, "Scan fingerprint", Toast.LENGTH_LONG).show(); + BiometricPrompt.PromptInfo.Builder builder = new BiometricPrompt.PromptInfo.Builder(); + builder.setTitle(intent.hasExtra("title") ? intent.getStringExtra("title") : "Authenticate"); + builder.setNegativeButtonText(intent.hasExtra("cancel") ? intent.getStringExtra("cancel") : "Cancel"); + if (intent.hasExtra("description")) { + builder.setDescription(intent.getStringExtra("description")); + } + if (intent.hasExtra("subtitle")) { + builder.setSubtitle(intent.getStringExtra("subtitle")); + } // listen to fingerprint sensor - FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher); - final CancellationSignal cancellationSignal = new CancellationSignal(); - fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, authenticationCallback, null); + biometricPrompt.authenticate(builder.build()); - addSensorTimeout(context, intent, cancellationSignal); + addSensorTimeout(context, intent, biometricPrompt); } /** * Adds a timeout for our fingerprint sensor which will force a result return if we * haven't already received one */ - protected static void addSensorTimeout(final Context context, final Intent intent, final CancellationSignal cancellationSignal) { + protected static void addSensorTimeout(final Context context, final Intent intent, final BiometricPrompt biometricPrompt) { final Handler timeoutHandler = new Handler(Looper.getMainLooper()); timeoutHandler.postDelayed(() -> { if (!postedResult) { appendFingerprintError(ERROR_TIMEOUT); - cancellationSignal.cancel(); + biometricPrompt.cancelAuthentication(); postFingerprintResult(context, intent, fingerprintResult); } }, SENSOR_TIMEOUT); } - - protected static void generateKey(KeyStore keyStore) { - try { - KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME); - keyStore.load(null); - keyGenerator.init( - new KeyGenParameterSpec.Builder(KEY_NAME, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setUserAuthenticationRequired(true) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .build()); - - keyGenerator.generateKey(); - } catch (Exception e) { - TermuxApiLogger.error(TAG, e); - appendFingerprintError(ERROR_KEY_GENERATOR); - } - } - - /** - * Create the cipher needed for use with our SecretKey - */ - protected static Cipher getCipher() { - Cipher cipher = null; - try { - cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES - + "/" + KeyProperties.BLOCK_MODE_CBC - + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); - } catch (Exception e) { - TermuxApiLogger.error(TAG, e); - appendFingerprintError(ERROR_CIPHER); - } - return cipher; - } } /** diff --git a/app/src/main/java/com/termux/api/JobSchedulerAPI.java b/app/src/main/java/com/termux/api/JobSchedulerAPI.java index 000b389..1feca8e 100644 --- a/app/src/main/java/com/termux/api/JobSchedulerAPI.java +++ b/app/src/main/java/com/termux/api/JobSchedulerAPI.java @@ -7,7 +7,7 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.PersistableBundle; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import android.text.TextUtils; import android.util.Log; diff --git a/app/src/main/java/com/termux/api/KeystoreAPI.java b/app/src/main/java/com/termux/api/KeystoreAPI.java index 737fa48..22bea18 100644 --- a/app/src/main/java/com/termux/api/KeystoreAPI.java +++ b/app/src/main/java/com/termux/api/KeystoreAPI.java @@ -6,7 +6,7 @@ import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyInfo; import android.security.keystore.KeyProperties; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import android.util.Base64; import android.util.JsonWriter; diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0db1880..62c8458 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -17,6 +17,19 @@ true + + +