Launch settings for clicking fingerprint unlock

Launch FingerprintSettings directly when user clicks "Fingerprint
Unlock". Let Fingerprint settings peform confirmLock() or chooseLock().
And to have smoothly animation, instead of generating challenge in
FingerprintSettings::onActivityResult(), challenge is generated in next
visible activity, and pass it back through next activity result.

Bug: 197717071
Test: atest GatekeeperPasswordProviderTest CredentialModelTest
Test: atest AutoCredentialViewModelTest FingerprintStatusUtilsTest
Test: RunSettingsRoboTests2 FingerprintEnrollIntroductionTest
Test: Manually test fingerprint enroll in settings or suw
Change-Id: Ie27c3c493ea475f6b53cb6bb3f2d45d555f47cb3
This commit is contained in:
Milton Wu
2022-12-05 03:09:58 +00:00
parent 58c3318e19
commit 160661dc6d
14 changed files with 899 additions and 131 deletions

View File

@@ -50,6 +50,7 @@ public final class CredentialModel {
/**
* Default value if GkPwHandle is invalid.
*/
@VisibleForTesting
public static final long INVALID_GK_PW_HANDLE = 0L;
/**
@@ -115,8 +116,8 @@ public final class CredentialModel {
/**
* Check user id is valid or not
*/
public static boolean isValidUserId(int userId) {
return userId != UserHandle.USER_NULL;
public boolean isValidUserId() {
return mUserId != UserHandle.USER_NULL;
}
/**
@@ -134,6 +135,13 @@ public final class CredentialModel {
mChallenge = value;
}
/**
* Check challenge is valid or not
*/
public boolean isValidChallenge() {
return mChallenge != INVALID_CHALLENGE;
}
/**
* Get challenge token
*/
@@ -153,8 +161,8 @@ public final class CredentialModel {
/**
* Check challengeToken is valid or not
*/
public static boolean isValidToken(@Nullable byte[] token) {
return token != null;
public boolean isValidToken() {
return mToken != null;
}
/**
@@ -175,8 +183,8 @@ public final class CredentialModel {
/**
* Check gkPwHandle is valid or not
*/
public static boolean isValidGkPwHandle(long gkPwHandle) {
return gkPwHandle != INVALID_GK_PW_HANDLE;
public boolean isValidGkPwHandle() {
return mGkPwHandle != INVALID_GK_PW_HANDLE;
}
/**
@@ -206,10 +214,10 @@ public final class CredentialModel {
+ ", userId:" + mUserId
+ ", challenge:{len:" + challengeLen
+ ", updateMillis:" + mUpdateChallengeMillis + "}"
+ ", token:{len:" + tokenLen + ", isValid:" + isValidToken(mToken)
+ ", token:{len:" + tokenLen + ", isValid:" + isValidToken()
+ ", updateMillis:" + mUpdateTokenMillis + "}"
+ ", gkPwHandle:{len:" + gkPwHandleLen + ", isValid:"
+ isValidGkPwHandle(mGkPwHandle) + ", clearMillis:" + mClearGkPwHandleMillis + "}"
+ ", gkPwHandle:{len:" + gkPwHandleLen + ", isValid:" + isValidGkPwHandle()
+ ", clearMillis:" + mClearGkPwHandleMillis + "}"
+ ", mSensorId:{id:" + mSensorId + ", updateMillis:" + mUpdateSensorIdMillis + "}"
+ " }";
}

View File

@@ -98,7 +98,6 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
checkCredential();
// Theme
setTheme(mViewModel.getRequest().getTheme());
@@ -107,12 +106,14 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
// fragment
setContentView(R.layout.biometric_enrollment_container);
final FingerprintEnrollIntroViewModel introViewModel =
viewModelProvider.get(FingerprintEnrollIntroViewModel.class);
introViewModel.setEnrollmentRequest(mViewModel.getRequest());
introViewModel.setUserId(mAutoCredentialViewModel.getUserId());
if (savedInstanceState == null) {
checkCredential();
final String tag = "FingerprintEnrollIntroFragment";
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
@@ -138,11 +139,34 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
/**
* Get intent which passing back to FingerprintSettings for late generateChallenge()
*/
@Nullable
private Intent createSetResultIntentWithGeneratingChallengeExtra(
@Nullable Intent activityResultIntent) {
if (!mViewModel.getRequest().isFromSettingsSummery()) {
return activityResultIntent;
}
final Bundle extra = mAutoCredentialViewModel.createGeneratingChallengeExtras();
if (extra != null) {
if (activityResultIntent == null) {
activityResultIntent = new Intent();
}
activityResultIntent.putExtras(extra);
}
return activityResultIntent;
}
private void onSetActivityResult(@NonNull ActivityResult result) {
setResult(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
? RESULT_CANCELED
: result.getResultCode(),
result.getData());
final int resultCode = mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
? RESULT_CANCELED
: result.getResultCode();
final Intent intent = resultCode == BiometricEnrollBase.RESULT_FINISHED
? createSetResultIntentWithGeneratingChallengeExtra(result.getData())
: result.getData();
setResult(resultCode, intent);
finish();
}

View File

@@ -65,6 +65,10 @@ public class AutoCredentialViewModel extends AndroidViewModel {
@VisibleForTesting
static final String KEY_CREDENTIAL_MODEL = "credential_model";
@VisibleForTesting
static final String KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL =
"is_generating_challenge_during_checking_credential";
private static final boolean DEBUG = false;
/**
@@ -173,6 +177,9 @@ public class AutoCredentialViewModel extends AndroidViewModel {
@NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailedLiveData =
new MutableLiveData<>();
// flag if token is generating through checkCredential()'s generateChallenge()
private boolean mIsGeneratingChallengeDuringCheckingCredential;
public AutoCredentialViewModel(
@NonNull Application application,
@NonNull LockPatternUtils lockPatternUtils,
@@ -189,10 +196,13 @@ public class AutoCredentialViewModel extends AndroidViewModel {
final Bundle bundle;
if (savedInstanceState != null) {
bundle = savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL);
mIsGeneratingChallengeDuringCheckingCredential = savedInstanceState.getBoolean(
KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL);
} else {
bundle = intent.getExtras();
}
mCredentialModel = new CredentialModel(bundle, SystemClock.elapsedRealtimeClock());
mCredentialModel = new CredentialModel(bundle != null ? bundle : new Bundle(),
SystemClock.elapsedRealtimeClock());
if (DEBUG) {
Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:"
@@ -204,6 +214,8 @@ public class AutoCredentialViewModel extends AndroidViewModel {
* Handle onSaveInstanceState from activity
*/
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(KEY_IS_GENERATING_CHALLENGE_DURING_CHECKING_CREDENTIAL,
mIsGeneratingChallengeDuringCheckingCredential);
outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle());
}
@@ -212,6 +224,24 @@ public class AutoCredentialViewModel extends AndroidViewModel {
return mGenerateChallengeFailedLiveData;
}
/**
* Get bundle which passing back to FingerprintSettings for late generateChallenge()
*/
@Nullable
public Bundle createGeneratingChallengeExtras() {
if (!mIsGeneratingChallengeDuringCheckingCredential
|| !mCredentialModel.isValidToken()
|| !mCredentialModel.isValidChallenge()) {
return null;
}
Bundle bundle = new Bundle();
bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
mCredentialModel.getToken());
bundle.putLong(EXTRA_KEY_CHALLENGE, mCredentialModel.getChallenge());
return bundle;
}
/**
* Check credential status for biometric enrollment.
*/
@@ -220,11 +250,11 @@ public class AutoCredentialViewModel extends AndroidViewModel {
if (isValidCredential()) {
return CREDENTIAL_VALID;
}
final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (isUnspecifiedPassword()) {
return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
} else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
generateChallenge(gkPwHandle);
} else if (mCredentialModel.isValidGkPwHandle()) {
generateChallenge(mCredentialModel.getGkPwHandle());
mIsGeneratingChallengeDuringCheckingCredential = true;
return CREDENTIAL_IS_GENERATING_CHALLENGE;
} else {
return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
@@ -261,8 +291,7 @@ public class AutoCredentialViewModel extends AndroidViewModel {
}
private boolean isValidCredential() {
return !isUnspecifiedPassword()
&& CredentialModel.isValidToken(mCredentialModel.getToken());
return !isUnspecifiedPassword() && mCredentialModel.isValidToken();
}
private boolean isUnspecifiedPassword() {
@@ -316,17 +345,14 @@ public class AutoCredentialViewModel extends AndroidViewModel {
@NonNull
public Bundle createCredentialIntentExtra() {
final Bundle retBundle = new Bundle();
final long gkPwHandle = mCredentialModel.getGkPwHandle();
if (CredentialModel.isValidGkPwHandle(gkPwHandle)) {
retBundle.putLong(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
if (mCredentialModel.isValidGkPwHandle()) {
retBundle.putLong(EXTRA_KEY_GK_PW_HANDLE, mCredentialModel.getGkPwHandle());
}
final byte[] token = mCredentialModel.getToken();
if (CredentialModel.isValidToken(token)) {
retBundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, token);
if (mCredentialModel.isValidToken()) {
retBundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, mCredentialModel.getToken());
}
final int userId = getUserId();
if (CredentialModel.isValidUserId(userId)) {
retBundle.putInt(Intent.EXTRA_USER_ID, userId);
if (mCredentialModel.isValidUserId()) {
retBundle.putInt(Intent.EXTRA_USER_ID, mCredentialModel.getUserId());
}
retBundle.putLong(EXTRA_KEY_CHALLENGE, mCredentialModel.getChallenge());
retBundle.putInt(EXTRA_KEY_SENSOR_ID, mCredentialModel.getSensorId());
@@ -346,9 +372,8 @@ public class AutoCredentialViewModel extends AndroidViewModel {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
final int userId = getUserId();
if (CredentialModel.isValidUserId(userId)) {
intent.putExtra(Intent.EXTRA_USER_ID, userId);
if (mCredentialModel.isValidUserId()) {
intent.putExtra(Intent.EXTRA_USER_ID, mCredentialModel.getUserId());
}
return intent;
}
@@ -367,9 +392,8 @@ public class AutoCredentialViewModel extends AndroidViewModel {
.setForegroundOnly(true)
.setReturnCredentials(true);
final int userId = mCredentialModel.getUserId();
if (CredentialModel.isValidUserId(userId)) {
builder.setUserId(userId);
if (mCredentialModel.isValidUserId()) {
builder.setUserId(mCredentialModel.getUserId());
}
return builder.build();
}

View File

@@ -104,7 +104,7 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel implements
* Handle activity result from FingerprintFindSensor
*/
public void onContinueEnrollActivityResult(@NonNull ActivityResult result, int userId) {
if (mIsWaitingActivityResult.compareAndSet(true, false)) {
if (!mIsWaitingActivityResult.compareAndSet(true, false)) {
Log.w(TAG, "fail to reset isWaiting flag for enrollment");
}
if (result.getResultCode() == RESULT_FINISHED