Loading AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2524,6 +2524,8 @@ <meta-data android:name="com.android.settings.icon_tintable" android:value="true" /> </activity-alias> <activity android:name=".biometrics.activeunlock.ActiveUnlockRequireBiometricSetup" android:exported="false"/> <!-- Note this must not be exported since it returns the password in the intent --> <activity android:name=".password.ConfirmLockPattern$InternalActivity" android:exported="false" Loading res/layout/activeunlock_require_biometric_setup.xml 0 → 100644 +24 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ 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 --> <com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/setup_wizard_layout" style="?attr/fingerprint_layout_theme" android:layout_width="match_parent" android:layout_height="match_parent"> </com.google.android.setupdesign.GlifLayout> src/com/android/settings/biometrics/BiometricEnrollActivity.java +7 −1 Original line number Diff line number Diff line Loading @@ -495,8 +495,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity { @Override public void finish() { if (mGkPwHandle != null) { // 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(); } Loading src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * 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; } } tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * 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); } } Loading
AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2524,6 +2524,8 @@ <meta-data android:name="com.android.settings.icon_tintable" android:value="true" /> </activity-alias> <activity android:name=".biometrics.activeunlock.ActiveUnlockRequireBiometricSetup" android:exported="false"/> <!-- Note this must not be exported since it returns the password in the intent --> <activity android:name=".password.ConfirmLockPattern$InternalActivity" android:exported="false" Loading
res/layout/activeunlock_require_biometric_setup.xml 0 → 100644 +24 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ 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 --> <com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/setup_wizard_layout" style="?attr/fingerprint_layout_theme" android:layout_width="match_parent" android:layout_height="match_parent"> </com.google.android.setupdesign.GlifLayout>
src/com/android/settings/biometrics/BiometricEnrollActivity.java +7 −1 Original line number Diff line number Diff line Loading @@ -495,8 +495,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity { @Override public void finish() { if (mGkPwHandle != null) { // 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(); } Loading
src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * 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; } }
tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetupTest.java 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * 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); } }