Loading src/com/android/settings/biometrics2/factory/BiometricsFragmentFactory.javadeleted 100644 → 0 +0 −57 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.biometrics2.factory; import android.app.Application; import android.app.admin.DevicePolicyManager; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentFactory; import androidx.lifecycle.ViewModelProvider; import com.android.settings.biometrics2.ui.view.FingerprintEnrollIntroFragment; /** * Fragment factory for biometrics */ public class BiometricsFragmentFactory extends FragmentFactory { private final Application mApplication; private final ViewModelProvider mViewModelProvider; public BiometricsFragmentFactory(Application application, ViewModelProvider viewModelProvider) { mApplication = application; mViewModelProvider = viewModelProvider; } @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { final Class<? extends Fragment> clazz = loadFragmentClass(classLoader, className); if (FingerprintEnrollIntroFragment.class.equals(clazz)) { final DevicePolicyManager devicePolicyManager = mApplication.getSystemService(DevicePolicyManager.class); if (devicePolicyManager != null) { return new FingerprintEnrollIntroFragment(mViewModelProvider, devicePolicyManager.getResources()); } } return super.instantiate(classLoader, className); } } src/com/android/settings/biometrics2/ui/model/CredentialModel.java +21 −7 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_C import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; import androidx.annotation.NonNull; Loading Loading @@ -80,17 +81,30 @@ public final class CredentialModel { @Nullable private Long mClearGkPwHandleMillis = null; public CredentialModel(@NonNull Intent intent, @NonNull Clock clock) { mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); mSensorId = intent.getIntExtra(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID); mChallenge = intent.getLongExtra(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE); mToken = intent.getByteArrayExtra(EXTRA_KEY_CHALLENGE_TOKEN); mGkPwHandle = intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE); public CredentialModel(@NonNull Bundle bundle, @NonNull Clock clock) { mUserId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId()); mSensorId = bundle.getInt(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID); mChallenge = bundle.getLong(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE); mToken = bundle.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN); mGkPwHandle = bundle.getLong(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE); mClock = clock; mInitMillis = mClock.millis(); } /** * Get a bundle which can be used to recreate CredentialModel */ @NonNull public Bundle getBundle() { final Bundle bundle = new Bundle(); bundle.putInt(Intent.EXTRA_USER_ID, mUserId); bundle.putInt(EXTRA_KEY_SENSOR_ID, mSensorId); bundle.putLong(EXTRA_KEY_CHALLENGE, mChallenge); bundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, mToken); bundle.putLong(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle); return bundle; } /** * Get userId for this credential */ Loading src/com/android/settings/biometrics2/ui/view/FingerprintEnrollIntroFragment.java +11 −13 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroSt import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT; import android.app.Activity; import android.app.admin.DevicePolicyResourcesManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; Loading Loading @@ -63,9 +63,6 @@ public class FingerprintEnrollIntroFragment extends Fragment { private static final String TAG = "FingerprintEnrollIntroFragment"; @NonNull private final ViewModelProvider mViewModelProvider; @Nullable private final DevicePolicyResourcesManager mDevicePolicyMgrRes; private FingerprintEnrollIntroViewModel mViewModel = null; private View mView = null; Loading @@ -75,12 +72,8 @@ public class FingerprintEnrollIntroFragment extends Fragment { private TextView mFooterMessage6 = null; @Nullable private PorterDuffColorFilter mIconColorFilter; public FingerprintEnrollIntroFragment( @NonNull ViewModelProvider viewModelProvider, @Nullable DevicePolicyResourcesManager devicePolicyMgrRes) { public FingerprintEnrollIntroFragment() { super(); mViewModelProvider = viewModelProvider; mDevicePolicyMgrRes = devicePolicyMgrRes; } @Nullable Loading Loading @@ -197,7 +190,8 @@ public class FingerprintEnrollIntroFragment extends Fragment { @Override public void onAttach(@NonNull Context context) { mViewModel = mViewModelProvider.get(FingerprintEnrollIntroViewModel.class); mViewModel = new ViewModelProvider(getActivity()) .get(FingerprintEnrollIntroViewModel.class); getLifecycle().addObserver(mViewModel); super.onAttach(context); } Loading Loading @@ -232,12 +226,16 @@ public class FingerprintEnrollIntroFragment extends Fragment { private String getDescriptionDisabledByAdmin(@NonNull Context context) { final int defaultStrId = R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled; if (mDevicePolicyMgrRes == null) { final DevicePolicyManager devicePolicyManager = getActivity() .getSystemService(DevicePolicyManager.class); if (devicePolicyManager != null) { return devicePolicyManager.getResources().getString(FINGERPRINT_UNLOCK_DISABLED, () -> context.getString(defaultStrId)); } else { Log.w(TAG, "getDescriptionDisabledByAdmin, null device policy manager res"); return ""; } return mDevicePolicyMgrRes.getString(FINGERPRINT_UNLOCK_DISABLED, () -> context.getString(defaultStrId)); } private void setHeaderText(@NonNull Activity activity, int resId) { Loading src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java +37 −30 Original line number Diff line number Diff line Loading @@ -19,20 +19,19 @@ package com.android.settings.biometrics2.ui.view; import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY; import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL; import android.app.Activity; import android.app.Application; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Color; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import androidx.activity.result.ActivityResult; Loading @@ -42,7 +41,6 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.viewmodel.CreationExtras; import androidx.lifecycle.viewmodel.MutableCreationExtras; Loading @@ -51,11 +49,9 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor; import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.factory.BiometricsFragmentFactory; import com.android.settings.biometrics2.factory.BiometricsViewModelFactory; import com.android.settings.biometrics2.ui.model.CredentialModel; import com.android.settings.biometrics2.ui.model.EnrollmentRequest; import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel; import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator; Loading Loading @@ -102,12 +98,12 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { getLifecycle().addObserver(mViewModel); mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class); mAutoCredentialViewModel.setCredentialModel(new CredentialModel(getIntent(), SystemClock.elapsedRealtimeClock())); getLifecycle().addObserver(mAutoCredentialViewModel); mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent()); mAutoCredentialViewModel.getGenerateChallengeFailLiveData().observe(this, this::onGenerateChallengeFail); mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult); mAutoCredentialViewModel.getActionLiveData().observe(this, this::onCredentialAction); checkCredential(); // Theme setTheme(mViewModel.getRequest().getTheme()); Loading @@ -116,22 +112,30 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { // fragment setContentView(R.layout.biometric_enrollment_container); final FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.setFragmentFactory( new BiometricsFragmentFactory(getApplication(), viewModelProvider)); final FingerprintEnrollIntroViewModel fingerprintEnrollIntroViewModel = viewModelProvider.get(FingerprintEnrollIntroViewModel.class); fingerprintEnrollIntroViewModel.setEnrollmentRequest(mViewModel.getRequest()); fingerprintEnrollIntroViewModel.setUserId(mAutoCredentialViewModel.getUserId()); // Clear ActionLiveData in FragmentViewModel to prevent getting previous action when // recreate fingerprintEnrollIntroViewModel.clearActionLiveData(); fingerprintEnrollIntroViewModel.getActionLiveData().observe( this, this::observeIntroAction); if (savedInstanceState == null) { final String tag = "FingerprintEnrollIntroFragment"; fragmentManager.beginTransaction() getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag) .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag) .commit(); } } private void onGenerateChallengeFail(@NonNull Boolean isFail) { onSetActivityResult(new ActivityResult(RESULT_CANCELED, null)); } private void onSetActivityResult(@NonNull ActivityResult result) { setResult(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction() Loading @@ -141,8 +145,8 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { finish(); } private void onCredentialAction(@NonNull Integer action) { switch (action) { private void checkCredential() { switch (mAutoCredentialViewModel.checkCredential()) { case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: { final Intent intent = mAutoCredentialViewModel.getChooseLockIntent(this, mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras()); Loading @@ -168,12 +172,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { } return; } case CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE: { Log.w(TAG, "observeCredentialLiveData, finish with action:" + action); if (mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()) { setResult(Activity.RESULT_CANCELED); } finish(); case CREDENTIAL_VALID: case CREDENTIAL_IS_GENERATING_CHALLENGE: { // Do nothing } } } Loading @@ -186,10 +187,15 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult( isChooseLock, activityResult)) { overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); } else { onSetActivityResult(activityResult); } } private void observeIntroAction(@NonNull Integer action) { private void observeIntroAction(@Nullable Integer action) { if (action == null) { return; } switch (action) { case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: { onSetActivityResult( Loading @@ -207,9 +213,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag"); } final Intent intent = new Intent(this, isSuw ? SetupFingerprintEnrollEnrolling.class ? SetupFingerprintEnrollFindSensor.class : FingerprintEnrollFindSensor.class); intent.putExtras(mAutoCredentialViewModel.getCredentialBundle()); intent.putExtras(mAutoCredentialViewModel.getCredentialIntentExtra()); intent.putExtras(mViewModel.getNextActivityBaseIntentExtras()); mNextActivityLauncher.launch(intent); } Loading Loading @@ -272,5 +278,6 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mViewModel.onSaveInstanceState(outState); mAutoCredentialViewModel.onSaveInstanceState(outState); } } src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java +57 −36 Original line number Diff line number Diff line Loading @@ -30,20 +30,21 @@ import android.app.Application; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import androidx.activity.result.ActivityResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException; import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.ui.model.CredentialModel; import com.android.settings.password.ChooseLockGeneric; Loading @@ -57,31 +58,40 @@ import java.lang.annotation.RetentionPolicy; * AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like * start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing. */ public class AutoCredentialViewModel extends AndroidViewModel implements DefaultLifecycleObserver { public class AutoCredentialViewModel extends AndroidViewModel { private static final String TAG = "AutoCredentialViewModel"; private static final boolean DEBUG = true; @VisibleForTesting static final String KEY_CREDENTIAL_MODEL = "credential_model"; private static final boolean DEBUG = false; /** * Need activity to run choose lock * Valid credential, activity doesn't need to do anything. */ public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 1; public static final int CREDENTIAL_VALID = 0; /** * Need activity to run confirm lock * This credential looks good, but still need to run generateChallenge(). */ public static final int CREDENTIAL_IS_GENERATING_CHALLENGE = 1; /** * Need activity to run choose lock */ public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 2; public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 2; /** * Fail to use challenge from hardware generateChallenge(), shall finish activity with proper * error code * Need activity to run confirm lock */ public static final int CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE = 3; public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 3; @IntDef(prefix = { "CREDENTIAL_" }, value = { CREDENTIAL_VALID, CREDENTIAL_IS_GENERATING_CHALLENGE, CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK, CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK, CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK }) @Retention(RetentionPolicy.SOURCE) public @interface CredentialAction {} Loading Loading @@ -157,11 +167,10 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default } } @NonNull private final LockPatternUtils mLockPatternUtils; @NonNull private final ChallengeGenerator mChallengeGenerator; private CredentialModel mCredentialModel = null; @NonNull private final MutableLiveData<Integer> mActionLiveData = @NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailLiveData = new MutableLiveData<>(); public AutoCredentialViewModel( Loading @@ -173,51 +182,63 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default mChallengeGenerator = challengeGenerator; } public void setCredentialModel(@NonNull CredentialModel credentialModel) { mCredentialModel = credentialModel; /** * Set CredentialModel, the source is coming from savedInstanceState or activity intent */ public void setCredentialModel(@Nullable Bundle savedInstanceState, @NonNull Intent intent) { mCredentialModel = new CredentialModel( savedInstanceState != null ? savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL) : intent.getExtras(), SystemClock.elapsedRealtimeClock()); if (DEBUG) { Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:" + (savedInstanceState != null)); } } /** * Observe ActionLiveData for actions about choosing lock, confirming lock, or finishing * activity * Handle onSaveInstanceState from activity */ @NonNull public LiveData<Integer> getActionLiveData() { return mActionLiveData; public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle()); } @Override public void onCreate(@NonNull LifecycleOwner owner) { checkCredential(); @NonNull public LiveData<Boolean> getGenerateChallengeFailLiveData() { return mGenerateChallengeFailLiveData; } /** * Check credential status for biometric enrollment. */ private void checkCredential() { @CredentialAction public int checkCredential() { if (isValidCredential()) { return; return CREDENTIAL_VALID; } final long gkPwHandle = mCredentialModel.getGkPwHandle(); if (isUnspecifiedPassword()) { mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK); return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK; } else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) { generateChallenge(gkPwHandle); return CREDENTIAL_IS_GENERATING_CHALLENGE; } else { mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK); return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK; } } private void generateChallenge(long gkPwHandle) { mChallengeGenerator.setCallback((sensorId, userId, challenge) -> { mCredentialModel.setSensorId(sensorId); mCredentialModel.setChallenge(challenge); try { final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId); mCredentialModel.setSensorId(sensorId); mCredentialModel.setChallenge(challenge); mCredentialModel.setToken(newToken); } catch (IllegalStateException e) { Log.e(TAG, "generateChallenge, IllegalStateException", e); mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE); mGenerateChallengeFailLiveData.postValue(true); return; } Loading @@ -231,7 +252,7 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default // Check credential again if (!isValidCredential()) { Log.w(TAG, "generateChallenge, invalid Credential"); mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE); mGenerateChallengeFailLiveData.postValue(true); } }); mChallengeGenerator.generateChallenge(getUserId()); Loading Loading @@ -282,16 +303,16 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default final VerifyCredentialResponse response = mLockPatternUtils .verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId); if (!response.isMatched()) { throw new IllegalStateException("Unable to request Gatekeeper HAT"); throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT"); } return response.getGatekeeperHAT(); } /** * Get Credential bundle which will be used to launch next activity. * Get Credential intent extra which will be used to launch next activity. */ @NonNull public Bundle getCredentialBundle() { public Bundle getCredentialIntentExtra() { final Bundle retBundle = new Bundle(); final long gkPwHandle = mCredentialModel.getGkPwHandle(); if (CredentialModel.isValidGkPwHandle(gkPwHandle)) { Loading Loading
src/com/android/settings/biometrics2/factory/BiometricsFragmentFactory.javadeleted 100644 → 0 +0 −57 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.biometrics2.factory; import android.app.Application; import android.app.admin.DevicePolicyManager; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentFactory; import androidx.lifecycle.ViewModelProvider; import com.android.settings.biometrics2.ui.view.FingerprintEnrollIntroFragment; /** * Fragment factory for biometrics */ public class BiometricsFragmentFactory extends FragmentFactory { private final Application mApplication; private final ViewModelProvider mViewModelProvider; public BiometricsFragmentFactory(Application application, ViewModelProvider viewModelProvider) { mApplication = application; mViewModelProvider = viewModelProvider; } @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { final Class<? extends Fragment> clazz = loadFragmentClass(classLoader, className); if (FingerprintEnrollIntroFragment.class.equals(clazz)) { final DevicePolicyManager devicePolicyManager = mApplication.getSystemService(DevicePolicyManager.class); if (devicePolicyManager != null) { return new FingerprintEnrollIntroFragment(mViewModelProvider, devicePolicyManager.getResources()); } } return super.instantiate(classLoader, className); } }
src/com/android/settings/biometrics2/ui/model/CredentialModel.java +21 −7 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_C import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; import androidx.annotation.NonNull; Loading Loading @@ -80,17 +81,30 @@ public final class CredentialModel { @Nullable private Long mClearGkPwHandleMillis = null; public CredentialModel(@NonNull Intent intent, @NonNull Clock clock) { mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); mSensorId = intent.getIntExtra(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID); mChallenge = intent.getLongExtra(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE); mToken = intent.getByteArrayExtra(EXTRA_KEY_CHALLENGE_TOKEN); mGkPwHandle = intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE); public CredentialModel(@NonNull Bundle bundle, @NonNull Clock clock) { mUserId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId()); mSensorId = bundle.getInt(EXTRA_KEY_SENSOR_ID, INVALID_SENSOR_ID); mChallenge = bundle.getLong(EXTRA_KEY_CHALLENGE, INVALID_CHALLENGE); mToken = bundle.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN); mGkPwHandle = bundle.getLong(EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE); mClock = clock; mInitMillis = mClock.millis(); } /** * Get a bundle which can be used to recreate CredentialModel */ @NonNull public Bundle getBundle() { final Bundle bundle = new Bundle(); bundle.putInt(Intent.EXTRA_USER_ID, mUserId); bundle.putInt(EXTRA_KEY_SENSOR_ID, mSensorId); bundle.putLong(EXTRA_KEY_CHALLENGE, mChallenge); bundle.putByteArray(EXTRA_KEY_CHALLENGE_TOKEN, mToken); bundle.putLong(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle); return bundle; } /** * Get userId for this credential */ Loading
src/com/android/settings/biometrics2/ui/view/FingerprintEnrollIntroFragment.java +11 −13 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroSt import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT; import android.app.Activity; import android.app.admin.DevicePolicyResourcesManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; Loading Loading @@ -63,9 +63,6 @@ public class FingerprintEnrollIntroFragment extends Fragment { private static final String TAG = "FingerprintEnrollIntroFragment"; @NonNull private final ViewModelProvider mViewModelProvider; @Nullable private final DevicePolicyResourcesManager mDevicePolicyMgrRes; private FingerprintEnrollIntroViewModel mViewModel = null; private View mView = null; Loading @@ -75,12 +72,8 @@ public class FingerprintEnrollIntroFragment extends Fragment { private TextView mFooterMessage6 = null; @Nullable private PorterDuffColorFilter mIconColorFilter; public FingerprintEnrollIntroFragment( @NonNull ViewModelProvider viewModelProvider, @Nullable DevicePolicyResourcesManager devicePolicyMgrRes) { public FingerprintEnrollIntroFragment() { super(); mViewModelProvider = viewModelProvider; mDevicePolicyMgrRes = devicePolicyMgrRes; } @Nullable Loading Loading @@ -197,7 +190,8 @@ public class FingerprintEnrollIntroFragment extends Fragment { @Override public void onAttach(@NonNull Context context) { mViewModel = mViewModelProvider.get(FingerprintEnrollIntroViewModel.class); mViewModel = new ViewModelProvider(getActivity()) .get(FingerprintEnrollIntroViewModel.class); getLifecycle().addObserver(mViewModel); super.onAttach(context); } Loading Loading @@ -232,12 +226,16 @@ public class FingerprintEnrollIntroFragment extends Fragment { private String getDescriptionDisabledByAdmin(@NonNull Context context) { final int defaultStrId = R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled; if (mDevicePolicyMgrRes == null) { final DevicePolicyManager devicePolicyManager = getActivity() .getSystemService(DevicePolicyManager.class); if (devicePolicyManager != null) { return devicePolicyManager.getResources().getString(FINGERPRINT_UNLOCK_DISABLED, () -> context.getString(defaultStrId)); } else { Log.w(TAG, "getDescriptionDisabledByAdmin, null device policy manager res"); return ""; } return mDevicePolicyMgrRes.getString(FINGERPRINT_UNLOCK_DISABLED, () -> context.getString(defaultStrId)); } private void setHeaderText(@NonNull Activity activity, int resId) { Loading
src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java +37 −30 Original line number Diff line number Diff line Loading @@ -19,20 +19,19 @@ package com.android.settings.biometrics2.ui.view; import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY; import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE; import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL; import android.app.Activity; import android.app.Application; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Color; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import androidx.activity.result.ActivityResult; Loading @@ -42,7 +41,6 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.viewmodel.CreationExtras; import androidx.lifecycle.viewmodel.MutableCreationExtras; Loading @@ -51,11 +49,9 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling; import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor; import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.factory.BiometricsFragmentFactory; import com.android.settings.biometrics2.factory.BiometricsViewModelFactory; import com.android.settings.biometrics2.ui.model.CredentialModel; import com.android.settings.biometrics2.ui.model.EnrollmentRequest; import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel; import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator; Loading Loading @@ -102,12 +98,12 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { getLifecycle().addObserver(mViewModel); mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class); mAutoCredentialViewModel.setCredentialModel(new CredentialModel(getIntent(), SystemClock.elapsedRealtimeClock())); getLifecycle().addObserver(mAutoCredentialViewModel); mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent()); mAutoCredentialViewModel.getGenerateChallengeFailLiveData().observe(this, this::onGenerateChallengeFail); mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult); mAutoCredentialViewModel.getActionLiveData().observe(this, this::onCredentialAction); checkCredential(); // Theme setTheme(mViewModel.getRequest().getTheme()); Loading @@ -116,22 +112,30 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { // fragment setContentView(R.layout.biometric_enrollment_container); final FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.setFragmentFactory( new BiometricsFragmentFactory(getApplication(), viewModelProvider)); final FingerprintEnrollIntroViewModel fingerprintEnrollIntroViewModel = viewModelProvider.get(FingerprintEnrollIntroViewModel.class); fingerprintEnrollIntroViewModel.setEnrollmentRequest(mViewModel.getRequest()); fingerprintEnrollIntroViewModel.setUserId(mAutoCredentialViewModel.getUserId()); // Clear ActionLiveData in FragmentViewModel to prevent getting previous action when // recreate fingerprintEnrollIntroViewModel.clearActionLiveData(); fingerprintEnrollIntroViewModel.getActionLiveData().observe( this, this::observeIntroAction); if (savedInstanceState == null) { final String tag = "FingerprintEnrollIntroFragment"; fragmentManager.beginTransaction() getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag) .add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null, tag) .commit(); } } private void onGenerateChallengeFail(@NonNull Boolean isFail) { onSetActivityResult(new ActivityResult(RESULT_CANCELED, null)); } private void onSetActivityResult(@NonNull ActivityResult result) { setResult(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction() Loading @@ -141,8 +145,8 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { finish(); } private void onCredentialAction(@NonNull Integer action) { switch (action) { private void checkCredential() { switch (mAutoCredentialViewModel.checkCredential()) { case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: { final Intent intent = mAutoCredentialViewModel.getChooseLockIntent(this, mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras()); Loading @@ -168,12 +172,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { } return; } case CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE: { Log.w(TAG, "observeCredentialLiveData, finish with action:" + action); if (mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()) { setResult(Activity.RESULT_CANCELED); } finish(); case CREDENTIAL_VALID: case CREDENTIAL_IS_GENERATING_CHALLENGE: { // Do nothing } } } Loading @@ -186,10 +187,15 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult( isChooseLock, activityResult)) { overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); } else { onSetActivityResult(activityResult); } } private void observeIntroAction(@NonNull Integer action) { private void observeIntroAction(@Nullable Integer action) { if (action == null) { return; } switch (action) { case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: { onSetActivityResult( Loading @@ -207,9 +213,9 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag"); } final Intent intent = new Intent(this, isSuw ? SetupFingerprintEnrollEnrolling.class ? SetupFingerprintEnrollFindSensor.class : FingerprintEnrollFindSensor.class); intent.putExtras(mAutoCredentialViewModel.getCredentialBundle()); intent.putExtras(mAutoCredentialViewModel.getCredentialIntentExtra()); intent.putExtras(mViewModel.getNextActivityBaseIntentExtras()); mNextActivityLauncher.launch(intent); } Loading Loading @@ -272,5 +278,6 @@ public class FingerprintEnrollmentActivity extends FragmentActivity { protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mViewModel.onSaveInstanceState(outState); mAutoCredentialViewModel.onSaveInstanceState(outState); } }
src/com/android/settings/biometrics2/ui/viewmodel/AutoCredentialViewModel.java +57 −36 Original line number Diff line number Diff line Loading @@ -30,20 +30,21 @@ import android.app.Application; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import androidx.activity.result.ActivityResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException; import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.ui.model.CredentialModel; import com.android.settings.password.ChooseLockGeneric; Loading @@ -57,31 +58,40 @@ import java.lang.annotation.RetentionPolicy; * AutoCredentialViewModel which uses CredentialModel to determine next actions for activity, like * start ChooseLockActivity, start ConfirmLockActivity, GenerateCredential, or do nothing. */ public class AutoCredentialViewModel extends AndroidViewModel implements DefaultLifecycleObserver { public class AutoCredentialViewModel extends AndroidViewModel { private static final String TAG = "AutoCredentialViewModel"; private static final boolean DEBUG = true; @VisibleForTesting static final String KEY_CREDENTIAL_MODEL = "credential_model"; private static final boolean DEBUG = false; /** * Need activity to run choose lock * Valid credential, activity doesn't need to do anything. */ public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 1; public static final int CREDENTIAL_VALID = 0; /** * Need activity to run confirm lock * This credential looks good, but still need to run generateChallenge(). */ public static final int CREDENTIAL_IS_GENERATING_CHALLENGE = 1; /** * Need activity to run choose lock */ public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 2; public static final int CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK = 2; /** * Fail to use challenge from hardware generateChallenge(), shall finish activity with proper * error code * Need activity to run confirm lock */ public static final int CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE = 3; public static final int CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK = 3; @IntDef(prefix = { "CREDENTIAL_" }, value = { CREDENTIAL_VALID, CREDENTIAL_IS_GENERATING_CHALLENGE, CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK, CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK, CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK }) @Retention(RetentionPolicy.SOURCE) public @interface CredentialAction {} Loading Loading @@ -157,11 +167,10 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default } } @NonNull private final LockPatternUtils mLockPatternUtils; @NonNull private final ChallengeGenerator mChallengeGenerator; private CredentialModel mCredentialModel = null; @NonNull private final MutableLiveData<Integer> mActionLiveData = @NonNull private final MutableLiveData<Boolean> mGenerateChallengeFailLiveData = new MutableLiveData<>(); public AutoCredentialViewModel( Loading @@ -173,51 +182,63 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default mChallengeGenerator = challengeGenerator; } public void setCredentialModel(@NonNull CredentialModel credentialModel) { mCredentialModel = credentialModel; /** * Set CredentialModel, the source is coming from savedInstanceState or activity intent */ public void setCredentialModel(@Nullable Bundle savedInstanceState, @NonNull Intent intent) { mCredentialModel = new CredentialModel( savedInstanceState != null ? savedInstanceState.getBundle(KEY_CREDENTIAL_MODEL) : intent.getExtras(), SystemClock.elapsedRealtimeClock()); if (DEBUG) { Log.d(TAG, "setCredentialModel " + mCredentialModel + ", savedInstanceState exist:" + (savedInstanceState != null)); } } /** * Observe ActionLiveData for actions about choosing lock, confirming lock, or finishing * activity * Handle onSaveInstanceState from activity */ @NonNull public LiveData<Integer> getActionLiveData() { return mActionLiveData; public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBundle(KEY_CREDENTIAL_MODEL, mCredentialModel.getBundle()); } @Override public void onCreate(@NonNull LifecycleOwner owner) { checkCredential(); @NonNull public LiveData<Boolean> getGenerateChallengeFailLiveData() { return mGenerateChallengeFailLiveData; } /** * Check credential status for biometric enrollment. */ private void checkCredential() { @CredentialAction public int checkCredential() { if (isValidCredential()) { return; return CREDENTIAL_VALID; } final long gkPwHandle = mCredentialModel.getGkPwHandle(); if (isUnspecifiedPassword()) { mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK); return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK; } else if (CredentialModel.isValidGkPwHandle(gkPwHandle)) { generateChallenge(gkPwHandle); return CREDENTIAL_IS_GENERATING_CHALLENGE; } else { mActionLiveData.postValue(CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK); return CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK; } } private void generateChallenge(long gkPwHandle) { mChallengeGenerator.setCallback((sensorId, userId, challenge) -> { mCredentialModel.setSensorId(sensorId); mCredentialModel.setChallenge(challenge); try { final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId); mCredentialModel.setSensorId(sensorId); mCredentialModel.setChallenge(challenge); mCredentialModel.setToken(newToken); } catch (IllegalStateException e) { Log.e(TAG, "generateChallenge, IllegalStateException", e); mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE); mGenerateChallengeFailLiveData.postValue(true); return; } Loading @@ -231,7 +252,7 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default // Check credential again if (!isValidCredential()) { Log.w(TAG, "generateChallenge, invalid Credential"); mActionLiveData.postValue(CREDENTIAL_FAIL_DURING_GENERATE_CHALLENGE); mGenerateChallengeFailLiveData.postValue(true); } }); mChallengeGenerator.generateChallenge(getUserId()); Loading Loading @@ -282,16 +303,16 @@ public class AutoCredentialViewModel extends AndroidViewModel implements Default final VerifyCredentialResponse response = mLockPatternUtils .verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId); if (!response.isMatched()) { throw new IllegalStateException("Unable to request Gatekeeper HAT"); throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT"); } return response.getGatekeeperHAT(); } /** * Get Credential bundle which will be used to launch next activity. * Get Credential intent extra which will be used to launch next activity. */ @NonNull public Bundle getCredentialBundle() { public Bundle getCredentialIntentExtra() { final Bundle retBundle = new Bundle(); final long gkPwHandle = mCredentialModel.getGkPwHandle(); if (CredentialModel.isValidGkPwHandle(gkPwHandle)) { Loading