diff --git a/src/chrome/android/BUILD.gn b/src/chrome/android/BUILD.gn index 15a4d013b12..fa2470c7d50 100644 --- a/src/chrome/android/BUILD.gn +++ b/src/chrome/android/BUILD.gn @@ -702,6 +702,7 @@ if (current_toolchain == default_toolchain) { "//third_party/androidx:androidx_activity_activity_java", "//third_party/androidx:androidx_annotation_annotation_experimental_java", "//third_party/androidx:androidx_asynclayoutinflater_asynclayoutinflater_appcompat_java", + "//third_party/androidx:androidx_biometric_biometric_java", "//third_party/androidx:androidx_browser_browser_java", "//third_party/androidx:androidx_collection_collection_java", "//third_party/androidx:androidx_constraintlayout_constraintlayout_java", diff --git a/src/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/src/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index a47f69c8244..9e205dbfc0f 100644 --- a/src/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/src/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java @@ -14,6 +14,7 @@ import android.os.Bundle; import android.os.SystemClock; import android.provider.Browser; +import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Pair; @@ -29,9 +30,13 @@ import android.view.WindowManager; import androidx.annotation.IntDef; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; +import androidx.biometric.BiometricManager; +import androidx.biometric.BiometricPrompt; +import androidx.core.content.ContextCompat; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleRegistry; @@ -265,6 +270,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.DoubleConsumer; @@ -3766,8 +3772,92 @@ private boolean shouldShowOverviewPageOnStart() { return shouldShowOverviewPageOnStart(getIntent()); } + private void checkBiometricSupportAndAuthenticate() { + BiometricManager biometricManager = BiometricManager.from(this); + switch (biometricManager.canAuthenticate( + BiometricManager.Authenticators.BIOMETRIC_STRONG + | BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { + case BiometricManager.BIOMETRIC_SUCCESS: + initiateBiometricAuthentication(); + break; + case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: + Toast.makeText(this, "No biometric hardware available", Toast.LENGTH_SHORT).show(); + break; + case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: + Toast.makeText(this, "Biometric hardware currently unavailable", Toast.LENGTH_SHORT) + .show(); + break; + case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: + Intent enrollIntent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL); + enrollIntent.putExtra( + Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, + BiometricManager.Authenticators.BIOMETRIC_STRONG + | BiometricManager.Authenticators.DEVICE_CREDENTIAL); + startActivity(enrollIntent); + break; + default: + Toast.makeText(this, "Biometric authentication not supported", Toast.LENGTH_SHORT) + .show(); + } + } + + private void initiateBiometricAuthentication() { + Executor executor = ContextCompat.getMainExecutor(this); + BiometricPrompt biometricPrompt = + new BiometricPrompt( + this, + executor, + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError( + int errorCode, @NonNull CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + Toast.makeText( + getApplicationContext(), + "Authentication error: " + errString, + Toast.LENGTH_SHORT) + .show(); + finish(); + } + + @Override + public void onAuthenticationSucceeded( + @NonNull BiometricPrompt.AuthenticationResult result) { + super.onAuthenticationSucceeded(result); + Toast.makeText( + getApplicationContext(), + "Authentication succeeded!", + Toast.LENGTH_SHORT) + .show(); + } + + @Override + public void onAuthenticationFailed() { + super.onAuthenticationFailed(); + Toast.makeText( + getApplicationContext(), + "Authentication failed", + Toast.LENGTH_SHORT) + .show(); + initiateBiometricAuthentication(); + } + }); + + BiometricPrompt.PromptInfo promptInfo = + new BiometricPrompt.PromptInfo.Builder() + .setTitle("Biometric login") + .setSubtitle("Log in using your biometric credential") + .setAllowedAuthenticators( + BiometricManager.Authenticators.BIOMETRIC_STRONG + | BiometricManager.Authenticators.DEVICE_CREDENTIAL) + .build(); + + biometricPrompt.authenticate(promptInfo); + } + @Override public void onStart() { + checkBiometricSupportAndAuthenticate(); try (TraceEvent e = TraceEvent.scoped("ChromeTabbedActivity.onStart")) { super.onStart(); } @@ -3789,6 +3879,7 @@ public void onPause() { @Override public void onResume() { + checkBiometricSupportAndAuthenticate(); try (TraceEvent e = TraceEvent.scoped("ChromeTabbedActivity.onResume")) { super.onResume(); } diff --git a/src/third_party/android_deps/build.gradle b/src/third_party/android_deps/build.gradle index 3770a921639..d20bd21b130 100644 --- a/src/third_party/android_deps/build.gradle +++ b/src/third_party/android_deps/build.gradle @@ -197,6 +197,7 @@ dependencies { // Use testCompile to have is_robolectric = true set on targets. testCompile "org.robolectric:robolectric:4.12.1" + implementation "androidx.biometric:biometric:1.2.0-alpha04" } task setUpRepository(type: BuildConfigGenerator) {