Add plumbing and placeholder screens for parental consent flow.
Bug: 188847063 Test: adb shell am start -a android.settings.BIOMETRIC_ENROLL --ez require_consent true Test: atest com.android.settings.biometrics.ParentalConsentHelperTest Change-Id: Ie136036d5f550775fd0b021979581a5d222f1b68
This commit is contained in:
@@ -1803,6 +1803,10 @@
|
|||||||
android:theme="@style/GlifV3Theme.Light"
|
android:theme="@style/GlifV3Theme.Light"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<activity android:name=".biometrics.face.FaceEnrollParentalConsent"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait"/>
|
||||||
|
|
||||||
<activity android:name=".biometrics.face.FaceEnrollIntroduction"
|
<activity android:name=".biometrics.face.FaceEnrollIntroduction"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:screenOrientation="portrait"/>
|
android:screenOrientation="portrait"/>
|
||||||
@@ -1837,6 +1841,7 @@
|
|||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFindSensor" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFindSensor" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollEnrolling" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollEnrolling" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFinish" android:exported="false"/>
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFinish" android:exported="false"/>
|
||||||
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollParentalConsent" android:exported="false"/>
|
||||||
<activity android:name=".biometrics.fingerprint.FingerprintEnrollIntroduction"
|
<activity android:name=".biometrics.fingerprint.FingerprintEnrollIntroduction"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/GlifTheme.Light">
|
android:theme="@style/GlifTheme.Light">
|
||||||
|
@@ -777,6 +777,8 @@
|
|||||||
<string name="security_settings_face_enroll_introduction_more">More</string>
|
<string name="security_settings_face_enroll_introduction_more">More</string>
|
||||||
<!-- Introduction title shown in face enrollment to introduce the face unlock feature [CHAR LIMIT=40] -->
|
<!-- Introduction title shown in face enrollment to introduce the face unlock feature [CHAR LIMIT=40] -->
|
||||||
<string name="security_settings_face_enroll_introduction_title">Unlock with your face</string>
|
<string name="security_settings_face_enroll_introduction_title">Unlock with your face</string>
|
||||||
|
<!-- Introduction title shown in face enrollment when when asking for parental consent for the face unlock feature [CHAR LIMIT=40] -->
|
||||||
|
<string name="security_settings_face_enroll_consent_introduction_title">Allow face unlock</string>
|
||||||
<!-- Introduction title shown in face enrollment to introduce the face unlock feature, when face unlock is disabled by device admin [CHAR LIMIT=60] -->
|
<!-- Introduction title shown in face enrollment to introduce the face unlock feature, when face unlock is disabled by device admin [CHAR LIMIT=60] -->
|
||||||
<string name="security_settings_face_enroll_introduction_title_unlock_disabled">Use your face to authenticate</string>
|
<string name="security_settings_face_enroll_introduction_title_unlock_disabled">Use your face to authenticate</string>
|
||||||
<!-- Introduction detail message shown in face enrollment dialog [CHAR LIMIT=NONE]-->
|
<!-- Introduction detail message shown in face enrollment dialog [CHAR LIMIT=NONE]-->
|
||||||
@@ -888,8 +890,10 @@
|
|||||||
</plurals>
|
</plurals>
|
||||||
<!-- message shown in summary field when no fingerprints are registered -->
|
<!-- message shown in summary field when no fingerprints are registered -->
|
||||||
<string name="security_settings_fingerprint_preference_summary_none"></string>
|
<string name="security_settings_fingerprint_preference_summary_none"></string>
|
||||||
<!-- Introduction title shown in fingerprint enrollment to introduce the fingerprint feature[CHAR LIMIT=29] -->
|
<!-- Introduction title shown in fingerprint enrollment to introduce the fingerprint feature [CHAR LIMIT=29] -->
|
||||||
<string name="security_settings_fingerprint_enroll_introduction_title">Set up your fingerprint</string>
|
<string name="security_settings_fingerprint_enroll_introduction_title">Set up your fingerprint</string>
|
||||||
|
<!-- Introduction title shown in fingerprint enrollment when asking for parental consent for fingerprint unlock [CHAR LIMIT=29] -->
|
||||||
|
<string name="security_settings_fingerprint_enroll_consent_introduction_title">Allow fingerprint unlock</string>
|
||||||
<!-- Introduction title shown in fingerprint enrollment to introduce the fingerprint feature, when fingerprint unlock is disabled by device admin [CHAR LIMIT=40] -->
|
<!-- Introduction title shown in fingerprint enrollment to introduce the fingerprint feature, when fingerprint unlock is disabled by device admin [CHAR LIMIT=40] -->
|
||||||
<string name="security_settings_fingerprint_enroll_introduction_title_unlock_disabled">Use your fingerprint</string>
|
<string name="security_settings_fingerprint_enroll_introduction_title_unlock_disabled">Use your fingerprint</string>
|
||||||
<!-- Introduction detail message shown in fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
|
<!-- Introduction detail message shown in fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
|
||||||
|
@@ -19,6 +19,9 @@ package com.android.settings.biometrics;
|
|||||||
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
|
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
|
||||||
import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
|
import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
|
||||||
|
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
@@ -64,6 +67,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
|
|
||||||
private static final int REQUEST_CHOOSE_LOCK = 1;
|
private static final int REQUEST_CHOOSE_LOCK = 1;
|
||||||
private static final int REQUEST_CONFIRM_LOCK = 2;
|
private static final int REQUEST_CONFIRM_LOCK = 2;
|
||||||
|
// prompt for parental consent options
|
||||||
|
private static final int REQUEST_CHOOSE_OPTIONS = 3;
|
||||||
|
|
||||||
public static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
|
public static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
|
||||||
|
|
||||||
@@ -71,8 +76,12 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
// this only applies to fingerprint.
|
// this only applies to fingerprint.
|
||||||
public static final String EXTRA_SKIP_INTRO = "skip_intro";
|
public static final String EXTRA_SKIP_INTRO = "skip_intro";
|
||||||
|
|
||||||
|
// TODO: temporary while waiting for team to add real flag
|
||||||
|
public static final String EXTRA_TEMP_REQUIRE_PARENTAL_CONSENT = "require_consent";
|
||||||
|
|
||||||
private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
|
private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
|
||||||
private static final String SAVED_STATE_ENROLL_ACTION_LOGGED = "enroll_action_logged";
|
private static final String SAVED_STATE_ENROLL_ACTION_LOGGED = "enroll_action_logged";
|
||||||
|
private static final String SAVED_STATE_PARENTAL_OPTIONS = "enroll_preferences";
|
||||||
private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle";
|
private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle";
|
||||||
|
|
||||||
public static final class InternalActivity extends BiometricEnrollActivity {}
|
public static final class InternalActivity extends BiometricEnrollActivity {}
|
||||||
@@ -80,9 +89,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
private int mUserId = UserHandle.myUserId();
|
private int mUserId = UserHandle.myUserId();
|
||||||
private boolean mConfirmingCredentials;
|
private boolean mConfirmingCredentials;
|
||||||
private boolean mIsEnrollActionLogged;
|
private boolean mIsEnrollActionLogged;
|
||||||
private boolean mIsFaceEnrollable;
|
private boolean mHasFeatureFace = false;
|
||||||
private boolean mIsFingerprintEnrollable;
|
private boolean mHasFeatureFingerprint = false;
|
||||||
|
private boolean mIsFaceEnrollable = false;
|
||||||
|
private boolean mIsFingerprintEnrollable = false;
|
||||||
|
private boolean mParentalOptionsRequired = false;
|
||||||
|
private Bundle mParentalOptions;
|
||||||
@Nullable private Long mGkPwHandle;
|
@Nullable private Long mGkPwHandle;
|
||||||
|
@Nullable private ParentalConsentHelper mParentalConsentHelper;
|
||||||
@Nullable private MultiBiometricEnrollHelper mMultiBiometricEnrollHelper;
|
@Nullable private MultiBiometricEnrollHelper mMultiBiometricEnrollHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,6 +115,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
SAVED_STATE_CONFIRMING_CREDENTIALS, false);
|
SAVED_STATE_CONFIRMING_CREDENTIALS, false);
|
||||||
mIsEnrollActionLogged = savedInstanceState.getBoolean(
|
mIsEnrollActionLogged = savedInstanceState.getBoolean(
|
||||||
SAVED_STATE_ENROLL_ACTION_LOGGED, false);
|
SAVED_STATE_ENROLL_ACTION_LOGGED, false);
|
||||||
|
mParentalOptions = savedInstanceState.getBundle(SAVED_STATE_PARENTAL_OPTIONS);
|
||||||
if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) {
|
if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) {
|
||||||
mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE);
|
mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE);
|
||||||
}
|
}
|
||||||
@@ -141,52 +156,98 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
SetupWizardUtils.getThemeString(intent));
|
SetupWizardUtils.getThemeString(intent));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
|
final PackageManager pm = getApplicationContext().getPackageManager();
|
||||||
final int authenticators = intent.getIntExtra(
|
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
|
||||||
EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
|
mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
|
||||||
|
|
||||||
|
// determine what can be enrolled
|
||||||
|
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
|
||||||
|
if (mHasFeatureFace) {
|
||||||
|
final FaceManager faceManager = getSystemService(FaceManager.class);
|
||||||
|
final List<FaceSensorPropertiesInternal> faceProperties =
|
||||||
|
faceManager.getSensorPropertiesInternal();
|
||||||
|
if (!faceProperties.isEmpty()) {
|
||||||
|
final int maxEnrolls =
|
||||||
|
isSetupWizard ? 1 : faceProperties.get(0).maxEnrollmentsPerUser;
|
||||||
|
mIsFaceEnrollable =
|
||||||
|
faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mHasFeatureFingerprint) {
|
||||||
|
final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
|
||||||
|
final List<FingerprintSensorPropertiesInternal> fpProperties =
|
||||||
|
fpManager.getSensorPropertiesInternal();
|
||||||
|
if (!fpProperties.isEmpty()) {
|
||||||
|
final int maxEnrolls =
|
||||||
|
isSetupWizard ? 1 : fpProperties.get(0).maxEnrollmentsPerUser;
|
||||||
|
mIsFingerprintEnrollable =
|
||||||
|
fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/188847063): replace with real flag when ready
|
||||||
|
mParentalOptionsRequired = intent.getBooleanExtra(
|
||||||
|
BiometricEnrollActivity.EXTRA_TEMP_REQUIRE_PARENTAL_CONSENT, false);
|
||||||
|
|
||||||
|
if (mParentalOptionsRequired && mParentalOptions == null) {
|
||||||
|
mParentalConsentHelper = new ParentalConsentHelper(
|
||||||
|
mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
|
||||||
|
setOrConfirmCredentialsNow();
|
||||||
|
} else {
|
||||||
|
startEnroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startEnroll() {
|
||||||
|
// TODO(b/188847063): This can be deleted, but log it now until it's wired up for real.
|
||||||
|
if (mParentalOptionsRequired) {
|
||||||
|
if (mParentalOptions == null) {
|
||||||
|
throw new IllegalStateException("consent options required, but not set");
|
||||||
|
}
|
||||||
|
Log.d(TAG, "consent for face: "
|
||||||
|
+ ParentalConsentHelper.hasFaceConsent(mParentalOptions));
|
||||||
|
Log.d(TAG, "consent for fingerprint: "
|
||||||
|
+ ParentalConsentHelper.hasFingerprintConsent(mParentalOptions));
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "startEnroll without requiring consent");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
|
||||||
|
final int authenticators = getIntent().getIntExtra(
|
||||||
|
EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
|
||||||
Log.d(TAG, "Authenticators: " + authenticators);
|
Log.d(TAG, "Authenticators: " + authenticators);
|
||||||
|
|
||||||
final PackageManager pm = getApplicationContext().getPackageManager();
|
startEnrollWith(authenticators, WizardManagerHelper.isAnySetupWizard(getIntent()));
|
||||||
final boolean hasFeatureFingerprint =
|
}
|
||||||
pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
|
|
||||||
final boolean hasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
|
|
||||||
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
|
|
||||||
|
|
||||||
if (isSetupWizard) {
|
private void startEnrollWith(@Authenticators.Types int authenticators, boolean setupWizard) {
|
||||||
if (hasFeatureFace && hasFeatureFingerprint) {
|
// If the caller is not setup wizard, and the user has something enrolled, finish.
|
||||||
setupForMultiBiometricEnroll();
|
if (!setupWizard) {
|
||||||
} else if (hasFeatureFace) {
|
|
||||||
launchFaceOnlyEnroll();
|
|
||||||
} else if (hasFeatureFingerprint) {
|
|
||||||
launchFingerprintOnlyEnroll();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "No biometrics but started by SUW?");
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the caller is not setup wizard, and the user has something enrolled, finish.
|
|
||||||
final BiometricManager bm = getSystemService(BiometricManager.class);
|
final BiometricManager bm = getSystemService(BiometricManager.class);
|
||||||
final @BiometricError int result = bm.canAuthenticate(authenticators);
|
final @BiometricError int result = bm.canAuthenticate(authenticators);
|
||||||
if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
|
if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
|
||||||
Log.e(TAG, "Unexpected result: " + result);
|
Log.e(TAG, "Unexpected result (has enrollments): " + result);
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This will need to be updated if the device has sensors other than BIOMETRIC_STRONG
|
// This will need to be updated if the device has sensors other than BIOMETRIC_STRONG
|
||||||
if (authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
|
if (!setupWizard && authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
|
||||||
launchCredentialOnlyEnroll();
|
launchCredentialOnlyEnroll();
|
||||||
} else if (hasFeatureFace && hasFeatureFingerprint) {
|
} else if (mHasFeatureFace && mHasFeatureFingerprint) {
|
||||||
setupForMultiBiometricEnroll();
|
if (mParentalOptionsRequired && mGkPwHandle != null) {
|
||||||
} else if (hasFeatureFingerprint) {
|
launchFaceAndFingerprintEnroll();
|
||||||
launchFingerprintOnlyEnroll();
|
|
||||||
} else if (hasFeatureFace) {
|
|
||||||
launchFaceOnlyEnroll();
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Unknown state, finishing");
|
setOrConfirmCredentialsNow();
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
|
} else if (mHasFeatureFingerprint) {
|
||||||
|
launchFingerprintOnlyEnroll();
|
||||||
|
} else if (mHasFeatureFace) {
|
||||||
|
launchFaceOnlyEnroll();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unknown state, finishing (was SUW: " + setupWizard + ")");
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +256,9 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials);
|
outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials);
|
||||||
outState.putBoolean(SAVED_STATE_ENROLL_ACTION_LOGGED, mIsEnrollActionLogged);
|
outState.putBoolean(SAVED_STATE_ENROLL_ACTION_LOGGED, mIsEnrollActionLogged);
|
||||||
|
if (mParentalOptions != null) {
|
||||||
|
outState.putBundle(SAVED_STATE_PARENTAL_OPTIONS, mParentalOptions);
|
||||||
|
}
|
||||||
if (mGkPwHandle != null) {
|
if (mGkPwHandle != null) {
|
||||||
outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle);
|
outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle);
|
||||||
}
|
}
|
||||||
@@ -204,31 +268,84 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
// single enrollment is handled entirely by the launched activity
|
||||||
|
// this handles multi enroll or if parental consent is required
|
||||||
|
if (mParentalConsentHelper != null) {
|
||||||
|
handleOnActivityResultWhileConsenting(requestCode, resultCode, data);
|
||||||
|
} else {
|
||||||
|
handleOnActivityResultWhileEnrollingMultiple(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handles responses while parental consent is pending
|
||||||
|
private void handleOnActivityResultWhileConsenting(
|
||||||
|
int requestCode, int resultCode, Intent data) {
|
||||||
|
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_CHOOSE_LOCK:
|
||||||
|
case REQUEST_CONFIRM_LOCK:
|
||||||
|
mConfirmingCredentials = false;
|
||||||
|
if (isSuccessfulConfirmOrChooseCredential(requestCode, resultCode)) {
|
||||||
|
updateGatekeeperPasswordHandle(data);
|
||||||
|
if (!mParentalConsentHelper.launchNext(this, REQUEST_CHOOSE_OPTIONS)) {
|
||||||
|
Log.e(TAG, "Nothing to prompt for consent (no modalities enabled)!");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
|
||||||
|
setResult(resultCode);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case REQUEST_CHOOSE_OPTIONS:
|
||||||
|
if (resultCode == RESULT_CONSENT_GRANTED || resultCode == RESULT_CONSENT_DENIED) {
|
||||||
|
final boolean isStillPrompting = mParentalConsentHelper.launchNext(
|
||||||
|
this, REQUEST_CHOOSE_OPTIONS, resultCode, data);
|
||||||
|
if (!isStillPrompting) {
|
||||||
|
Log.d(TAG, "Enrollment options set, starting enrollment now");
|
||||||
|
|
||||||
|
mParentalOptions = mParentalConsentHelper.getConsentResult();
|
||||||
|
mParentalConsentHelper = null;
|
||||||
|
startEnroll();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Unknown or cancelled parental consent");
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "Unknown consenting requestCode: " + requestCode + ", finishing");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handles responses while multi biometric enrollment is pending
|
||||||
|
private void handleOnActivityResultWhileEnrollingMultiple(
|
||||||
|
int requestCode, int resultCode, Intent data) {
|
||||||
if (mMultiBiometricEnrollHelper == null) {
|
if (mMultiBiometricEnrollHelper == null) {
|
||||||
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
||||||
|
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case REQUEST_CHOOSE_LOCK:
|
case REQUEST_CHOOSE_LOCK:
|
||||||
|
case REQUEST_CONFIRM_LOCK:
|
||||||
mConfirmingCredentials = false;
|
mConfirmingCredentials = false;
|
||||||
if (resultCode == ChooseLockPattern.RESULT_FINISHED) {
|
final boolean isOk =
|
||||||
startMultiBiometricEnroll(data);
|
isSuccessfulConfirmOrChooseCredential(requestCode, resultCode);
|
||||||
|
// single modality enrollment requests confirmation directly
|
||||||
|
// via BiometricEnrollBase#onCreate and should never get here
|
||||||
|
if (isOk && mHasFeatureFace && mHasFeatureFingerprint) {
|
||||||
|
updateGatekeeperPasswordHandle(data);
|
||||||
|
launchFaceAndFingerprintEnroll();
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Unknown result for chooseLock: " + resultCode);
|
Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
|
||||||
setResult(resultCode);
|
setResult(resultCode);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REQUEST_CONFIRM_LOCK:
|
|
||||||
mConfirmingCredentials = false;
|
|
||||||
if (resultCode == RESULT_OK) {
|
|
||||||
startMultiBiometricEnroll(data);
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Unknown result for confirmLock: " + resultCode);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
Log.d(TAG, "Unknown requestCode: " + requestCode + ", finishing");
|
Log.w(TAG, "Unknown enrolling requestCode: " + requestCode + ", finishing");
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -236,18 +353,28 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isSuccessfulConfirmOrChooseCredential(int requestCode, int resultCode) {
|
||||||
|
final boolean okChoose = requestCode == REQUEST_CHOOSE_LOCK
|
||||||
|
&& resultCode == ChooseLockPattern.RESULT_FINISHED;
|
||||||
|
final boolean okConfirm = requestCode == REQUEST_CONFIRM_LOCK
|
||||||
|
&& resultCode == RESULT_OK;
|
||||||
|
return okChoose || okConfirm;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||||
final int new_resid = SetupWizardUtils.getTheme(this, getIntent());
|
final int newResid = SetupWizardUtils.getTheme(this, getIntent());
|
||||||
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
|
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
|
||||||
super.onApplyThemeResource(theme, new_resid, first);
|
super.onApplyThemeResource(theme, newResid, first);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
|
||||||
if (mConfirmingCredentials || mMultiBiometricEnrollHelper != null) {
|
if (mConfirmingCredentials
|
||||||
|
|| mMultiBiometricEnrollHelper != null
|
||||||
|
|| mParentalConsentHelper != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +384,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupForMultiBiometricEnroll() {
|
|
||||||
|
private void setOrConfirmCredentialsNow() {
|
||||||
if (!mConfirmingCredentials) {
|
if (!mConfirmingCredentials) {
|
||||||
mConfirmingCredentials = true;
|
mConfirmingCredentials = true;
|
||||||
if (!userHasPassword(mUserId)) {
|
if (!userHasPassword(mUserId)) {
|
||||||
@@ -268,37 +396,11 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startMultiBiometricEnroll(Intent data) {
|
private void updateGatekeeperPasswordHandle(@NonNull Intent data) {
|
||||||
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
|
|
||||||
final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
|
|
||||||
final FaceManager faceManager = getSystemService(FaceManager.class);
|
|
||||||
final List<FingerprintSensorPropertiesInternal> fpProperties =
|
|
||||||
fingerprintManager.getSensorPropertiesInternal();
|
|
||||||
final List<FaceSensorPropertiesInternal> faceProperties =
|
|
||||||
faceManager.getSensorPropertiesInternal();
|
|
||||||
|
|
||||||
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
|
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
|
||||||
|
if (mParentalConsentHelper != null) {
|
||||||
if (isSetupWizard) {
|
mParentalConsentHelper.updateGatekeeperHandle(data);
|
||||||
// This would need to be updated for devices with multiple sensors of the same modality
|
|
||||||
mIsFaceEnrollable = !faceProperties.isEmpty()
|
|
||||||
&& faceManager.getEnrolledFaces(mUserId).size() == 0;
|
|
||||||
mIsFingerprintEnrollable = !fpProperties.isEmpty()
|
|
||||||
&& fingerprintManager.getEnrolledFingerprints(mUserId).size() == 0;
|
|
||||||
} else {
|
|
||||||
// This would need to be updated for devices with multiple sensors of the same modality
|
|
||||||
mIsFaceEnrollable = !faceProperties.isEmpty()
|
|
||||||
&& faceManager.getEnrolledFaces(mUserId).size()
|
|
||||||
< faceProperties.get(0).maxEnrollmentsPerUser;
|
|
||||||
mIsFingerprintEnrollable = !fpProperties.isEmpty()
|
|
||||||
&& fingerprintManager.getEnrolledFingerprints(mUserId).size()
|
|
||||||
< fpProperties.get(0).maxEnrollmentsPerUser;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mMultiBiometricEnrollHelper = new MultiBiometricEnrollHelper(this, mUserId,
|
|
||||||
mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
|
|
||||||
mMultiBiometricEnrollHelper.startNextStep();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean userHasPassword(int userId) {
|
private boolean userHasPassword(int userId) {
|
||||||
@@ -310,6 +412,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
|
|
||||||
private void launchChooseLock() {
|
private void launchChooseLock() {
|
||||||
Log.d(TAG, "launchChooseLock");
|
Log.d(TAG, "launchChooseLock");
|
||||||
|
|
||||||
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
|
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
|
||||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
|
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
|
||||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
|
||||||
@@ -323,6 +426,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
|
|
||||||
private void launchConfirmLock() {
|
private void launchConfirmLock() {
|
||||||
Log.d(TAG, "launchConfirmLock");
|
Log.d(TAG, "launchConfirmLock");
|
||||||
|
|
||||||
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
|
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
|
||||||
builder.setRequestCode(REQUEST_CONFIRM_LOCK)
|
builder.setRequestCode(REQUEST_CONFIRM_LOCK)
|
||||||
.setRequestGatekeeperPasswordHandle(true)
|
.setRequestGatekeeperPasswordHandle(true)
|
||||||
@@ -346,7 +450,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
* @param intent Enrollment activity that should be started (e.g. FaceEnrollIntroduction.class,
|
* @param intent Enrollment activity that should be started (e.g. FaceEnrollIntroduction.class,
|
||||||
* etc).
|
* etc).
|
||||||
*/
|
*/
|
||||||
private void launchEnrollActivity(@NonNull Intent intent) {
|
private void launchSingleSensorEnrollActivity(@NonNull Intent intent) {
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
|
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
|
||||||
byte[] hardwareAuthToken = null;
|
byte[] hardwareAuthToken = null;
|
||||||
if (this instanceof InternalActivity) {
|
if (this instanceof InternalActivity) {
|
||||||
@@ -362,7 +466,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
// If only device credential was specified, ask the user to only set that up.
|
// If only device credential was specified, ask the user to only set that up.
|
||||||
intent = new Intent(this, ChooseLockGeneric.class);
|
intent = new Intent(this, ChooseLockGeneric.class);
|
||||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
|
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
|
||||||
launchEnrollActivity(intent);
|
launchSingleSensorEnrollActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchFingerprintOnlyEnroll() {
|
private void launchFingerprintOnlyEnroll() {
|
||||||
@@ -374,12 +478,18 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
|||||||
} else {
|
} else {
|
||||||
intent = BiometricUtils.getFingerprintIntroIntent(this, getIntent());
|
intent = BiometricUtils.getFingerprintIntroIntent(this, getIntent());
|
||||||
}
|
}
|
||||||
launchEnrollActivity(intent);
|
launchSingleSensorEnrollActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchFaceOnlyEnroll() {
|
private void launchFaceOnlyEnroll() {
|
||||||
final Intent intent = BiometricUtils.getFaceIntroIntent(this, getIntent());
|
final Intent intent = BiometricUtils.getFaceIntroIntent(this, getIntent());
|
||||||
launchEnrollActivity(intent);
|
launchSingleSensorEnrollActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchFaceAndFingerprintEnroll() {
|
||||||
|
mMultiBiometricEnrollHelper = new MultiBiometricEnrollHelper(this, mUserId,
|
||||||
|
mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
|
||||||
|
mMultiBiometricEnrollHelper.startNextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -26,6 +26,7 @@ import android.graphics.Color;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -50,12 +51,15 @@ import com.google.android.setupdesign.util.ThemeHelper;
|
|||||||
*/
|
*/
|
||||||
public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||||
|
|
||||||
|
private static final String TAG = "BiometricEnrollBase";
|
||||||
|
|
||||||
public static final String EXTRA_FROM_SETTINGS_SUMMARY = "from_settings_summary";
|
public static final String EXTRA_FROM_SETTINGS_SUMMARY = "from_settings_summary";
|
||||||
public static final String EXTRA_KEY_LAUNCHED_CONFIRM = "launched_confirm_lock";
|
public static final String EXTRA_KEY_LAUNCHED_CONFIRM = "launched_confirm_lock";
|
||||||
public static final String EXTRA_KEY_REQUIRE_VISION = "accessibility_vision";
|
public static final String EXTRA_KEY_REQUIRE_VISION = "accessibility_vision";
|
||||||
public static final String EXTRA_KEY_REQUIRE_DIVERSITY = "accessibility_diversity";
|
public static final String EXTRA_KEY_REQUIRE_DIVERSITY = "accessibility_diversity";
|
||||||
public static final String EXTRA_KEY_SENSOR_ID = "sensor_id";
|
public static final String EXTRA_KEY_SENSOR_ID = "sensor_id";
|
||||||
public static final String EXTRA_KEY_CHALLENGE = "challenge";
|
public static final String EXTRA_KEY_CHALLENGE = "challenge";
|
||||||
|
public static final String EXTRA_KEY_MODALITY = "sensor_modality";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the choose fingerprint wizard to indicate the wizard is
|
* Used by the choose fingerprint wizard to indicate the wizard is
|
||||||
@@ -84,11 +88,26 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
*/
|
*/
|
||||||
public static final int RESULT_TIMEOUT = RESULT_FIRST_USER + 2;
|
public static final int RESULT_TIMEOUT = RESULT_FIRST_USER + 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by consent screens to indicate that consent was granted. Extras, such as
|
||||||
|
* EXTRA_KEY_MODALITY, will be included in the result to provide details about the
|
||||||
|
* consent that was granted.
|
||||||
|
*/
|
||||||
|
public static final int RESULT_CONSENT_GRANTED = RESULT_FIRST_USER + 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by consent screens to indicate that consent was denied. Extras, such as
|
||||||
|
* EXTRA_KEY_MODALITY, will be included in the result to provide details about the
|
||||||
|
* consent that was not granted.
|
||||||
|
*/
|
||||||
|
public static final int RESULT_CONSENT_DENIED = RESULT_FIRST_USER + 4;
|
||||||
|
|
||||||
public static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
|
public static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
|
||||||
public static final int BIOMETRIC_FIND_SENSOR_REQUEST = 2;
|
public static final int BIOMETRIC_FIND_SENSOR_REQUEST = 2;
|
||||||
public static final int LEARN_MORE_REQUEST = 3;
|
public static final int LEARN_MORE_REQUEST = 3;
|
||||||
public static final int CONFIRM_REQUEST = 4;
|
public static final int CONFIRM_REQUEST = 4;
|
||||||
public static final int ENROLL_REQUEST = 5;
|
public static final int ENROLL_REQUEST = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request code when starting another biometric enrollment from within a biometric flow. For
|
* Request code when starting another biometric enrollment from within a biometric flow. For
|
||||||
* example, when starting fingerprint enroll after face enroll.
|
* example, when starting fingerprint enroll after face enroll.
|
||||||
@@ -242,6 +261,8 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void launchConfirmLock(int titleResId) {
|
protected void launchConfirmLock(int titleResId) {
|
||||||
|
Log.d(TAG, "launchConfirmLock");
|
||||||
|
|
||||||
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
|
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
|
||||||
builder.setRequestCode(CONFIRM_REQUEST)
|
builder.setRequestCode(CONFIRM_REQUEST)
|
||||||
.setTitle(getString(titleResId))
|
.setTitle(getString(titleResId))
|
||||||
|
@@ -294,15 +294,19 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
|||||||
mConfirmingCredentials = false;
|
mConfirmingCredentials = false;
|
||||||
if (resultCode == RESULT_FINISHED) {
|
if (resultCode == RESULT_FINISHED) {
|
||||||
updatePasswordQuality();
|
updatePasswordQuality();
|
||||||
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
final boolean handled = onSetOrConfirmCredentials(data);
|
||||||
getNextButton().setEnabled(false);
|
if (!handled) {
|
||||||
getChallenge(((sensorId, userId, challenge) -> {
|
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
||||||
mSensorId = sensorId;
|
getNextButton().setEnabled(false);
|
||||||
mChallenge = challenge;
|
getChallenge(((sensorId, userId, challenge) -> {
|
||||||
mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId, challenge);
|
mSensorId = sensorId;
|
||||||
BiometricUtils.removeGatekeeperPasswordHandle(this, data);
|
mChallenge = challenge;
|
||||||
getNextButton().setEnabled(true);
|
mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId,
|
||||||
}));
|
challenge);
|
||||||
|
BiometricUtils.removeGatekeeperPasswordHandle(this, data);
|
||||||
|
getNextButton().setEnabled(true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setResult(resultCode, data);
|
setResult(resultCode, data);
|
||||||
finish();
|
finish();
|
||||||
@@ -310,15 +314,19 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
|||||||
} else if (requestCode == CONFIRM_REQUEST) {
|
} else if (requestCode == CONFIRM_REQUEST) {
|
||||||
mConfirmingCredentials = false;
|
mConfirmingCredentials = false;
|
||||||
if (resultCode == RESULT_OK && data != null) {
|
if (resultCode == RESULT_OK && data != null) {
|
||||||
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
final boolean handled = onSetOrConfirmCredentials(data);
|
||||||
getNextButton().setEnabled(false);
|
if (!handled) {
|
||||||
getChallenge(((sensorId, userId, challenge) -> {
|
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
|
||||||
mSensorId = sensorId;
|
getNextButton().setEnabled(false);
|
||||||
mChallenge = challenge;
|
getChallenge(((sensorId, userId, challenge) -> {
|
||||||
mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId, challenge);
|
mSensorId = sensorId;
|
||||||
BiometricUtils.removeGatekeeperPasswordHandle(this, data);
|
mChallenge = challenge;
|
||||||
getNextButton().setEnabled(true);
|
mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId,
|
||||||
}));
|
challenge);
|
||||||
|
BiometricUtils.removeGatekeeperPasswordHandle(this, data);
|
||||||
|
getNextButton().setEnabled(true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setResult(resultCode, data);
|
setResult(resultCode, data);
|
||||||
finish();
|
finish();
|
||||||
@@ -335,6 +343,18 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
|||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after confirming credentials. Can be used to prevent the default
|
||||||
|
* behavior of immediately calling #getChallenge (useful to things like intro
|
||||||
|
* consent screens that don't actually do enrollment and will later start an
|
||||||
|
* activity that does).
|
||||||
|
*
|
||||||
|
* @return True if the default behavior should be skipped and handled by this method instead.
|
||||||
|
*/
|
||||||
|
protected boolean onSetOrConfirmCredentials(@Nullable Intent data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected void onCancelButtonClick(View view) {
|
protected void onCancelButtonClick(View view) {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
168
src/com/android/settings/biometrics/ParentalConsentHelper.java
Normal file
168
src/com/android/settings/biometrics/ParentalConsentHelper.java
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics;
|
||||||
|
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
|
||||||
|
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_MODALITY;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.face.FaceEnrollParentalConsent;
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollParentalConsent;
|
||||||
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
|
|
||||||
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for {@link BiometricEnrollActivity} to ask for parental consent prior to actual user
|
||||||
|
* enrollment.
|
||||||
|
*/
|
||||||
|
public class ParentalConsentHelper {
|
||||||
|
|
||||||
|
private static final String KEY_FACE_CONSENT = "face";
|
||||||
|
private static final String KEY_FINGERPRINT_CONSENT = "fingerprint";
|
||||||
|
|
||||||
|
private final boolean mRequireFace;
|
||||||
|
private final boolean mRequireFingerprint;
|
||||||
|
|
||||||
|
private long mGkPwHandle;
|
||||||
|
@Nullable
|
||||||
|
private Boolean mConsentFace;
|
||||||
|
@Nullable
|
||||||
|
private Boolean mConsentFingerprint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for aggregating user consent.
|
||||||
|
*
|
||||||
|
* @param requireFace if face consent should be shown
|
||||||
|
* @param requireFingerprint if fingerprint consent should be shown
|
||||||
|
* @param gkPwHandle for launched intents
|
||||||
|
*/
|
||||||
|
public ParentalConsentHelper(boolean requireFace, boolean requireFingerprint,
|
||||||
|
@Nullable Long gkPwHandle) {
|
||||||
|
mRequireFace = requireFace;
|
||||||
|
mRequireFingerprint = requireFingerprint;
|
||||||
|
mGkPwHandle = gkPwHandle != null ? gkPwHandle : 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updated the handle used for launching activities
|
||||||
|
*
|
||||||
|
* @param data result intent for credential verification
|
||||||
|
*/
|
||||||
|
public void updateGatekeeperHandle(Intent data) {
|
||||||
|
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch the next consent screen.
|
||||||
|
*
|
||||||
|
* @param activity root activity
|
||||||
|
* @param requestCode request code to launch new activity
|
||||||
|
* @param resultCode result code of the last consent launch
|
||||||
|
* @param data result data from the last consent launch
|
||||||
|
* @return true if a consent activity was launched or false when complete
|
||||||
|
*/
|
||||||
|
public boolean launchNext(@NonNull Activity activity, int requestCode, int resultCode,
|
||||||
|
@Nullable Intent data) {
|
||||||
|
if (data != null) {
|
||||||
|
switch (data.getIntExtra(EXTRA_KEY_MODALITY, TYPE_NONE)) {
|
||||||
|
case TYPE_FACE:
|
||||||
|
mConsentFace = isConsent(resultCode, mConsentFace);
|
||||||
|
break;
|
||||||
|
case TYPE_FINGERPRINT:
|
||||||
|
mConsentFingerprint = isConsent(resultCode, mConsentFingerprint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return launchNext(activity, requestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Boolean isConsent(int resultCode, @Nullable Boolean defaultValue) {
|
||||||
|
switch (resultCode) {
|
||||||
|
case RESULT_CONSENT_GRANTED:
|
||||||
|
return true;
|
||||||
|
case RESULT_CONSENT_DENIED:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see #launchNext(Activity, int, int, Intent) */
|
||||||
|
public boolean launchNext(@NonNull Activity activity, int requestCode) {
|
||||||
|
final Intent intent = getNextConsentIntent(activity);
|
||||||
|
if (intent != null) {
|
||||||
|
WizardManagerHelper.copyWizardManagerExtras(activity.getIntent(), intent);
|
||||||
|
if (mGkPwHandle != 0) {
|
||||||
|
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
|
||||||
|
}
|
||||||
|
activity.startActivityForResult(intent, requestCode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Intent getNextConsentIntent(@NonNull Context context) {
|
||||||
|
if (mRequireFace && mConsentFace == null) {
|
||||||
|
return new Intent(context, FaceEnrollParentalConsent.class);
|
||||||
|
}
|
||||||
|
if (mRequireFingerprint && mConsentFingerprint == null) {
|
||||||
|
return new Intent(context, FingerprintEnrollParentalConsent.class);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the result of all consent requests.
|
||||||
|
*
|
||||||
|
* This should be called when {@link #launchNext(Activity, int, int, Intent)} returns false
|
||||||
|
* to indicate that all responses have been recorded.
|
||||||
|
*
|
||||||
|
* @return The aggregate consent status.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Bundle getConsentResult() {
|
||||||
|
final Bundle result = new Bundle();
|
||||||
|
result.putBoolean(KEY_FACE_CONSENT, mConsentFace != null ? mConsentFace : false);
|
||||||
|
result.putBoolean(KEY_FINGERPRINT_CONSENT,
|
||||||
|
mConsentFingerprint != null ? mConsentFingerprint : false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return If the result bundle contains consent for face authentication. */
|
||||||
|
public static boolean hasFaceConsent(@NonNull Bundle bundle) {
|
||||||
|
return bundle.getBoolean(KEY_FACE_CONSENT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return If the result bundle contains consent for fingerprint authentication. */
|
||||||
|
public static boolean hasFingerprintConsent(@NonNull Bundle bundle) {
|
||||||
|
return bundle.getBoolean(KEY_FINGERPRINT_CONSENT, false);
|
||||||
|
}
|
||||||
|
}
|
@@ -85,22 +85,28 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
|||||||
mFaceFeatureProvider = FeatureFactory.getFactory(getApplicationContext())
|
mFaceFeatureProvider = FeatureFactory.getFactory(getApplicationContext())
|
||||||
.getFaceFeatureProvider();
|
.getFaceFeatureProvider();
|
||||||
|
|
||||||
|
|
||||||
// This path is an entry point for SetNewPasswordController, e.g.
|
// This path is an entry point for SetNewPasswordController, e.g.
|
||||||
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
|
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
|
||||||
if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
|
if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
|
||||||
mFooterBarMixin.getPrimaryButton().setEnabled(false);
|
if (generateChallengeOnCreate()) {
|
||||||
// We either block on generateChallenge, or need to gray out the "next" button until
|
mFooterBarMixin.getPrimaryButton().setEnabled(false);
|
||||||
// the challenge is ready. Let's just do this for now.
|
// We either block on generateChallenge, or need to gray out the "next" button until
|
||||||
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
|
// the challenge is ready. Let's just do this for now.
|
||||||
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);
|
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
|
||||||
mSensorId = sensorId;
|
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId,
|
||||||
mChallenge = challenge;
|
challenge);
|
||||||
mFooterBarMixin.getPrimaryButton().setEnabled(true);
|
mSensorId = sensorId;
|
||||||
});
|
mChallenge = challenge;
|
||||||
|
mFooterBarMixin.getPrimaryButton().setEnabled(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean generateChallengeOnCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isDisabledByAdmin() {
|
protected boolean isDisabledByAdmin() {
|
||||||
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
|
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
|
||||||
|
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.face;
|
||||||
|
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays parental consent information for face authentication.
|
||||||
|
*
|
||||||
|
* TODO(b/188847063): swap strings for consent screen
|
||||||
|
*/
|
||||||
|
public class FaceEnrollParentalConsent extends FaceEnrollIntroduction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNextButtonClick(View view) {
|
||||||
|
onConsentResult(true /* granted */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSkipButtonClick(View view) {
|
||||||
|
onConsentResult(false /* granted */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConsentResult(boolean granted) {
|
||||||
|
final Intent result = new Intent();
|
||||||
|
result.putExtra(EXTRA_KEY_MODALITY, TYPE_FACE);
|
||||||
|
setResult(granted ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED, result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onSetOrConfirmCredentials(@Nullable Intent data) {
|
||||||
|
// prevent challenge from being generated by default
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean generateChallengeOnCreate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getHeaderResDefault() {
|
||||||
|
return R.string.security_settings_face_enroll_consent_introduction_title;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.fingerprint;
|
||||||
|
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays parental consent information for fingerprint authentication.
|
||||||
|
*
|
||||||
|
* TODO(b/188847063): swap strings for consent screen
|
||||||
|
*/
|
||||||
|
public class FingerprintEnrollParentalConsent extends FingerprintEnrollIntroduction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNextButtonClick(View view) {
|
||||||
|
onConsentResult(true /* granted */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSkipButtonClick(View view) {
|
||||||
|
onConsentResult(false /* granted */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConsentResult(boolean granted) {
|
||||||
|
final Intent result = new Intent();
|
||||||
|
result.putExtra(EXTRA_KEY_MODALITY, TYPE_FINGERPRINT);
|
||||||
|
setResult(granted ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED, result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onSetOrConfirmCredentials(@Nullable Intent data) {
|
||||||
|
// prevent challenge from being generated by default
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getHeaderResDefault() {
|
||||||
|
return R.string.security_settings_fingerprint_enroll_consent_introduction_title;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics;
|
||||||
|
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||||
|
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
|
||||||
|
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_MODALITY;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.hardware.biometrics.BiometricAuthenticator;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.settings.biometrics.face.FaceEnrollParentalConsent;
|
||||||
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollParentalConsent;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ParentalConsentHelperTest {
|
||||||
|
|
||||||
|
private static final int REQUEST_CODE = 12;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final MockitoRule mMocks = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Activity mRootActivity;
|
||||||
|
@Mock
|
||||||
|
private Intent mRootActivityIntent;
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<Intent> mLastStarted;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
when(mRootActivity.getIntent()).thenAnswer(invocation -> mRootActivityIntent);
|
||||||
|
when(mRootActivityIntent.getBundleExtra(any())).thenAnswer(invocation -> null);
|
||||||
|
when(mRootActivityIntent.getStringExtra(any())).thenAnswer(invocation -> null);
|
||||||
|
when(mRootActivityIntent.getBooleanExtra(any(), anyBoolean()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArguments()[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_face_and_fingerprint_all_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
true /* requireFace */, true /* grantFace */,
|
||||||
|
true /* requireFingerprint */, true /* grantFace */,
|
||||||
|
90 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_nothing_to_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
false /* requireFace */, false /* grantFace */,
|
||||||
|
false /* requireFingerprint */, false /* grantFace */,
|
||||||
|
80 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_face_and_fingerprint_no_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
true /* requireFace */, false /* grantFace */,
|
||||||
|
true /* requireFingerprint */, false /* grantFace */,
|
||||||
|
70 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_face_and_fingerprint_only_face_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
true /* requireFace */, true /* grantFace */,
|
||||||
|
true /* requireFingerprint */, false /* grantFace */,
|
||||||
|
60 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_face_and_fingerprint_only_fingerprint_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
true /* requireFace */, false /* grantFace */,
|
||||||
|
true /* requireFingerprint */, true /* grantFace */,
|
||||||
|
50 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_face_with_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
true /* requireFace */, true /* grantFace */,
|
||||||
|
false /* requireFingerprint */, false /* grantFace */,
|
||||||
|
40 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_face_without_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
true /* requireFace */, false /* grantFace */,
|
||||||
|
false /* requireFingerprint */, false /* grantFace */,
|
||||||
|
30 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_fingerprint_with_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
false /* requireFace */, false /* grantFace */,
|
||||||
|
true /* requireFingerprint */, true /* grantFace */,
|
||||||
|
20 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLaunchNext_fingerprint_without_consent() {
|
||||||
|
testLaunchNext(
|
||||||
|
false /* requireFace */, false /* grantFace */,
|
||||||
|
true /* requireFingerprint */, false /* grantFace */,
|
||||||
|
10 /* gkpw */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testLaunchNext(
|
||||||
|
boolean requireFace, boolean grantFace,
|
||||||
|
boolean requireFingerprint, boolean grantFingerprint,
|
||||||
|
long gkpw) {
|
||||||
|
final List<Pair<String, Boolean>> expectedLaunches = new ArrayList<>();
|
||||||
|
if (requireFace) {
|
||||||
|
expectedLaunches.add(new Pair(FaceEnrollParentalConsent.class.getName(), grantFace));
|
||||||
|
}
|
||||||
|
if (requireFingerprint) {
|
||||||
|
expectedLaunches.add(
|
||||||
|
new Pair(FingerprintEnrollParentalConsent.class.getName(), grantFingerprint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial consent status
|
||||||
|
final ParentalConsentHelper helper =
|
||||||
|
new ParentalConsentHelper(requireFace, requireFingerprint, gkpw);
|
||||||
|
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
|
||||||
|
.isFalse();
|
||||||
|
|
||||||
|
// check expected launches
|
||||||
|
for (int i = 0; i <= expectedLaunches.size(); i++) {
|
||||||
|
final Pair<String, Boolean> expected = i > 0 ? expectedLaunches.get(i - 1) : null;
|
||||||
|
final boolean launchedNext = i == 0
|
||||||
|
? helper.launchNext(mRootActivity, REQUEST_CODE)
|
||||||
|
: helper.launchNext(mRootActivity, REQUEST_CODE,
|
||||||
|
expected.second ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED,
|
||||||
|
getResultIntent(getStartedModality(expected.first)));
|
||||||
|
assertThat(launchedNext).isEqualTo(i < expectedLaunches.size());
|
||||||
|
}
|
||||||
|
verify(mRootActivity, times(expectedLaunches.size()))
|
||||||
|
.startActivityForResult(mLastStarted.capture(), eq(REQUEST_CODE));
|
||||||
|
assertThat(mLastStarted.getAllValues()
|
||||||
|
.stream().map(i -> i.getComponent().getClassName()).collect(Collectors.toList()))
|
||||||
|
.containsExactlyElementsIn(
|
||||||
|
expectedLaunches.stream().map(i -> i.first).collect(Collectors.toList()))
|
||||||
|
.inOrder();
|
||||||
|
if (!expectedLaunches.isEmpty()) {
|
||||||
|
assertThat(mLastStarted.getAllValues()
|
||||||
|
.stream().map(BiometricUtils::getGatekeeperPasswordHandle).distinct()
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
.containsExactly(gkpw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// final consent status
|
||||||
|
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
|
||||||
|
.isEqualTo(requireFace && grantFace);
|
||||||
|
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
|
||||||
|
.isEqualTo(requireFingerprint && grantFingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Intent getResultIntent(@BiometricAuthenticator.Modality int modality) {
|
||||||
|
final Intent intent = new Intent();
|
||||||
|
intent.putExtra(EXTRA_KEY_MODALITY, modality);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BiometricAuthenticator.Modality
|
||||||
|
private static int getStartedModality(String name) {
|
||||||
|
if (name.equals(FaceEnrollParentalConsent.class.getName())) {
|
||||||
|
return TYPE_FACE;
|
||||||
|
}
|
||||||
|
if (name.equals(FingerprintEnrollParentalConsent.class.getName())) {
|
||||||
|
return TYPE_FINGERPRINT;
|
||||||
|
}
|
||||||
|
return TYPE_NONE;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user