2/n: Add default implementation for multi-biometric enroll
1) Adds a layout for multi-biometric selection in BiometricEnrollActivity 2) Adds widgets for checkboxes 3) Shows ConfirmLock*/ChooseLock* for multi-biometric devices in BiometricEnrollActivity 4) finish()'s when loses foreground 5) Adds default string for ChooseLock* and multi-biometrics, e.g. "Set up Password + Biometrics", as well as associated plumbing to bring the user back to BiometricEnrollActivity once the credential is enrolled 6) When max templates enrolled, checkbox becomes disabled and description string is updated Bug: 162341940 Bug: 152242790 Fixes: 161742393 No effect on existing devices with the following: Test: adb shell am start -a android.settings.BIOMETRIC_ENROLL Test: SUW Test: make -j RunSettingsRoboTests Exempt-From-Owner-Approval: Biometric-related change to EncryptionInterstitial Change-Id: I855460d50228ace24d4ec5fbe330f02ab406cc02
This commit is contained in:
@@ -16,16 +16,35 @@
|
||||
|
||||
package com.android.settings.biometrics;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.SetupChooseLockGeneric;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
/**
|
||||
* Common biometric utilities.
|
||||
*/
|
||||
public class BiometricUtils {
|
||||
private static final String TAG = "BiometricUtils";
|
||||
/**
|
||||
* Given the result from confirming or choosing a credential, request Gatekeeper to generate
|
||||
* a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
|
||||
@@ -36,24 +55,29 @@ public class BiometricUtils {
|
||||
* @param challenge Unique biometric challenge from FingerprintManager/FaceManager
|
||||
* @return
|
||||
*/
|
||||
public static byte[] requestGatekeeperHat(Context context, Intent result, int userId,
|
||||
long challenge) {
|
||||
final long gatekeeperPasswordHandle = result.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
if (gatekeeperPasswordHandle == 0L) {
|
||||
public static byte[] requestGatekeeperHat(@NonNull Context context, @NonNull Intent result,
|
||||
int userId, long challenge) {
|
||||
if (!containsGatekeeperPasswordHandle(result)) {
|
||||
throw new IllegalStateException("Gatekeeper Password is missing!!");
|
||||
}
|
||||
final long gatekeeperPasswordHandle = result.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
return requestGatekeeperHat(context, gatekeeperPasswordHandle, userId, challenge);
|
||||
}
|
||||
|
||||
public static byte[] requestGatekeeperHat(@NonNull Context context, long gkPwHandle, int userId,
|
||||
long challenge) {
|
||||
final LockPatternUtils utils = new LockPatternUtils(context);
|
||||
return utils.verifyGatekeeperPasswordHandle(gatekeeperPasswordHandle, challenge, userId)
|
||||
return utils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId)
|
||||
.getGatekeeperHAT();
|
||||
}
|
||||
|
||||
public static boolean containsGatekeeperPassword(Intent data) {
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L) != 0L;
|
||||
public static boolean containsGatekeeperPasswordHandle(@Nullable Intent data) {
|
||||
return data != null && data.hasExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
|
||||
}
|
||||
|
||||
public static long getGatekeeperPasswordHandle(@NonNull Intent data) {
|
||||
return data.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,16 +88,157 @@ public class BiometricUtils {
|
||||
* @param context Caller's context
|
||||
* @param data The onActivityResult intent from ChooseLock* or ConfirmLock*
|
||||
*/
|
||||
public static void removeGatekeeperPasswordHandle(Context context, Intent data) {
|
||||
public static void removeGatekeeperPasswordHandle(@NonNull Context context,
|
||||
@Nullable Intent data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
final long gatekeeperPasswordsHandle = data.getLongExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 0L);
|
||||
if (gatekeeperPasswordsHandle == 0L) {
|
||||
if (!containsGatekeeperPasswordHandle(data)) {
|
||||
return;
|
||||
}
|
||||
removeGatekeeperPasswordHandle(context, getGatekeeperPasswordHandle(data));
|
||||
}
|
||||
|
||||
public static void removeGatekeeperPasswordHandle(@NonNull Context context, long handle) {
|
||||
final LockPatternUtils utils = new LockPatternUtils(context);
|
||||
utils.removeGatekeeperPasswordHandle(gatekeeperPasswordsHandle);
|
||||
utils.removeGatekeeperPasswordHandle(handle);
|
||||
Log.d(TAG, "Removed handle");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting ChooseLock*
|
||||
*/
|
||||
public static Intent getChooseLockIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
if (WizardManagerHelper.isAnySetupWizard(activityIntent)) {
|
||||
// Default to PIN lock in setup wizard
|
||||
Intent intent = new Intent(context, SetupChooseLockGeneric.class);
|
||||
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
|
||||
intent.putExtra(
|
||||
LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment
|
||||
.EXTRA_SHOW_OPTIONS_BUTTON, true);
|
||||
}
|
||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||
return intent;
|
||||
} else {
|
||||
return new Intent(context, ChooseLockGeneric.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting FingerprintEnrollFindSensor
|
||||
*/
|
||||
public static Intent getFingerprintFindSensorIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
Intent intent = new Intent(context, FingerprintEnrollFindSensor.class);
|
||||
SetupWizardUtils.copySetupExtras(activityIntent, intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting FingerprintEnrollIntroduction
|
||||
*/
|
||||
public static Intent getFingerprintIntroIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
if (WizardManagerHelper.isAnySetupWizard(activityIntent)) {
|
||||
Intent intent = new Intent(context, SetupFingerprintEnrollIntroduction.class);
|
||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||
return intent;
|
||||
} else {
|
||||
return new Intent(context, FingerprintEnrollIntroduction.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context caller's context
|
||||
* @param activityIntent The intent that started the caller's activity
|
||||
* @return Intent for starting FaceEnrollIntroduction
|
||||
*/
|
||||
public static Intent getFaceIntroIntent(@NonNull Context context,
|
||||
@NonNull Intent activityIntent) {
|
||||
Intent intent = new Intent(context, FaceEnrollIntroduction.class);
|
||||
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activity Reference to the calling activity, used to startActivity
|
||||
* @param intent Intent pointing to the enrollment activity
|
||||
* @param requestCode If non-zero, will invoke startActivityForResult instead of startActivity
|
||||
* @param hardwareAuthToken HardwareAuthToken from Gatekeeper
|
||||
* @param userId User to request enrollment for
|
||||
*/
|
||||
public static void launchEnrollForResult(@NonNull BiometricEnrollActivity activity,
|
||||
@NonNull Intent intent, int requestCode,
|
||||
@Nullable byte[] hardwareAuthToken, @Nullable Long gkPwHandle, int userId) {
|
||||
if (hardwareAuthToken != null) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
|
||||
hardwareAuthToken);
|
||||
}
|
||||
if (gkPwHandle != null) {
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
}
|
||||
|
||||
if (activity instanceof BiometricEnrollActivity.InternalActivity) {
|
||||
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
}
|
||||
|
||||
if (requestCode != 0) {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
} else {
|
||||
activity.startActivity(intent);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activity Activity that we want to check
|
||||
* @return True if the activity is going through a multi-biometric enrollment flow.
|
||||
*/
|
||||
public static boolean isMultiBiometricEnrollmentFlow(@NonNull Activity activity) {
|
||||
return activity.getIntent().hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE);
|
||||
}
|
||||
|
||||
public static void copyMultiBiometricExtras(@NonNull Intent fromIntent,
|
||||
@NonNull Intent toIntent) {
|
||||
final PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra(
|
||||
MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, null);
|
||||
if (pendingIntent != null) {
|
||||
toIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current biometric enrollment (e.g. face) should be followed by another one (e.g.
|
||||
* fingerprint) (see {@link #isMultiBiometricEnrollmentFlow(Activity)}), retrieves the
|
||||
* PendingIntent pointing to the next enrollment and starts it. The caller will receive the
|
||||
* result in onActivityResult.
|
||||
* @return true if the next enrollment was started
|
||||
*/
|
||||
public static boolean tryStartingNextBiometricEnroll(@NonNull Activity activity,
|
||||
int requestCode) {
|
||||
final PendingIntent pendingIntent = (PendingIntent) activity.getIntent()
|
||||
.getExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE);
|
||||
if (pendingIntent != null) {
|
||||
try {
|
||||
Log.d(TAG, "Starting pendingIntent: " + pendingIntent);
|
||||
IntentSender intentSender = pendingIntent.getIntentSender();
|
||||
activity.startIntentSenderForResult(intentSender, requestCode,
|
||||
null /* fillInIntent */, 0 /* flagMask */, 0 /* flagValues */,
|
||||
0 /* extraFlags */);
|
||||
return true;
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Log.e(TAG, "Pending intent canceled: " + e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user