From 06466b030b897bd639453ade32c1aab28c551492 Mon Sep 17 00:00:00 2001 From: yuanjiahsu Date: Fri, 13 Jan 2023 08:47:49 +0800 Subject: [PATCH] =?UTF-8?q?Implement=20=E2=80=9CSet=20up=20Face=20or=20Fin?= =?UTF-8?q?gerprint=20Unlock=20first=E2=80=9D=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The activity which instructs the user to set up face or fingerprint unlock before setting the watch unlock Bug: 264813445 Bug: 264962961 Test: make RunSettingsRoboTests ROBOTEST_FILTER=ActiveUnlockRequireBiometricSetupTest Change-Id: I556c62b6b8102f6e15045a37cf506c0c0eedf733 --- AndroidManifest.xml | 2 + .../activeunlock_require_biometric_setup.xml | 24 +++ .../biometrics/BiometricEnrollActivity.java | 8 +- .../ActiveUnlockRequireBiometricSetup.java | 147 ++++++++++++++++++ ...ActiveUnlockRequireBiometricSetupTest.java | 96 ++++++++++++ 5 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 res/layout/activeunlock_require_biometric_setup.xml create mode 100644 src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java create mode 100644 tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b5818c2b541..772ac1ddd7d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2510,6 +2510,8 @@ + + + + + + diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java index b4f89677453..e63a754e3e3 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java +++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java @@ -495,7 +495,13 @@ public class BiometricEnrollActivity extends InstrumentedActivity { @Override public void finish() { if (mGkPwHandle != null) { - BiometricUtils.removeGatekeeperPasswordHandle(this, mGkPwHandle); + // When launched as InternalActivity, the mGkPwHandle was gotten from intent extra + // instead of requesting from the user. Do not remove the mGkPwHandle in service side + // for this case because the caller activity may still need it and will be responsible + // for removing it. + if (!(this instanceof InternalActivity)) { + BiometricUtils.removeGatekeeperPasswordHandle(this, mGkPwHandle); + } } super.finish(); } diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java new file mode 100644 index 00000000000..60cc16e87e7 --- /dev/null +++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2023 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.activeunlock; + +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL; +import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED; + +import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE; + +import android.app.settings.SettingsEnums; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.Log; +import android.view.View; + +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.biometrics.BiometricEnrollActivity; +import com.android.settings.biometrics.BiometricEnrollBase; +import com.android.settings.biometrics.combination.CombinedBiometricStatusUtils; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; + +/** + * Activity which instructs the user to set up face or fingerprint unlock before setting the watch + * unlock. + */ +public class ActiveUnlockRequireBiometricSetup extends BiometricEnrollBase { + private static final String TAG = "ActiveUnlockRequireBiometricSetup"; + + @VisibleForTesting + static final int BIOMETRIC_ENROLL_REQUEST = 1001; + private long mGkPwHandle; + private boolean mNextClicked; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activeunlock_require_biometric_setup); + + mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); + Log.i(TAG, "mUserId = " + mUserId); + mGkPwHandle = getIntent().getLongExtra(EXTRA_KEY_GK_PW_HANDLE, 0L); + + final PackageManager pm = getApplicationContext().getPackageManager(); + boolean hasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE); + boolean hasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); + if (hasFeatureFace && hasFeatureFingerprint) { + setHeaderText( + R.string.security_settings_activeunlock_require_face_fingerprint_setup_title); + setDescriptionText( + R.string.security_settings_activeunlock_require_face_fingerprint_setup_message); + } else if (hasFeatureFingerprint) { + setHeaderText(R.string.security_settings_activeunlock_require_fingerprint_setup_title); + setDescriptionText( + R.string.security_settings_activeunlock_require_fingerprint_setup_message); + } else if (hasFeatureFace) { + setHeaderText(R.string.security_settings_activeunlock_require_face_setup_title); + setDescriptionText( + R.string.security_settings_activeunlock_require_face_setup_message); + } + + mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class); + mFooterBarMixin.setSecondaryButton( + new FooterButton.Builder(this) + .setText(R.string.cancel) + .setListener(this::onCancelClick) + .setButtonType(FooterButton.ButtonType.CANCEL) + .setTheme(R.style.SudGlifButton_Secondary) + .build() + ); + + mFooterBarMixin.setPrimaryButton( + new FooterButton.Builder(this) + .setText(R.string.security_settings_activeunlock_biometric_setup) + .setListener(this::onNextButtonClick) + .setButtonType(FooterButton.ButtonType.NEXT) + .setTheme(R.style.SudGlifButton_Primary) + .build() + ); + } + + @Override + public void onBackPressed() { + finish(); + } + + private void onCancelClick(View view) { + finish(); + } + + @Override + protected boolean shouldFinishWhenBackgrounded() { + return super.shouldFinishWhenBackgrounded() && !mNextClicked; + } + + @Override + protected void onNextButtonClick(View view) { + mNextClicked = true; + Intent intent = new Intent(this, BiometricEnrollActivity.InternalActivity.class); + intent.setAction(ACTION_BIOMETRIC_ENROLL); + intent.putExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG); + intent.putExtra(Intent.EXTRA_USER_ID, mUserId); + intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle); + startActivityForResult(intent, BIOMETRIC_ENROLL_REQUEST); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == BIOMETRIC_ENROLL_REQUEST && resultCode != RESULT_CANCELED) { + CombinedBiometricStatusUtils combinedBiometricStatusUtils = + new CombinedBiometricStatusUtils(this, mUserId); + if (combinedBiometricStatusUtils.hasEnrolled()) { + // TODO(b/264813444): launch active unlock setting page in GmsCore without double + // authentication. + } + } + mNextClicked = false; + finish(); + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.ACTIVE_UNLOCK_REQUIRE_BIOMETRIC_SETUP; + } +} diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java new file mode 100644 index 00000000000..d3bbf247ae8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.activeunlock; + +import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED; +import static com.android.settings.biometrics.activeunlock.ActiveUnlockRequireBiometricSetup.BIOMETRIC_ENROLL_REQUEST; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.robolectric.RuntimeEnvironment.application; + +import android.app.settings.SettingsEnums; +import android.content.ComponentName; + +import com.android.settings.R; +import com.android.settings.biometrics.BiometricEnrollActivity; + +import com.google.android.setupcompat.PartnerCustomizationLayout; +import com.google.android.setupcompat.template.FooterBarMixin; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowActivity; + +@RunWith(RobolectricTestRunner.class) +public class ActiveUnlockRequireBiometricSetupTest { + + private ActiveUnlockRequireBiometricSetup mActivity; + private PartnerCustomizationLayout mLayout; + + @Before + public void setUp() { + mActivity = Robolectric.buildActivity( + ActiveUnlockRequireBiometricSetup.class).setup().get(); + mLayout = mActivity.findViewById(R.id.setup_wizard_layout); + } + + @Test + public void onBackPressed_shouldFinish() { + mActivity.onBackPressed(); + + assertThat(mActivity.isFinishing()).isTrue(); + } + + @Test + public void clickCancel_shouldFinish() { + mLayout.getMixin(FooterBarMixin.class).getSecondaryButtonView().performClick(); + + assertThat(mActivity.isFinishing()).isTrue(); + } + + @Test + public void clickNext_shouldLaunchBiometricSetup() { + final ComponentName expectedComponent = new ComponentName(application, + BiometricEnrollActivity.InternalActivity.class); + + mLayout.getMixin(FooterBarMixin.class).getPrimaryButtonView().performClick(); + + ShadowActivity.IntentForResult startedActivity = Shadows.shadowOf( + mActivity).getNextStartedActivityForResult(); + assertWithMessage("Next activity").that(startedActivity).isNotNull(); + assertThat(startedActivity.intent.getComponent()).isEqualTo(expectedComponent); + } + + @Test + public void onActivityResult_shouldFinish() { + mActivity.onActivityResult(BIOMETRIC_ENROLL_REQUEST, RESULT_FINISHED, null); + + assertThat(mActivity.isFinishing()).isTrue(); + } + + @Test + public void getMetricsCategory_returnsCorrectCategory() { + assertThat(mActivity.getMetricsCategory()).isEqualTo( + SettingsEnums.ACTIVE_UNLOCK_REQUIRE_BIOMETRIC_SETUP); + } +}