From c5d374af04aa1c110037c35674e353a34bd82870 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 7 Aug 2018 18:33:11 -0300 Subject: [PATCH 1/4] ScreenLock: Implement fingerprint unlocking with BiometricPromptCompat --- briar-android/build.gradle | 5 +- briar-android/src/main/AndroidManifest.xml | 7 +- .../briar/android/login/UnlockActivity.java | 80 ++++++++++++++++++- .../briar/android/util/UiUtils.java | 11 +++ .../src/main/res/layout/activity_unlock.xml | 18 +---- briar-android/src/main/res/values/strings.xml | 2 + briar-android/witness.gradle | 1 + 7 files changed, 103 insertions(+), 21 deletions(-) diff --git a/briar-android/build.gradle b/briar-android/build.gradle index bb4988e82..9f7e35b06 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'com.google.zxing:core:3.3.0' implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0' implementation 'com.vanniktech:emoji-google:0.5.1' + implementation 'moe.feng.support.biometricprompt:library:1.0.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2' @@ -75,8 +76,8 @@ def getStdout = { command, defaultValue -> } android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.2' defaultConfig { minSdkVersion 15 diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 82d4b83cf..722f3ff16 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -1,7 +1,10 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> + + @@ -17,6 +20,8 @@ + + requestKeyguardUnlock()); + button.setOnClickListener(view -> requestUnlock()); keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN); } @@ -83,18 +97,80 @@ public class UnlockActivity extends BaseActivity { // Check if app is still locked, lockable // and not finishing (which is possible if recreated) if (!keyguardShown && lockManager.isLocked() && !isFinishing()) { - requestKeyguardUnlock(); + requestUnlock(); } else if (!lockManager.isLocked()) { setResult(RESULT_OK); finish(); } } + private void requestUnlock() { + if (hasUsableFingerprint(this)) { + requestFingerprintUnlock(); + } else { + requestKeyguardUnlock(); + } + } + @Override public void onBackPressed() { moveTaskToBack(true); } + private void requestFingerprintUnlock() { + BiometricPromptCompat biometricPrompt = new Builder(this) + .setTitle(R.string.lock_unlock) + .setDescription(R.string.lock_unlock_fingerprint_description) + .setNegativeButton(R.string.lock_unlock_password, + (dialog, which) -> { + requestKeyguardUnlock(); + }) + .build(); + CancellationSignal signal = new CancellationSignal(); + biometricPrompt.authenticate(signal, new IAuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, + @Nullable CharSequence errString) { + // when back button is pressed while fingerprint dialog shows + if (errorCode == BIOMETRIC_ERROR_CANCELED || + errorCode == BIOMETRIC_ERROR_USER_CANCELED) { + finish(); + } + // locked out due to 5 failed attempts, lasts for 30 seconds + else if (errorCode == BIOMETRIC_ERROR_LOCKOUT || + errorCode == BIOMETRIC_ERROR_LOCKOUT_PERMANENT) { + if (hasKeyguardLock(UnlockActivity.this)) { + requestKeyguardUnlock(); + } else { + // normally fingerprints require a screen lock, but + // who knows if that's true for all devices out there + Toast.makeText(UnlockActivity.this, errString, + Toast.LENGTH_LONG).show(); + finish(); + } + } else { + Toast.makeText(UnlockActivity.this, errString, + Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onAuthenticationHelp(int helpCode, + @Nullable CharSequence helpString) { + } + + @Override + public void onAuthenticationSucceeded( + @NonNull IAuthenticationResult result) { + unlock(); + } + + @Override + public void onAuthenticationFailed() { + } + }); + } + private void requestKeyguardUnlock() { KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 7fb316a62..7eb04eb8a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -59,6 +59,8 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; +import static moe.feng.support.biometricprompt.BiometricPromptCompat.hasEnrolledFingerprints; +import static moe.feng.support.biometricprompt.BiometricPromptCompat.isHardwareDetected; import static org.briarproject.briar.BuildConfig.APPLICATION_ID; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; @@ -261,6 +263,10 @@ public class UiUtils { } public static boolean hasScreenLock(Context ctx) { + return hasKeyguardLock(ctx) || hasUsableFingerprint(ctx); + } + + public static boolean hasKeyguardLock(Context ctx) { if (SDK_INT < 21) return false; KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE); @@ -271,6 +277,11 @@ public class UiUtils { (SDK_INT >= 23 && keyguardManager.isDeviceSecure()); } + public static boolean hasUsableFingerprint(Context ctx) { + return SDK_INT >= 23 && isHardwareDetected(ctx) && + hasEnrolledFingerprints(ctx); + } + public static void triggerFeedback(AndroidExecutor androidExecutor) { androidExecutor.runOnBackgroundThread( () -> ACRA.getErrorReporter() diff --git a/briar-android/src/main/res/layout/activity_unlock.xml b/briar-android/src/main/res/layout/activity_unlock.xml index 48413a198..32e87ad55 100644 --- a/briar-android/src/main/res/layout/activity_unlock.xml +++ b/briar-android/src/main/res/layout/activity_unlock.xml @@ -14,27 +14,13 @@ android:layout_height="150dp" android:layout_margin="@dimen/margin_large" android:src="@drawable/splash_screen" - app:layout_constraintBottom_toTopOf="@+id/is_locked" + app:layout_constraintBottom_toTopOf="@+id/unlock" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_chainStyle="spread" + app:layout_constraintVertical_bias="0.1" app:tint="?attr/colorControlNormal"/> - -