Merge changes from topic "face-settings"

* changes:
  3/n: Remove all faces when Pin/Pattern/Pass is removed
  2/n: Add face to ChooseLock*
  1/n Start adding Face settings (base, intro)
This commit is contained in:
Kevin Chyn
2018-07-03 00:51:49 +00:00
committed by Android (Google) Code Review
41 changed files with 1578 additions and 326 deletions

View File

@@ -1569,6 +1569,8 @@
android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/GlifTheme.Light"/>
<activity android:name=".biometrics.face.FaceEnrollIntroduction" android:exported="false" />
<activity android:name=".biometrics.fingerprint.FingerprintSettings" android:exported="false"/>
<activity android:name=".biometrics.fingerprint.FingerprintEnrollFindSensor" android:exported="false"/>
<activity android:name=".biometrics.fingerprint.FingerprintEnrollEnrolling" android:exported="false"/>

View File

@@ -0,0 +1,23 @@
<!--
~ Copyright (C) 2018 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
</vector>

View File

@@ -0,0 +1,23 @@
<!--
~ Copyright (C) 2018 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
</vector>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/face_header_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="56dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:text="@string/lock_settings_picker_face_message"
style="@style/FaceHeaderStyle" />

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 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.android.setupwizardlib.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?attr/face_layout_theme"
android:id="@+id/setup_wizard_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:suwFooter="@layout/face_enroll_introduction_footer">
<LinearLayout
style="@style/SuwContentFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<com.android.setupwizardlib.view.RichTextView
android:id="@+id/description_text"
style="@style/SuwDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/security_settings_face_enroll_introduction_message" />
<com.android.setupwizardlib.view.RichTextView
android:id="@+id/error_text"
style="@style/SuwDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.android.setupwizardlib.view.FillContentLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
style="@style/SuwContentIllustration"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:src="@drawable/face_enroll_introduction" />
</com.android.setupwizardlib.view.FillContentLayout>
</LinearLayout>
</com.android.setupwizardlib.GlifLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 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
-->
<!-- TODO: Use aapt:attr when it is fixed (b/36809755) -->
<com.android.setupwizardlib.view.ButtonBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/SuwGlifButtonBar.Stackable"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/face_cancel_button"
style="@style/SuwGlifButton.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/security_settings_face_enroll_introduction_cancel" />
<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/face_next_button"
style="@style/SuwGlifButton.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/suw_next_button_label" />
</com.android.setupwizardlib.view.ButtonBarLayout>

View File

@@ -23,4 +23,4 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="@dimen/suw_description_glif_margin_top"
android:paddingBottom="@dimen/suw_description_glif_margin_bottom_lists"
android:text="@string/lock_settings_picker_fingerprint_added_security_message" />
android:text="@string/lock_settings_picker_biometrics_added_security_message" />

View File

@@ -877,6 +877,33 @@
<string name="security_dashboard_summary">Screen lock, fingerprint</string>
<!-- Summary for Security settings when fingerprint is not supported [CHAR LIMIT=NONE]-->
<string name="security_dashboard_summary_no_fingerprint">Screen lock</string>
<!-- Face enrollment and settings --><skip />
<!-- Message shown in summary field when face authentication is set up. [CHAR LIMIT=22] -->
<string name="security_settings_face_preference_summary">Face added</string>
<!-- Message shown in summary field when face authentication is not set up. [CHAR LIMIT=40] -->
<string name="security_settings_face_preference_summary_none">Tap to set up face authentication</string>
<!-- Title shown for menu item that launches face settings or enrollment. [CHAR LIMIT=22] -->
<string name="security_settings_face_preference_title">Face authentication</string>
<!-- Button text to cancel enrollment from the introduction [CHAR LIMIT=22] -->
<string name="security_settings_face_enroll_introduction_cancel">Cancel</string>
<!-- Introduction title shown in face enrollment to introduce the face unlock feature [CHAR LIMIT=30] -->
<string name="security_settings_face_enroll_introduction_title">Unlock with your face</string>
<!-- Introduction title shown in face enrollment to introduce the face authentication feature, when face unlock is disabled by device admin [CHAR LIMIT=40] -->
<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]-->
<string name="security_settings_face_enroll_introduction_message">Use your face to unlock your phone, authorize purchases, or sign in to apps.</string>
<!-- Introduction detail message shown in face enrollment dialog, when face unlock is disabled by device admin [CHAR LIMIT=NONE] -->
<string name="security_settings_face_enroll_introduction_message_unlock_disabled">Use you</string>
<!-- Introduction detail message shwon in face enrollment screen in setup wizard. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_enroll_introduction_message_setup">Use your face to unlock your phone, authorize purchases, or sign in to apps</string>
<!-- Text shown when "Add face" button is disabled -->
<string name="face_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
<!-- Text shown when users has enrolled a maximum number of faces [CHAR LIMIT=NONE] -->
<string name="face_intro_error_max">You\u2019ve added the maximum number of faces</string>
<!-- Text shown when an unknown error caused the device to be unable to add faces [CHAR LIMIT=NONE] -->
<string name="face_intro_error_unknown">Can\u2019t add more faces</string>
<!-- Fingerprint enrollment and settings --><skip />
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_preference_title">Fingerprint</string>
@@ -1193,7 +1220,7 @@
<string name="setup_lock_settings_picker_title" product="default">Protect your phone</string>
<!-- Message shown when setting up screen lock, prompting user to choose the their backup screen lock method [CHAR LIMIT=NONE] -->
<string name="lock_settings_picker_fingerprint_added_security_message">For added security, set a backup screen lock</string>
<string name="lock_settings_picker_biometrics_added_security_message">For added security, set a backup screen lock</string>
<!-- Description text for screen in setup wizard asking user to set up screen lock, explaining to the user how setting up a screen lock protect them from losing data. (tablet) [CHAR LIMIT=NONE] -->
<string name="setup_lock_settings_picker_message" product="tablet">Prevent others from using this tablet without your permission by activating device protection features. Choose the screen lock you want to use.</string>
@@ -1205,6 +1232,9 @@
<!-- Message shown in screen lock picker while setting up the backup/fallback screen lock method for fingerprint. Users can choose to use this method to unlock the screen instead of fingerprint, or when fingerprint is not accepted. [CHAR LIMIT=80] [BACKUP_MESSAGE_ID=2799884038398627882] -->
<string name="lock_settings_picker_fingerprint_message">Choose your backup screen lock method</string>
<!-- Message shown in screen lock picker while setting up the backup/fallbakc screen lock method for face authentication. Users can choose to use this method to unlock the screen instead of face authentication, or when face authentication is not accepted. [CHAR LIMIT=80] -->
<string name="lock_settings_picker_face_message">Choose your backup screen lock method</string>
<!-- Label for button in screen lock settings, allowing users to choose other types of screen locks. [CHAR LIMIT=40] -->
<string name="setup_lock_settings_options_button_label">Screen lock options</string>
@@ -1280,6 +1310,21 @@
<!-- Message shown in screen lock picker while setting up the new screen lock with fingerprint option. [CHAR LIMIT=NONE]-->
<string name="fingerprint_unlock_title">You can unlock your phone using your fingerprint. For security, this option requires a backup screen lock.</string>
<!-- Title for preference that guides the user through creating a backup unlock pattern for face authentication [CHAR LIMIT=45]-->
<string name="face_unlock_set_unlock_pattern">Face authentication + Pattern</string>
<!-- Title for preference that guides the user through creating a backup unlock PIN for face authentication [CHAR LIMIT=45]-->
<string name="face_unlock_set_unlock_pin">Face authentication + PIN</string>
<!-- Title for preference that guides the user through creating a backup unlock password for face authentication [CHAR LIMIT=45]-->
<string name="face_unlock_set_unlock_password">Face authentication + Password</string>
<!-- Title for preference that guides the user to skip face authentication setup [CHAR LIMIT=60]-->
<string name="face_unlock_skip_face">Continue without face authentication</string>
<!-- Message shown in screen lock picker while setting up the new screen lock with face authentication option. [CHAR LIMIT=NONE] -->
<string name="face_unlock_title">You can unlock your phone using your face. For security, this option requires a backup screen lock.</string>
<!-- Summary for preference that has been disabled by because of the DevicePolicyAdmin, or because device encryption is enabled, or because there are credentials in the credential storage [CHAR LIMIT=50] -->
<string name="unlock_set_unlock_disabled_summary">Disabled by admin, encryption policy, or
credential storage</string>
@@ -3696,6 +3741,12 @@
<string name="lockpassword_pin_set_toast">PIN has been set</string>
<!-- Toast shown if setting pattern was successful -->
<string name="lockpassword_pattern_set_toast">Pattern has been set</string>
<!-- Header on first screen of choose password/PIN as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
<string name="lockpassword_choose_your_password_header_for_face">To use face authentication, set password</string>
<!-- Header on first screen of choose pattern as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
<string name="lockpassword_choose_your_pattern_header_for_face">To use face authentication, set pattern</string>
<!-- Header on first screen of choose password/PIN as backup for face authentication flow. If this string cannot be translated in under 40 characters, please translate "Set face authentication backup" [CHAR LIMIT=40] -->
<string name="lockpassword_choose_your_pin_header_for_face">To use face authentication, set PIN</string>
<!-- Message to be used to explain the user that he needs to enter his pattern to continue a
particular operation. [CHAR LIMIT=70]-->
@@ -7005,6 +7056,10 @@
<!-- Search keyword for Ambient display settings screen. -->
<string name="keywords_ambient_display_screen">Ambient display, Lock screen display</string>
<!-- Search keyword for face settings. -->
<string name="keywords_face_settings">face</string>
<!-- Search keyword for fingerprint settings. -->
<string name="keywords_fingerprint_settings">fingerprint</string>
@@ -8195,6 +8250,13 @@
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a password to encrypt the device while setting up fingerprint unlock. [CHAR LIMIT=NONE] -->
<string name="encryption_interstitial_message_password_for_fingerprint">In addition to using your fingerprint to unlock your device, you can further protect this device by requiring your password before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require password to start your device?</string>
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a PIN to encrypt the device while setting up face unlock. [CHAR LIMIT=NONE] -->
<string name="encryption_interstitial_message_pin_for_face">In addition to using your face to unlock your device, you can further protect this device by requiring your PIN before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require PIN to start your device?</string>
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a pattern to encrypt the device while setting up face unlock. [CHAR LIMIT=NONE] -->
<string name="encryption_interstitial_message_pattern_for_face">In addition to using your face to unlock your device, you can further protect this device by requiring your pattern before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require pattern to start your device?</string>
<!-- Message shown on encryption interstitial to ask the user whether or not they want to use a password to encrypt the device while setting up face unlock. [CHAR LIMIT=NONE] -->
<string name="encryption_interstitial_message_password_for_face">In addition to using your face to unlock your device, you can further protect this device by requiring your password before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices. Require password to start your device?</string>
<!-- Button label to say yes to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->
<string name="encryption_interstitial_yes">Yes</string>
<!-- Button label to say no to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->

View File

@@ -364,6 +364,10 @@
<item name="android:icon">@drawable/ic_fingerprint_header</item>
</style>
<style name="FaceLayoutTheme">
<item name="android:icon">@drawable/ic_face_header</item>
</style>
<style name="TextAppearance.ConfirmDeviceCredentialsErrorText"
parent="android:TextAppearance.Material.Body1">
<item name="android:textColor">?android:attr/colorError</item>
@@ -406,6 +410,12 @@
<item name="android:lineSpacingMultiplier">1.2</item>
</style>
<style name="FaceHeaderStyle" parent="android:style/TextAppearance.Material.Subhead">
<item name="android:paddingTop">16dp</item>
<item name="android:textColor">@color/primary_dark_material_light</item>
<item name="android:lineSpacingMultiplier">1.2</item>
</style>
<style name="RingProgressBarStyle" parent="android:style/Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminate">false</item>
<item name="android:max">10000</item>

View File

@@ -16,6 +16,7 @@
<resources>
<attr name="fingerprint_layout_theme" format="reference" />
<attr name="face_layout_theme" format="reference" />
<attr name="ic_menu_moreoverflow" format="reference" />
<attr name="side_margin" format="reference|dimension" />
<attr name="wifi_signal_color" format="reference" />
@@ -26,6 +27,7 @@
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
<item name="side_margin">0dip</item>
<item name="wifi_signal_color">@color/setup_wizard_wifi_color_dark</item>
@@ -44,6 +46,7 @@
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
<item name="side_margin">0dip</item>
<item name="wifi_signal_color">@color/setup_wizard_wifi_color_light</item>
@@ -62,6 +65,7 @@
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
<item name="side_margin">0dip</item>
<item name="wifi_signal_color">@color/setup_wizard_wifi_color_dark</item>
@@ -80,6 +84,7 @@
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
<item name="side_margin">0dip</item>
<item name="wifi_signal_color">@color/setup_wizard_wifi_color_light</item>
@@ -98,6 +103,7 @@
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
<item name="side_margin">0dip</item>
<item name="wifi_signal_color">@color/setup_wizard_wifi_color_dark</item>
@@ -116,6 +122,7 @@
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
<item name="side_margin">0dip</item>
<item name="wifi_signal_color">@color/setup_wizard_wifi_color_light</item>
@@ -217,6 +224,7 @@
<item name="*android:preferenceFragmentPaddingSide">@dimen/settings_side_margin</item>
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_holo_dark</item>
<item name="wifi_signal">@drawable/wifi_signal</item>
<item name="wifi_signal_color">?android:attr/colorAccent</item>

View File

@@ -50,6 +50,11 @@
android:summary="@string/summary_placeholder"
settings:keywords="@string/keywords_fingerprint_settings" />
<Preference
android:key="face_settings"
android:title="@string/security_settings_face_preference_title"
android:summary="@string/summary_placeholder"
settings:keywords="@string/keywords_face_settings" />
</PreferenceCategory>
<!-- work profile security section -->

View File

@@ -52,4 +52,9 @@
android:title="@string/fingerprint_unlock_skip_fingerprint"
android:persistent="false"/>
<com.android.settingslib.RestrictedPreference
android:key="unlock_skip_face"
android:title="@string/face_unlock_skip_face"
android:persistent="false"/>
</PreferenceScreen>

View File

@@ -110,8 +110,10 @@ public class EncryptionInterstitial extends SettingsActivity {
mRequirePasswordToDecrypt = view.findViewById(R.id.encrypt_require_password);
mDontRequirePasswordToDecrypt = view.findViewById(R.id.encrypt_dont_require_password);
boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
final boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
final boolean forFace = getActivity().getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
Intent intent = getActivity().getIntent();
mRequestedPasswordQuality = intent.getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
mUnlockMethodIntent = intent.getParcelableExtra(EXTRA_UNLOCK_METHOD_INTENT);
@@ -120,17 +122,23 @@ public class EncryptionInterstitial extends SettingsActivity {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
msgId = forFingerprint ?
R.string.encryption_interstitial_message_pattern_for_fingerprint :
forFace ?
R.string.encryption_interstitial_message_pattern_for_face :
R.string.encryption_interstitial_message_pattern;
break;
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
msgId = forFingerprint ?
R.string.encryption_interstitial_message_pin_for_fingerprint :
forFace ?
R.string.encryption_interstitial_message_pin_for_face :
R.string.encryption_interstitial_message_pin;
break;
default:
msgId = forFingerprint ?
R.string.encryption_interstitial_message_password_for_fingerprint :
forFace ?
R.string.encryption_interstitial_message_password_for_face :
R.string.encryption_interstitial_message_password;
break;
}

View File

@@ -49,6 +49,7 @@ import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
@@ -813,6 +814,19 @@ public final class Utils extends com.android.settingslib.Utils {
return fingerprintManager != null && fingerprintManager.isHardwareDetected();
}
public static FaceManager getFaceManagerOrNull(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
return (FaceManager) context.getSystemService(Context.FACE_SERVICE);
} else {
return null;
}
}
public static boolean hasFaceHardware(Context context) {
FaceManager faceManager = getFaceManagerOrNull(context);
return faceManager != null && faceManager.isHardwareDetected();
}
/**
* Launches an intent which may optionally have a user id defined.
* @param fragment Fragment to use to launch the activity.

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2018 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.
@@ -14,7 +14,7 @@
* limitations under the License
*/
package com.android.settings.biometrics.fingerprint;
package com.android.settings.biometrics;
import android.annotation.Nullable;
import android.content.Intent;
@@ -29,6 +29,8 @@ import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.biometrics.fingerprint.FingerprintSettings;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.setupwizardlib.GlifLayout;
@@ -36,11 +38,11 @@ import com.android.setupwizardlib.GlifLayout;
/**
* Base activity for all fingerprint enrollment steps.
*/
public abstract class FingerprintEnrollBase extends InstrumentedActivity
public abstract class BiometricEnrollBase extends InstrumentedActivity
implements View.OnClickListener {
public static final int RESULT_FINISHED = FingerprintSettings.RESULT_FINISHED;
static final int RESULT_SKIP = FingerprintSettings.RESULT_SKIP;
static final int RESULT_TIMEOUT = FingerprintSettings.RESULT_TIMEOUT;
public static final int RESULT_FINISHED = BiometricSettings.RESULT_FINISHED;
public static final int RESULT_SKIP = BiometricSettings.RESULT_SKIP;
public static final int RESULT_TIMEOUT = BiometricSettings.RESULT_TIMEOUT;
protected byte[] mToken;
protected int mUserId;
@@ -118,7 +120,7 @@ public abstract class FingerprintEnrollBase extends InstrumentedActivity
protected void onNextButtonClick() {
}
protected Intent getEnrollingIntent() {
protected Intent getFingerprintEnrollingIntent() {
Intent intent = new Intent();
intent.setClassName("com.android.settings", FingerprintEnrollEnrolling.class.getName());
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);

View File

@@ -0,0 +1,233 @@
/*
* Copyright (C) 2018 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 android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.setupwizardlib.span.LinkSpan;
/**
* Abstract base class for the intro onboarding activity for biometric enrollment.
*/
public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
implements LinkSpan.OnClickListener {
public static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
public static final int BIOMETRIC_FIND_SENSOR_REQUEST = 2;
public static final int LEARN_MORE_REQUEST = 3;
private UserManager mUserManager;
private boolean mHasPassword;
private boolean mBiometricUnlockDisabledByAdmin;
private TextView mErrorText;
/**
* @return true if the biometric is disabled by a device administrator
*/
protected abstract boolean isDisabledByAdmin();
/**
* @return the layout resource
*/
protected abstract int getLayoutResource();
/**
* @return the header resource for if the biometric has been disabled by a device administrator
*/
protected abstract int getHeaderResDisabledByAdmin();
/**
* @return the default header resource
*/
protected abstract int getHeaderResDefault();
/**
* @return the description resource for if the biometric has been disabled by a device admin
*/
protected abstract int getDescriptionResDisabledByAdmin();
/**
* @return the cancel button
*/
protected abstract Button getCancelButton();
/**
* @return the next button
*/
protected abstract Button getNextButton();
/**
* @return the error TextView
*/
protected abstract TextView getErrorTextView();
/**
* @return 0 if there are no errors, otherwise returns the resource ID for the error string
* to be displayed.
*/
protected abstract int checkMaxEnrolled();
/**
* @return the challenge generated by the biometric hardware
*/
protected abstract long getChallenge();
/**
* @return one of the ChooseLockSettingsHelper#EXTRA_KEY_FOR_* constants
*/
protected abstract String getExtraKeyForBiometric();
/**
* @return the intent for proceeding to the next step of enrollment
*/
protected abstract Intent getFindSensorIntent();
/**
* @param span
*/
public abstract void onClick(LinkSpan span);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBiometricUnlockDisabledByAdmin = isDisabledByAdmin();
setContentView(getLayoutResource());
if (mBiometricUnlockDisabledByAdmin) {
setHeaderText(getHeaderResDisabledByAdmin());
} else {
setHeaderText(getHeaderResDefault());
}
Button cancelButton = getCancelButton();
cancelButton.setOnClickListener(v -> onCancelButtonClick());
mErrorText = getErrorTextView();
mUserManager = UserManager.get(this);
updatePasswordQuality();
}
@Override
protected void onResume() {
super.onResume();
final int errorMsg = checkMaxEnrolled();
if (errorMsg == 0) {
mErrorText.setText(null);
getNextButton().setVisibility(View.VISIBLE);
} else {
mErrorText.setText(errorMsg);
getNextButton().setVisibility(View.GONE);
}
}
private void updatePasswordQuality() {
final int passwordQuality = new ChooseLockSettingsHelper(this).utils()
.getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId));
mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
@Override
protected void onNextButtonClick() {
if (!mHasPassword) {
// No biometrics registered, launch into enrollment wizard.
launchChooseLock();
} else {
// Lock thingy is already set up, launch directly into find sensor step from wizard.
launchFindSensor(null);
}
}
private void launchChooseLock() {
Intent intent = getChooseLockIntent();
long challenge = getChallenge();
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
intent.putExtra(getExtraKeyForBiometric(), true);
if (mUserId != UserHandle.USER_NULL) {
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
}
startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
}
private void launchFindSensor(byte[] token) {
Intent intent = getFindSensorIntent();
if (token != null) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
if (mUserId != UserHandle.USER_NULL) {
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
}
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
}
protected Intent getChooseLockIntent() {
return new Intent(this, ChooseLockGeneric.class);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
final boolean isResultFinished = resultCode == RESULT_FINISHED;
if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) {
if (isResultFinished || resultCode == RESULT_SKIP) {
final int result = isResultFinished ? RESULT_OK : RESULT_SKIP;
setResult(result, data);
finish();
return;
}
} else if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
if (isResultFinished) {
updatePasswordQuality();
byte[] token = data.getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
launchFindSensor(token);
return;
}
} else if (requestCode == LEARN_MORE_REQUEST) {
overridePendingTransition(R.anim.suw_slide_back_in, R.anim.suw_slide_back_out);
}
super.onActivityResult(requestCode, resultCode, data);
}
protected void onCancelButtonClick() {
finish();
}
@Override
protected void initViews() {
super.initViews();
TextView description = (TextView) findViewById(R.id.description_text);
if (mBiometricUnlockDisabledByAdmin) {
description.setText(getDescriptionResDisabledByAdmin());
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2018 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 com.android.settings.SubSettings;
/**
* Abstract base class for biometric settings, such as Fingerprint, Face, Iris
*/
public abstract class BiometricSettings extends SubSettings {
/**
* Used by the choose fingerprint wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
* <p>
* Previously, each activity in the wizard would finish itself after
* starting the next activity. However, this leads to broken 'Back'
* behavior. So, now an activity does not finish itself until it gets this
* result.
*/
protected static final int RESULT_FINISHED = RESULT_FIRST_USER;
/**
* Used by the enrolling screen during setup wizard to skip over setting up fingerprint, which
* will be useful if the user accidentally entered this flow.
*/
protected static final int RESULT_SKIP = RESULT_FIRST_USER + 1;
/**
* Like {@link #RESULT_FINISHED} except this one indicates enrollment failed because the
* device was left idle. This is used to clear the credential token to require the user to
* re-enter their pin/pattern/password before continuing.
*/
protected static final int RESULT_TIMEOUT = RESULT_FIRST_USER + 2;
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2018 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 android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import androidx.preference.Preference;
public abstract class BiometricStatusPreferenceController extends BasePreferenceController {
protected final UserManager mUm;
protected final LockPatternUtils mLockPatternUtils;
protected final int mUserId = UserHandle.myUserId();
protected final int mProfileChallengeUserId;
/**
* @return true if the manager is not null and the hardware is detected.
*/
protected abstract boolean isDeviceSupported();
/**
* @return true if the user has enrolled biometrics of the subclassed type.
*/
protected abstract boolean hasEnrolledBiometrics();
/**
* @return the summary text if biometrics are enrolled.
*/
protected abstract String getSummaryTextEnrolled();
/**
* @return the summary text if no biometrics are enrolled.
*/
protected abstract String getSummaryTextNoneEnrolled();
/**
* @return the class name for the settings page.
*/
protected abstract String getSettingsClassName();
/**
* @return the class name for entry to enrollment.
*/
protected abstract String getEnrollClassName();
public BiometricStatusPreferenceController(Context context, String key) {
super(context, key);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mLockPatternUtils = FeatureFactory.getFactory(context)
.getSecurityFeatureProvider()
.getLockPatternUtils(context);
mProfileChallengeUserId = Utils.getManagedProfileId(mUm, mUserId);
}
@Override
public int getAvailabilityStatus() {
if (!isDeviceSupported()) {
return UNSUPPORTED_ON_DEVICE;
}
if (isUserSupported()) {
return AVAILABLE;
} else {
return DISABLED_FOR_USER;
}
}
@Override
public void updateState(Preference preference) {
if (!isAvailable()) {
if (preference != null) {
preference.setVisible(false);
}
return;
} else {
preference.setVisible(true);
}
final int userId = getUserId();
final String clazz;
if (hasEnrolledBiometrics()) {
preference.setSummary(getSummaryTextEnrolled());
clazz = getSettingsClassName();
} else {
preference.setSummary(getSummaryTextNoneEnrolled());
clazz = getEnrollClassName();
}
preference.setOnPreferenceClickListener(target -> {
final Context context = target.getContext();
final UserManager userManager = UserManager.get(context);
if (Utils.startQuietModeDialogIfNecessary(context, userManager,
userId)) {
return false;
}
Intent intent = new Intent();
intent.setClassName("com.android.settings", clazz);
intent.putExtra(Intent.EXTRA_USER_ID, userId);
context.startActivity(intent);
return true;
});
}
protected int getUserId() {
return mUserId;
}
protected boolean isUserSupported() {
return true;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2018 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 android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.hardware.face.FaceManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.RestrictedLockUtils;
import com.android.setupwizardlib.span.LinkSpan;
public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
private static final String TAG = "FaceIntro";
private FaceManager mFaceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFaceManager = Utils.getFaceManagerOrNull(this);
}
@Override
protected boolean isDisabledByAdmin() {
return RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
this, DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) != null;
}
@Override
protected int getLayoutResource() {
return R.layout.face_enroll_introduction;
}
@Override
protected int getHeaderResDisabledByAdmin() {
return R.string.security_settings_face_enroll_introduction_title_unlock_disabled;
}
@Override
protected int getHeaderResDefault() {
return R.string.security_settings_face_enroll_introduction_title;
}
@Override
protected int getDescriptionResDisabledByAdmin() {
return R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
}
@Override
protected Button getCancelButton() {
return findViewById(R.id.face_cancel_button);
}
@Override
protected Button getNextButton() {
return findViewById(R.id.face_next_button);
}
@Override
protected TextView getErrorTextView() {
return findViewById(R.id.error_text);
}
@Override
protected int checkMaxEnrolled() {
if (mFaceManager != null) {
final int max = getResources().getInteger(
com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size();
if (numEnrolledFaces >= max) {
return R.string.face_intro_error_max;
}
} else {
return R.string.face_intro_error_unknown;
}
return 0;
}
@Override
protected long getChallenge() {
if (mFaceManager == null) {
return 0;
}
return mFaceManager.preEnroll();
}
@Override
protected String getExtraKeyForBiometric() {
return ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
}
@Override
protected Intent getFindSensorIntent() {
return null; // TODO
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FACE_ENROLL_INTRO;
}
@Override
public void onClick(LinkSpan span) {
// TODO(b/110906762)
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2018 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 android.content.Context;
import android.hardware.face.FaceManager;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
public class FaceStatusPreferenceController extends BiometricStatusPreferenceController {
private static final String KEY_FACE_SETTINGS = "face_settings";
protected final FaceManager mFaceManager;
public FaceStatusPreferenceController(Context context) {
this(context, KEY_FACE_SETTINGS);
}
public FaceStatusPreferenceController(Context context, String key) {
super(context, key);
mFaceManager = Utils.getFaceManagerOrNull(context);
}
@Override
protected boolean isDeviceSupported() {
return mFaceManager != null && mFaceManager.isHardwareDetected();
}
@Override
protected boolean hasEnrolledBiometrics() {
return mFaceManager.hasEnrolledFaces(mUserId);
}
@Override
protected String getSummaryTextEnrolled() {
return mContext.getResources()
.getString(R.string.security_settings_face_preference_summary);
}
@Override
protected String getSummaryTextNoneEnrolled() {
return mContext.getResources()
.getString(R.string.security_settings_face_preference_summary_none);
}
@Override
protected String getSettingsClassName() {
return null;
}
@Override
protected String getEnrollClassName() {
return FaceEnrollIntroduction.class.getName();
}
}

View File

@@ -44,13 +44,14 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
/**
* Activity which handles the actual enrolling for fingerprint.
*/
public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
public class FingerprintEnrollEnrolling extends BiometricEnrollBase
implements FingerprintEnrollSidecar.Listener {
static final String TAG_SIDECAR = "sidecar";

View File

@@ -27,6 +27,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollSidecar.Listener;
import com.android.settings.password.ChooseLockSettingsHelper;
@@ -35,7 +36,7 @@ import androidx.annotation.Nullable;
/**
* Activity explaining the fingerprint sensor location for fingerprint enrollment.
*/
public class FingerprintEnrollFindSensor extends FingerprintEnrollBase {
public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
@VisibleForTesting
static final int CONFIRM_REQUEST = 1;
@@ -166,7 +167,7 @@ public class FingerprintEnrollFindSensor extends FingerprintEnrollBase {
}
getFragmentManager().beginTransaction().remove(mSidecar).commitAllowingStateLoss();
mSidecar = null;
startActivityForResult(getEnrollingIntent(), ENROLLING);
startActivityForResult(getFingerprintEnrollingIntent(), ENROLLING);
}
}

View File

@@ -25,11 +25,12 @@ import android.widget.Button;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
/**
* Activity which concludes fingerprint enrollment.
*/
public class FingerprintEnrollFinish extends FingerprintEnrollBase {
public class FingerprintEnrollFinish extends BiometricEnrollBase {
private static final int REQUEST_ADD_ANOTHER = 1;
@@ -71,7 +72,7 @@ public class FingerprintEnrollFinish extends FingerprintEnrollBase {
@Override
public void onClick(View v) {
if (v.getId() == R.id.add_another_button) {
startActivityForResult(getEnrollingIntent(), REQUEST_ADD_ANOTHER);
startActivityForResult(getFingerprintEnrollingIntent(), REQUEST_ADD_ANOTHER);
}
super.onClick(v);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2018 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.
@@ -21,195 +21,109 @@ import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.setupwizardlib.span.LinkSpan;
/**
* Onboarding activity for fingerprint enrollment.
*/
public class FingerprintEnrollIntroduction extends FingerprintEnrollBase
implements View.OnClickListener, LinkSpan.OnClickListener {
public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
private static final String TAG = "FingerprintIntro";
protected static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
protected static final int FINGERPRINT_FIND_SENSOR_REQUEST = 2;
protected static final int LEARN_MORE_REQUEST = 3;
private UserManager mUserManager;
private boolean mHasPassword;
private boolean mFingerprintUnlockDisabledByAdmin;
private TextView mErrorText;
private FingerprintManager mFingerprintManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFingerprintUnlockDisabledByAdmin = RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
this, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId) != null;
setContentView(R.layout.fingerprint_enroll_introduction);
if (mFingerprintUnlockDisabledByAdmin) {
setHeaderText(R.string
.security_settings_fingerprint_enroll_introduction_title_unlock_disabled);
} else {
setHeaderText(R.string.security_settings_fingerprint_enroll_introduction_title);
}
Button cancelButton = (Button) findViewById(R.id.fingerprint_cancel_button);
cancelButton.setOnClickListener(this);
mErrorText = (TextView) findViewById(R.id.error_text);
mUserManager = UserManager.get(this);
updatePasswordQuality();
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
}
@Override
protected void onResume() {
super.onResume();
final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(this);
int errorMsg = 0;
if (fingerprintManager != null) {
final int max = getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
final int numEnrolledFingerprints =
fingerprintManager.getEnrolledFingerprints(mUserId).size();
if (numEnrolledFingerprints >= max) {
errorMsg = R.string.fingerprint_intro_error_max;
}
} else {
errorMsg = R.string.fingerprint_intro_error_unknown;
}
if (errorMsg == 0) {
mErrorText.setText(null);
getNextButton().setVisibility(View.VISIBLE);
} else {
mErrorText.setText(errorMsg);
getNextButton().setVisibility(View.GONE);
}
protected boolean isDisabledByAdmin() {
return RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
this, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId) != null;
}
private void updatePasswordQuality() {
final int passwordQuality = new ChooseLockSettingsHelper(this).utils()
.getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId));
mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@Override
protected int getLayoutResource() {
return R.layout.fingerprint_enroll_introduction;
}
@Override
protected int getHeaderResDisabledByAdmin() {
return R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled;
}
@Override
protected int getHeaderResDefault() {
return R.string.security_settings_fingerprint_enroll_introduction_title;
}
@Override
protected int getDescriptionResDisabledByAdmin() {
return R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
}
@Override
protected Button getCancelButton() {
return findViewById(R.id.fingerprint_cancel_button);
}
@Override
protected Button getNextButton() {
return (Button) findViewById(R.id.fingerprint_next_button);
return findViewById(R.id.fingerprint_next_button);
}
@Override
protected void onNextButtonClick() {
if (!mHasPassword) {
// No fingerprints registered, launch into enrollment wizard.
launchChooseLock();
protected TextView getErrorTextView() {
return findViewById(R.id.error_text);
}
@Override
protected int checkMaxEnrolled() {
if (mFingerprintManager != null) {
final int max = getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
final int numEnrolledFingerprints =
mFingerprintManager.getEnrolledFingerprints(mUserId).size();
if (numEnrolledFingerprints >= max) {
return R.string.fingerprint_intro_error_max;
}
} else {
// Lock thingy is already set up, launch directly into find sensor step from wizard.
launchFindSensor(null);
return R.string.fingerprint_intro_error_unknown;
}
return 0;
}
private void launchChooseLock() {
Intent intent = getChooseLockIntent();
long challenge = Utils.getFingerprintManagerOrNull(this).preEnroll();
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
if (mUserId != UserHandle.USER_NULL) {
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
@Override
protected long getChallenge() {
if (mFingerprintManager == null) {
return 0;
}
startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
return mFingerprintManager.preEnroll();
}
private void launchFindSensor(byte[] token) {
Intent intent = getFindSensorIntent();
if (token != null) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
}
if (mUserId != UserHandle.USER_NULL) {
intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
}
startActivityForResult(intent, FINGERPRINT_FIND_SENSOR_REQUEST);
}
protected Intent getChooseLockIntent() {
return new Intent(this, ChooseLockGeneric.class);
@Override
protected String getExtraKeyForBiometric() {
return ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
}
@Override
protected Intent getFindSensorIntent() {
return new Intent(this, FingerprintEnrollFindSensor.class);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
final boolean isResultFinished = resultCode == RESULT_FINISHED;
if (requestCode == FINGERPRINT_FIND_SENSOR_REQUEST) {
if (isResultFinished || resultCode == RESULT_SKIP) {
final int result = isResultFinished ? RESULT_OK : RESULT_SKIP;
setResult(result, data);
finish();
return;
}
} else if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
if (isResultFinished) {
updatePasswordQuality();
byte[] token = data.getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
launchFindSensor(token);
return;
}
} else if (requestCode == LEARN_MORE_REQUEST) {
overridePendingTransition(R.anim.suw_slide_back_in, R.anim.suw_slide_back_out);
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.fingerprint_cancel_button) {
onCancelButtonClick();
} else {
super.onClick(v);
}
}
@Override
public int getMetricsCategory() {
return MetricsEvent.FINGERPRINT_ENROLL_INTRO;
}
protected void onCancelButtonClick() {
finish();
}
@Override
protected void initViews() {
super.initViews();
TextView description = (TextView) findViewById(R.id.description_text);
if (mFingerprintUnlockDisabledByAdmin) {
description.setText(R.string
.security_settings_fingerprint_enroll_introduction_message_unlock_disabled);
}
return MetricsProto.MetricsEvent.FINGERPRINT_ENROLL_INTRO;
}
@Override

View File

@@ -43,6 +43,7 @@ import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricSettings;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
@@ -66,34 +67,10 @@ import androidx.preference.PreferenceViewHolder;
/**
* Settings screen for fingerprints
*/
public class FingerprintSettings extends SubSettings {
public class FingerprintSettings extends BiometricSettings {
private static final String TAG = "FingerprintSettings";
/**
* Used by the choose fingerprint wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
* <p>
* Previously, each activity in the wizard would finish itself after
* starting the next activity. However, this leads to broken 'Back'
* behavior. So, now an activity does not finish itself until it gets this
* result.
*/
protected static final int RESULT_FINISHED = RESULT_FIRST_USER;
/**
* Used by the enrolling screen during setup wizard to skip over setting up fingerprint, which
* will be useful if the user accidentally entered this flow.
*/
protected static final int RESULT_SKIP = RESULT_FIRST_USER + 1;
/**
* Like {@link #RESULT_FINISHED} except this one indicates enrollment failed because the
* device was left idle. This is used to clear the credential token to require the user to
* re-enter their pin/pattern/password before continuing.
*/
protected static final int RESULT_TIMEOUT = RESULT_FIRST_USER + 2;
private static final long LOCKOUT_DURATION = 30000; // time we have to wait for fp to reset, ms
public static final String ANNOTATION_URL = "url";

View File

@@ -17,32 +17,17 @@
package com.android.settings.biometrics.fingerprint;
import android.content.Context;
import android.content.Intent;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
import java.util.List;
import androidx.preference.Preference;
public class FingerprintStatusPreferenceController extends BasePreferenceController {
public class FingerprintStatusPreferenceController extends BiometricStatusPreferenceController {
private static final String KEY_FINGERPRINT_SETTINGS = "fingerprint_settings";
protected final FingerprintManager mFingerprintManager;
protected final UserManager mUm;
protected final LockPatternUtils mLockPatternUtils;
protected final int mUserId = UserHandle.myUserId();
protected final int mProfileChallengeUserId;
public FingerprintStatusPreferenceController(Context context) {
this(context, KEY_FINGERPRINT_SETTINGS);
@@ -51,69 +36,39 @@ public class FingerprintStatusPreferenceController extends BasePreferenceControl
public FingerprintStatusPreferenceController(Context context, String key) {
super(context, key);
mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mLockPatternUtils = FeatureFactory.getFactory(context)
.getSecurityFeatureProvider()
.getLockPatternUtils(context);
mProfileChallengeUserId = Utils.getManagedProfileId(mUm, mUserId);
}
@Override
public int getAvailabilityStatus() {
if (mFingerprintManager == null || !mFingerprintManager.isHardwareDetected()) {
return UNSUPPORTED_ON_DEVICE;
}
if (isUserSupported()) {
return AVAILABLE;
} else {
return DISABLED_FOR_USER;
}
protected boolean isDeviceSupported() {
return mFingerprintManager != null && mFingerprintManager.isHardwareDetected();
}
@Override
public void updateState(Preference preference) {
if (!isAvailable()) {
if (preference != null) {
preference.setVisible(false);
}
return;
} else {
preference.setVisible(true);
}
final int userId = getUserId();
final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(userId);
final int fingerprintCount = items != null ? items.size() : 0;
final String clazz;
if (fingerprintCount > 0) {
preference.setSummary(mContext.getResources().getQuantityString(
R.plurals.security_settings_fingerprint_preference_summary,
fingerprintCount, fingerprintCount));
clazz = FingerprintSettings.class.getName();
} else {
preference.setSummary(
R.string.security_settings_fingerprint_preference_summary_none);
clazz = FingerprintEnrollIntroduction.class.getName();
}
preference.setOnPreferenceClickListener(target -> {
final Context context = target.getContext();
final UserManager userManager = UserManager.get(context);
if (Utils.startQuietModeDialogIfNecessary(context, userManager,
userId)) {
return false;
}
Intent intent = new Intent();
intent.setClassName("com.android.settings", clazz);
intent.putExtra(Intent.EXTRA_USER_ID, userId);
context.startActivity(intent);
return true;
});
protected boolean hasEnrolledBiometrics() {
return mFingerprintManager.hasEnrolledFingerprints(mUserId);
}
protected int getUserId() {
return mUserId;
@Override
protected String getSummaryTextEnrolled() {
final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
return mContext.getResources().getQuantityString(
R.plurals.security_settings_fingerprint_preference_summary,
numEnrolled, numEnrolled);
}
protected boolean isUserSupported() {
return true;
@Override
protected String getSummaryTextNoneEnrolled() {
return mContext.getString(R.string.security_settings_fingerprint_preference_summary_none);
}
@Override
protected String getSettingsClassName() {
return FingerprintSettings.class.getName();
}
@Override
protected String getEnrollClassName() {
return FingerprintEnrollIntroduction.class.getName();
}
}

View File

@@ -42,7 +42,7 @@ public class SetupFingerprintEnrollFindSensor extends FingerprintEnrollFindSenso
}
@Override
protected Intent getEnrollingIntent() {
protected Intent getFingerprintEnrollingIntent() {
Intent intent = new Intent(this, SetupFingerprintEnrollEnrolling.class);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
if (mUserId != UserHandle.USER_NULL) {

View File

@@ -28,7 +28,7 @@ import com.android.settings.password.ChooseLockSettingsHelper;
public class SetupFingerprintEnrollFinish extends FingerprintEnrollFinish {
@Override
protected Intent getEnrollingIntent() {
protected Intent getFingerprintEnrollingIntent() {
Intent intent = new Intent(this, SetupFingerprintEnrollEnrolling.class);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
if (mUserId != UserHandle.USER_NULL) {

View File

@@ -97,7 +97,7 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// if lock was already present, do not return intent data since it must have been
// reported in previous attempts
if (requestCode == FINGERPRINT_FIND_SENSOR_REQUEST && isKeyguardSecure()
if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST && isKeyguardSecure()
&& !mAlreadyHadLockScreenSetup) {
data = getMetricIntent(data);
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.password;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment
.RESULT_FINISHED;
@@ -31,6 +32,8 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.RemovalCallback;
@@ -52,9 +55,9 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollBase;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreference;
@@ -99,6 +102,7 @@ public class ChooseLockGeneric extends SettingsActivity {
private static final String TAG = "ChooseLockGenericFragment";
private static final String KEY_SKIP_FINGERPRINT = "unlock_skip_fingerprint";
private static final String KEY_SKIP_FACE = "unlock_skip_face";
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
@@ -143,6 +147,7 @@ public class ChooseLockGeneric extends SettingsActivity {
private String mUserPassword;
private LockPatternUtils mLockPatternUtils;
private FingerprintManager mFingerprintManager;
private FaceManager mFaceManager;
private int mUserId;
private boolean mHideDrawer = false;
private ManagedLockPasswordProvider mManagedPasswordProvider;
@@ -151,6 +156,7 @@ public class ChooseLockGeneric extends SettingsActivity {
private ChooseLockGenericController mController;
protected boolean mForFingerprint = false;
protected boolean mForFace = false;
@Override
public int getMetricsCategory() {
@@ -163,6 +169,7 @@ public class ChooseLockGeneric extends SettingsActivity {
String chooseLockAction = getActivity().getIntent().getAction();
mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
mFaceManager = Utils.getFaceManagerOrNull(getActivity());
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
mLockPatternUtils = new LockPatternUtils(getActivity());
@@ -185,6 +192,8 @@ public class ChooseLockGeneric extends SettingsActivity {
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
mForFingerprint = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForFace = getActivity().getIntent().getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
mUserManager = UserManager.get(getActivity());
@@ -248,6 +257,12 @@ public class ChooseLockGeneric extends SettingsActivity {
((TextView) getHeaderView().findViewById(R.id.fingerprint_header_description))
.setText(R.string.fingerprint_unlock_title);
}
} else if (mForFace) {
setHeaderView(R.layout.choose_lock_generic_face_header);
if (mIsSetNewPassword) {
((TextView) getHeaderView().findViewById(R.id.face_header_description))
.setText(R.string.face_unlock_title);
}
}
}
@@ -260,7 +275,7 @@ public class ChooseLockGeneric extends SettingsActivity {
// unlock method to an insecure one
showFactoryResetProtectionWarningDialog(key);
return true;
} else if (KEY_SKIP_FINGERPRINT.equals(key)) {
} else if (KEY_SKIP_FINGERPRINT.equals(key) || KEY_SKIP_FACE.equals(key)) {
Intent chooseLockGenericIntent = new Intent(getActivity(),
ChooseLockGeneric.InternalActivity.class);
chooseLockGenericIntent.setAction(getIntent().getAction());
@@ -310,6 +325,8 @@ public class ChooseLockGeneric extends SettingsActivity {
unlockMethodIntent);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
mForFingerprint);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE,
mForFace);
intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer);
startActivityForResult(
intent,
@@ -360,7 +377,7 @@ public class ChooseLockGeneric extends SettingsActivity {
}
}
} else if (requestCode == CHOOSE_LOCK_BEFORE_FINGERPRINT_REQUEST
&& resultCode == FingerprintEnrollBase.RESULT_FINISHED) {
&& resultCode == BiometricEnrollBase.RESULT_FINISHED) {
Intent intent = getFindSensorIntent(getActivity());
if (data != null) {
intent.putExtras(data.getExtras());
@@ -430,6 +447,7 @@ public class ChooseLockGeneric extends SettingsActivity {
// Used for testing purposes
findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
findPreference(KEY_SKIP_FACE).setViewId(R.id.lock_none);
findPreference(ScreenLockType.PIN.preferenceKey).setViewId(R.id.lock_pin);
findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
}
@@ -441,6 +459,12 @@ public class ChooseLockGeneric extends SettingsActivity {
setPreferenceTitle(ScreenLockType.PIN, R.string.fingerprint_unlock_set_unlock_pin);
setPreferenceTitle(ScreenLockType.PASSWORD,
R.string.fingerprint_unlock_set_unlock_password);
} else if (mForFace) {
setPreferenceTitle(ScreenLockType.PATTERN,
R.string.face_unlock_set_unlock_pattern);
setPreferenceTitle(ScreenLockType.PIN, R.string.face_unlock_set_unlock_pin);
setPreferenceTitle(ScreenLockType.PASSWORD,
R.string.face_unlock_set_unlock_password);
}
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
@@ -453,6 +477,9 @@ public class ChooseLockGeneric extends SettingsActivity {
if (!(mForFingerprint && mIsSetNewPassword)) {
removePreference(KEY_SKIP_FINGERPRINT);
}
if (!(mForFace && mIsSetNewPassword)) {
removePreference(KEY_SKIP_FACE);
}
}
private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
@@ -581,6 +608,7 @@ public class ChooseLockGeneric extends SettingsActivity {
new ChooseLockPassword.IntentBuilder(getContext())
.setPasswordQuality(quality)
.setForFingerprint(mForFingerprint)
.setForFace(mForFace)
.setUserId(mUserId);
if (mHasChallenge) {
builder.setChallenge(mChallenge);
@@ -595,6 +623,7 @@ public class ChooseLockGeneric extends SettingsActivity {
ChooseLockPattern.IntentBuilder builder =
new ChooseLockPattern.IntentBuilder(getContext())
.setForFingerprint(mForFingerprint)
.setForFace(mForFace)
.setUserId(mUserId);
if (mHasChallenge) {
builder.setChallenge(mChallenge);
@@ -611,6 +640,30 @@ public class ChooseLockGeneric extends SettingsActivity {
unlockMethodIntent);
}
/**
* Keeps track of the biometric removal status. When all biometrics (including managed
* profiles) are removed, finishes the activity. Otherwise, it's possible the UI still
* shows enrolled biometrics due to the async remove.
*/
private class RemovalTracker {
boolean mFingerprintDone;
boolean mFaceDone;
void onFingerprintDone() {
mFingerprintDone = true;
if (mFingerprintDone && mFaceDone) {
finish();
}
}
void onFaceDone() {
mFaceDone = true;
if (mFingerprintDone && mFaceDone) {
finish();
}
}
}
/**
* Invokes an activity to change the user's pattern, password or PIN based on given quality
* and minimum quality specified by DevicePolicyManager. If quality is
@@ -647,12 +700,18 @@ public class ChooseLockGeneric extends SettingsActivity {
mChooseLockSettingsHelper.utils().clearLock(mUserPassword, mUserId);
mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled, mUserId);
getActivity().setResult(Activity.RESULT_OK);
removeAllFingerprintForUserAndFinish(mUserId);
removeAllBiometricsForUserAndFinish(mUserId);
} else {
removeAllFingerprintForUserAndFinish(mUserId);
removeAllBiometricsForUserAndFinish(mUserId);
}
}
private void removeAllBiometricsForUserAndFinish(final int userId) {
final RemovalTracker tracker = new RemovalTracker();
removeAllFingerprintForUserAndFinish(userId, tracker);
removeAllFaceForUserAndFinish(userId, tracker);
}
private Intent getIntentForUnlockMethod(int quality) {
Intent intent = null;
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
@@ -668,7 +727,8 @@ public class ChooseLockGeneric extends SettingsActivity {
return intent;
}
private void removeAllFingerprintForUserAndFinish(final int userId) {
private void removeAllFingerprintForUserAndFinish(final int userId,
RemovalTracker tracker) {
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
mFingerprintManager.setActiveUser(userId);
@@ -690,25 +750,27 @@ public class ChooseLockGeneric extends SettingsActivity {
@Override
public void onRemovalSucceeded(Fingerprint fp, int remaining) {
if (remaining == 0) {
removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
removeManagedProfileFingerprintsAndFinishIfNecessary(userId,
tracker);
}
}
});
} else {
// No fingerprints in this user, we may also want to delete managed profile
// fingerprints
removeManagedProfileFingerprintsAndFinishIfNecessary(userId);
removeManagedProfileFingerprintsAndFinishIfNecessary(userId, tracker);
}
} else {
// The removal callback will call finish, once all fingerprints are removed.
// We need to wait for that to occur, otherwise, the UI will still show that
// fingerprints exist even though they are (about to) be removed depending on
// the race condition.
finish();
tracker.onFingerprintDone();
}
}
private void removeManagedProfileFingerprintsAndFinishIfNecessary(final int parentUserId) {
private void removeManagedProfileFingerprintsAndFinishIfNecessary(final int parentUserId,
RemovalTracker tracker) {
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
mFingerprintManager.setActiveUser(UserHandle.myUserId());
}
@@ -721,14 +783,69 @@ public class ChooseLockGeneric extends SettingsActivity {
final UserInfo userInfo = profiles.get(i);
if (userInfo.isManagedProfile() && !mLockPatternUtils
.isSeparateProfileChallengeEnabled(userInfo.id)) {
removeAllFingerprintForUserAndFinish(userInfo.id);
removeAllFingerprintForUserAndFinish(userInfo.id, tracker);
hasChildProfile = true;
break;
}
}
}
if (!hasChildProfile) {
finish();
tracker.onFingerprintDone();
}
}
// TODO: figure out how to eliminate duplicated code. It's a bit hard due to the async-ness
private void removeAllFaceForUserAndFinish(final int userId, RemovalTracker tracker) {
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
if (mFaceManager.hasEnrolledFaces(userId)) {
mFaceManager.setActiveUser(userId);
Face face = new Face(null, 0, 0);
mFaceManager.remove(face, userId,
new FaceManager.RemovalCallback() {
@Override
public void onRemovalError(Face face, int errMsgId, CharSequence err) {
Log.e(TAG, String.format("Can't remove face %d. Reason: %s",
face.getBiometricId(), err));
}
@Override
public void onRemovalSucceeded(Face face, int remaining) {
if (remaining == 0) {
removeManagedProfileFacesAndFinishIfNecessary(userId, tracker);
}
}
});
} else {
// No faces in this user, we may also want to delete managed profile faces
removeManagedProfileFacesAndFinishIfNecessary(userId, tracker);
}
} else {
tracker.onFaceDone();
}
}
// TODO: figure out how to eliminate duplicated code. It's a bit hard due to the async-ness
private void removeManagedProfileFacesAndFinishIfNecessary(final int parentUserId,
RemovalTracker tracker) {
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
mFaceManager.setActiveUser(UserHandle.myUserId());
}
boolean hasChildProfile = false;
if (!mUserManager.getUserInfo(parentUserId).isManagedProfile()) {
// Current user is primary profile, remove work profile faces if necessary
final List<UserInfo> profiles = mUserManager.getProfiles(parentUserId);
final int profilesSize = profiles.size();
for (int i = 0; i < profilesSize; i++) {
final UserInfo userInfo = profiles.get(i);
if (userInfo.isManagedProfile() && !mLockPatternUtils
.isSeparateProfileChallengeEnabled(userInfo.id)) {
removeAllFaceForUserAndFinish(userInfo.id, tracker);
hasChildProfile = true;
break;
}
}
}
if (!hasChildProfile) {
tracker.onFaceDone();
}
}

View File

@@ -126,6 +126,11 @@ public class ChooseLockPassword extends SettingsActivity {
return this;
}
public IntentBuilder setForFace(boolean forFace) {
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
return this;
}
public Intent build() {
return mIntent;
}
@@ -144,11 +149,18 @@ public class ChooseLockPassword extends SettingsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean forFingerprint = getIntent()
final boolean forFingerprint = getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
CharSequence msg = getText(forFingerprint
? R.string.lockpassword_choose_your_password_header_for_fingerprint
: R.string.lockpassword_choose_your_screen_lock_header);
final boolean forFace = getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
CharSequence msg = getText(R.string.lockpassword_choose_your_screen_lock_header);
if (forFingerprint) {
msg = getText(R.string.lockpassword_choose_your_password_header_for_fingerprint);
} else if (forFace) {
msg = getText(R.string.lockpassword_choose_your_password_header_for_face);
}
setTitle(msg);
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
layout.setFitsSystemWindows(false);
@@ -193,6 +205,7 @@ public class ChooseLockPassword extends SettingsActivity {
private PasswordRequirementAdapter mPasswordRequirementAdapter;
private GlifLayout mLayout;
protected boolean mForFingerprint;
protected boolean mForFace;
private String mFirstPin;
private RecyclerView mPasswordRestrictionView;
@@ -235,19 +248,23 @@ public class ChooseLockPassword extends SettingsActivity {
protected enum Stage {
Introduction(
R.string.lockpassword_choose_your_screen_lock_header,
R.string.lockpassword_choose_your_screen_lock_header, // password
R.string.lockpassword_choose_your_password_header_for_fingerprint,
R.string.lockpassword_choose_your_screen_lock_header,
R.string.lockpassword_choose_your_password_header_for_face,
R.string.lockpassword_choose_your_screen_lock_header, // pin
R.string.lockpassword_choose_your_pin_header_for_fingerprint,
R.string.lockpassword_choose_your_password_message,
R.string.lock_settings_picker_fingerprint_added_security_message,
R.string.lockpassword_choose_your_pin_header_for_face,
R.string.lockpassword_choose_your_password_message, // added security message
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pin_message,
R.string.lock_settings_picker_fingerprint_added_security_message,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.next_label),
NeedToConfirm(
R.string.lockpassword_confirm_your_password_header,
R.string.lockpassword_confirm_your_password_header,
R.string.lockpassword_confirm_your_password_header,
R.string.lockpassword_confirm_your_pin_header,
R.string.lockpassword_confirm_your_pin_header,
R.string.lockpassword_confirm_your_pin_header,
0,
@@ -259,6 +276,8 @@ public class ChooseLockPassword extends SettingsActivity {
ConfirmWrong(
R.string.lockpassword_confirm_passwords_dont_match,
R.string.lockpassword_confirm_passwords_dont_match,
R.string.lockpassword_confirm_passwords_dont_match,
R.string.lockpassword_confirm_pins_dont_match,
R.string.lockpassword_confirm_pins_dont_match,
R.string.lockpassword_confirm_pins_dont_match,
0,
@@ -267,45 +286,71 @@ public class ChooseLockPassword extends SettingsActivity {
0,
R.string.lockpassword_confirm_label);
Stage(int hintInAlpha, int hintInAlphaForFingerprint,
int hintInNumeric, int hintInNumericForFingerprint,
int messageInAlpha, int messageInAlphaForFingerprint,
int messageInNumeric, int messageInNumericForFingerprint,
Stage(int hintInAlpha, int hintInAlphaForFingerprint, int hintInAlphaForFace,
int hintInNumeric, int hintInNumericForFingerprint, int hintInNumericForFace,
int messageInAlpha, int messageInAlphaForBiometrics,
int messageInNumeric, int messageInNumericForBiometrics,
int nextButtonText) {
this.alphaHint = hintInAlpha;
this.alphaHintForFingerprint = hintInAlphaForFingerprint;
this.alphaHintForFace = hintInAlphaForFace;
this.numericHint = hintInNumeric;
this.numericHintForFingerprint = hintInNumericForFingerprint;
this.numericHintForFace = hintInNumericForFace;
this.alphaMessage = messageInAlpha;
this.alphaMessageForFingerprint = messageInAlphaForFingerprint;
this.alphaMessageForBiometrics = messageInAlphaForBiometrics;
this.numericMessage = messageInNumeric;
this.numericMessageForFingerprint = messageInNumericForFingerprint;
this.numericMessageForBiometrics = messageInNumericForBiometrics;
this.buttonText = nextButtonText;
}
public static final int TYPE_NONE = 0;
public static final int TYPE_FINGERPRINT = 1;
public static final int TYPE_FACE = 2;
// Password
public final int alphaHint;
public final int alphaHintForFingerprint;
public final int alphaHintForFace;
// PIN
public final int numericHint;
public final int numericHintForFingerprint;
public final int numericHintForFace;
public final int alphaMessage;
public final int alphaMessageForFingerprint;
public final int alphaMessageForBiometrics;
public final int numericMessage;
public final int numericMessageForFingerprint;
public final int numericMessageForBiometrics;
public final int buttonText;
public @StringRes int getHint(boolean isAlpha, boolean isFingerprint) {
public @StringRes int getHint(boolean isAlpha, int type) {
if (isAlpha) {
return isFingerprint ? alphaHintForFingerprint : alphaHint;
if (type == TYPE_FINGERPRINT) {
return alphaHintForFingerprint;
} else if (type == TYPE_FACE) {
return alphaHintForFace;
} else {
return alphaHint;
}
} else {
return isFingerprint ? numericHintForFingerprint : numericHint;
if (type == TYPE_FINGERPRINT) {
return numericHintForFingerprint;
} else if (type == TYPE_FACE) {
return numericHintForFace;
} else {
return numericHint;
}
}
}
public @StringRes int getMessage(boolean isAlpha, boolean isFingerprint) {
public @StringRes int getMessage(boolean isAlpha, int type) {
if (isAlpha) {
return isFingerprint ? alphaMessageForFingerprint : alphaMessage;
return type != TYPE_NONE ? alphaMessageForBiometrics : alphaMessage;
} else {
return isFingerprint ? numericMessageForFingerprint : numericMessage;
return type != TYPE_NONE ? numericMessageForBiometrics : numericMessage;
}
}
}
@@ -327,6 +372,7 @@ public class ChooseLockPassword extends SettingsActivity {
mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
mForFingerprint = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
processPasswordRequirements(intent);
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
@@ -374,6 +420,8 @@ public class ChooseLockPassword extends SettingsActivity {
mMessage = view.findViewById(R.id.message);
if (mForFingerprint) {
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_fingerprint_header));
} else if (mForFace) {
mLayout.setIcon(getActivity().getDrawable(R.drawable.ic_face_header));
}
mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
@@ -433,12 +481,18 @@ public class ChooseLockPassword extends SettingsActivity {
if (activity instanceof SettingsActivity) {
final SettingsActivity sa = (SettingsActivity) activity;
int title = Stage.Introduction.getHint(mIsAlphaMode, mForFingerprint);
int title = Stage.Introduction.getHint(mIsAlphaMode, getStageType());
sa.setTitle(title);
mLayout.setHeaderText(title);
}
}
private int getStageType() {
return mForFingerprint ? Stage.TYPE_FINGERPRINT :
mForFace ? Stage.TYPE_FACE :
Stage.TYPE_NONE;
}
private void setupPasswordRequirementsView(View view) {
final List<Integer> passwordRequirements = new ArrayList<>();
if (mPasswordMinUpperCase > 0) {
@@ -844,11 +898,11 @@ public class ChooseLockPassword extends SettingsActivity {
} else {
// Hide password requirement view when we are just asking user to confirm the pw.
mPasswordRestrictionView.setVisibility(View.GONE);
setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, mForFingerprint)));
setHeaderText(getString(mUiStage.getHint(mIsAlphaMode, getStageType())));
setNextEnabled(canInput && length >= mPasswordMinLength);
mClearButton.setEnabled(canInput && length > 0);
}
int message = mUiStage.getMessage(mIsAlphaMode, mForFingerprint);
int message = mUiStage.getMessage(mIsAlphaMode, getStageType());
if (message != 0) {
mMessage.setVisibility(View.VISIBLE);
mMessage.setText(message);

View File

@@ -121,6 +121,11 @@ public class ChooseLockPattern extends SettingsActivity {
return this;
}
public IntentBuilder setForFace(boolean forFace) {
mIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
return this;
}
public Intent build() {
return mIntent;
}
@@ -140,10 +145,19 @@ public class ChooseLockPattern extends SettingsActivity {
protected void onCreate(Bundle savedInstanceState) {
// requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
boolean forFingerprint = getIntent()
final boolean forFingerprint = getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
setTitle(forFingerprint ? R.string.lockpassword_choose_your_pattern_header_for_fingerprint
: R.string.lockpassword_choose_your_screen_lock_header);
final boolean forFace = getIntent()
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
int msg = R.string.lockpassword_choose_your_screen_lock_header;
if (forFingerprint) {
msg = R.string.lockpassword_choose_your_pattern_header_for_fingerprint;
} else if (forFace) {
msg = R.string.lockpassword_choose_your_pattern_header_for_face;
}
setTitle(msg);
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
layout.setFitsSystemWindows(false);
}
@@ -344,7 +358,7 @@ public class ChooseLockPattern extends SettingsActivity {
protected enum Stage {
Introduction(
R.string.lock_settings_picker_fingerprint_added_security_message,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pattern_message,
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Gone, RightButtonMode.ContinueDisabled,
@@ -353,13 +367,13 @@ public class ChooseLockPattern extends SettingsActivity {
ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
R.string.lock_settings_picker_fingerprint_added_security_message,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pattern_message,
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
R.string.lock_settings_picker_fingerprint_added_security_message,
R.string.lock_settings_picker_biometrics_added_security_message,
R.string.lockpassword_choose_your_pattern_message,
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
@@ -377,7 +391,7 @@ public class ChooseLockPattern extends SettingsActivity {
/**
* @param messageForFingerprint The message displayed at the top, above header for
* @param messageForBiometrics The message displayed at the top, above header for
* fingerprint flow.
* @param message The message displayed at the top.
* @param headerMessage The message displayed at the top.
@@ -386,12 +400,12 @@ public class ChooseLockPattern extends SettingsActivity {
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
Stage(int messageForFingerprint, int message, int headerMessage,
Stage(int messageForBiometrics, int message, int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
this.messageForFingerprint = messageForFingerprint;
this.messageForBiometrics = messageForBiometrics;
this.message = message;
this.leftMode = leftMode;
this.rightMode = rightMode;
@@ -400,7 +414,7 @@ public class ChooseLockPattern extends SettingsActivity {
}
final int headerMessage;
final int messageForFingerprint;
final int messageForBiometrics;
final int message;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
@@ -420,6 +434,7 @@ public class ChooseLockPattern extends SettingsActivity {
private SaveAndFinishWorker mSaveAndFinishWorker;
protected int mUserId;
protected boolean mForFingerprint;
protected boolean mForFace;
private static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
@@ -451,6 +466,8 @@ public class ChooseLockPattern extends SettingsActivity {
mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
mForFingerprint = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
mForFace = intent.getBooleanExtra(
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
}
@Override
@@ -467,6 +484,8 @@ public class ChooseLockPattern extends SettingsActivity {
} else {
if (mForFingerprint) {
layout.setIcon(getActivity().getDrawable(R.drawable.ic_fingerprint_header));
} else if (mForFace) {
layout.setIcon(getActivity().getDrawable(R.drawable.ic_face_header));
}
}
return layout;
@@ -663,7 +682,8 @@ public class ChooseLockPattern extends SettingsActivity {
} else {
mHeaderText.setText(stage.headerMessage);
}
int message = mForFingerprint ? stage.messageForFingerprint : stage.message;
final boolean forBiometrics = mForFingerprint || mForFace;
int message = forBiometrics ? stage.messageForBiometrics : stage.message;
if (message == ID_EMPTY_MESSAGE) {
mMessageText.setText("");
} else {
@@ -686,7 +706,7 @@ public class ChooseLockPattern extends SettingsActivity {
mHeaderText.setTextColor(mDefaultHeaderColorList);
}
if (stage == Stage.NeedToConfirm && mForFingerprint) {
if (stage == Stage.NeedToConfirm && forBiometrics) {
mHeaderText.setText("");
mTitleText.setText(R.string.lockpassword_draw_your_pattern_again_header);
}

View File

@@ -41,6 +41,7 @@ public final class ChooseLockSettingsHelper {
public static final String EXTRA_KEY_CHALLENGE = "challenge";
public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
public static final String EXTRA_KEY_FOR_FACE = "for_face";
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
/**

View File

@@ -17,6 +17,7 @@
package com.android.settings.password;
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -27,6 +28,7 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.IBinder;
@@ -57,6 +59,8 @@ final class SetNewPasswordController {
private final PackageManager mPackageManager;
@Nullable
private final FingerprintManager mFingerprintManager;
@Nullable
private final FaceManager mFaceManager;
private final DevicePolicyManager mDevicePolicyManager;
private final Ui mUi;
@@ -77,9 +81,10 @@ final class SetNewPasswordController {
}
// Create a wrapper of FingerprintManager for testing, see IFingerPrintManager for details.
final FingerprintManager fingerprintManager = Utils.getFingerprintManagerOrNull(context);
final FaceManager faceManager = Utils.getFaceManagerOrNull(context);
return new SetNewPasswordController(userId,
context.getPackageManager(),
fingerprintManager,
fingerprintManager, faceManager,
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE), ui);
}
@@ -88,11 +93,13 @@ final class SetNewPasswordController {
int targetUserId,
PackageManager packageManager,
FingerprintManager fingerprintManager,
FaceManager faceManager,
DevicePolicyManager devicePolicyManager,
Ui ui) {
mTargetUserId = targetUserId;
mPackageManager = checkNotNull(packageManager);
mFingerprintManager = fingerprintManager;
mFaceManager = faceManager;
mDevicePolicyManager = checkNotNull(devicePolicyManager);
mUi = checkNotNull(ui);
}
@@ -102,7 +109,14 @@ final class SetNewPasswordController {
*/
public void dispatchSetNewPasswordIntent() {
final Bundle extras;
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
// TODO: handle the case with multiple biometrics, perhaps take an arg for biometric type?
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
&& mFaceManager != null
&& mFaceManager.isHardwareDetected()
&& !mFaceManager.hasEnrolledFaces(mTargetUserId)
&& !isFaceDisabledByAdmin()) {
extras = getFaceChooseLockExtras();
} else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
&& mFingerprintManager != null
&& mFingerprintManager.isHardwareDetected()
&& !mFingerprintManager.hasEnrolledFingerprints(mTargetUserId)
@@ -130,9 +144,28 @@ final class SetNewPasswordController {
return chooseLockExtras;
}
private Bundle getFaceChooseLockExtras() {
Bundle chooseLockExtras = new Bundle();
long challenge = mFaceManager.preEnroll();
chooseLockExtras.putInt(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
PASSWORD_QUALITY_SOMETHING);
chooseLockExtras.putBoolean(
ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
chooseLockExtras.putLong(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
chooseLockExtras.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, true);
return chooseLockExtras;
}
private boolean isFingerprintDisabledByAdmin() {
int disabledFeatures =
mDevicePolicyManager.getKeyguardDisabledFeatures(null, mTargetUserId);
return (disabledFeatures & KEYGUARD_DISABLE_FINGERPRINT) != 0;
}
private boolean isFaceDisabledByAdmin() {
int disabledFeatures =
mDevicePolicyManager.getKeyguardDisabledFeatures(null, mTargetUserId);
return (disabledFeatures & KEYGUARD_DISABLE_FACE) != 0;
}
}

View File

@@ -102,8 +102,8 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
@Override
protected void addHeaderView() {
if (mForFingerprint) {
setHeaderView(R.layout.setup_choose_lock_generic_fingerprint_header);
if (mForFingerprint || mForFace) {
setHeaderView(R.layout.setup_choose_lock_generic_biometrics_header);
} else {
setHeaderView(R.layout.setup_choose_lock_generic_header);
}

View File

@@ -15,7 +15,8 @@
*/
package com.android.settings.security;
import static com.android.settings.security.EncryptionStatusPreferenceController.PREF_KEY_ENCRYPTION_SECURITY_PAGE;
import static com.android.settings.security.EncryptionStatusPreferenceController
.PREF_KEY_ENCRYPTION_SECURITY_PAGE;
import android.app.Activity;
import android.content.Context;
@@ -26,11 +27,12 @@ import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintProfileStatusPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.enterprise.EnterprisePrivacyPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintProfileStatusPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.location.LocationPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.security.trustagent.ManageTrustAgentsPreferenceController;
@@ -123,6 +125,7 @@ public class SecuritySettings extends DashboardFragment {
controllers.add(new TrustAgentListPreferenceController(context, host, lifecycle));
final List<AbstractPreferenceController> securityPreferenceControllers = new ArrayList<>();
securityPreferenceControllers.add(new FaceStatusPreferenceController(context));
securityPreferenceControllers.add(new FingerprintStatusPreferenceController(context));
securityPreferenceControllers.add(new ChangeScreenLockPreferenceController(context, host));
controllers.add(new PreferenceCategoryController(context, SECURITY_CATEGORY)

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2018 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 com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.os.UserManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import java.util.Collections;
import androidx.preference.Preference;
@RunWith(SettingsRobolectricTestRunner.class)
public class FaceStatusPreferenceControllerTest {
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
private FaceManager mFaceManager;
@Mock
private UserManager mUm;
@Mock
private PackageManager mPackageManager;
private FakeFeatureFactory mFeatureFactory;
private Context mContext;
private FaceStatusPreferenceController mController;
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
ShadowApplication.getInstance().setSystemService(Context.FACE_SERVICE, mFaceManager);
ShadowApplication.getInstance().setSystemService(Context.USER_SERVICE, mUm);
mPreference = new Preference(mContext);
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mFeatureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
.thenReturn(mLockPatternUtils);
when(mUm.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[] {1234});
mController = new FaceStatusPreferenceController(mContext);
}
@Test
public void getAvailabilityStatus_noFaceManger_DISABLED() {
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_hasFaceManger_AVAILABLE() {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void updateState_notSupported_shouldDoNothing() {
when(mFaceManager.isHardwareDetected()).thenReturn(false);
mController.updateState(mPreference);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void updateState_noFace_shouldShowDefaultSummary() {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo(
mContext.getString(R.string.security_settings_face_preference_summary_none));
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
}
@Test
public void updateState_hasFace_shouldShowSummary() {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.getEnrolledFaces(anyInt()))
.thenReturn(Collections.singletonList(mock(Face.class)));
when(mFaceManager.hasEnrolledFaces(anyInt()))
.thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo(mContext.getResources()
.getString(R.string.security_settings_face_preference_summary));
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
}
}

View File

@@ -31,6 +31,7 @@ import android.os.CancellationSignal;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -137,7 +138,7 @@ public class FingerprintEnrollFindSensorTest {
ShadowActivity shadowActivity = Shadows.shadowOf(mActivity);
assertThat(shadowActivity.getResultCode()).named("result code")
.isEqualTo(FingerprintEnrollBase.RESULT_SKIP);
.isEqualTo(BiometricEnrollBase.RESULT_SKIP);
}
private EnrollmentCallback verifyAndCaptureEnrollmentCallback() {

View File

@@ -121,6 +121,8 @@ public class FingerprintStatusPreferenceControllerTest {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(Collections.singletonList(mock(Fingerprint.class)));
when(mFingerprintManager.hasEnrolledFingerprints(anyInt()))
.thenReturn(true);
mController.updateState(mPreference);

View File

@@ -27,6 +27,8 @@ import android.view.View;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroductionTest.ShadowStorageManagerWrapper;
import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment;
import com.android.settings.password.SetupSkipDialog;
@@ -118,7 +120,7 @@ public class SetupFingerprintEnrollIntroductionTest {
ShadowActivity shadowActivity = Shadows.shadowOf(mController.get());
assertThat(mController.get().isFinishing()).named("Is finishing").isTrue();
assertThat(shadowActivity.getResultCode()).named("Result code")
.isEqualTo(FingerprintEnrollBase.RESULT_SKIP);
.isEqualTo(BiometricEnrollBase.RESULT_SKIP);
}
@Test
@@ -179,8 +181,8 @@ public class SetupFingerprintEnrollIntroductionTest {
public void testOnResultFromFindSensor_shouldNotSetIntentDataIfLockScreenPresentBeforeLaunch() {
getShadowKeyguardManager().setIsKeyguardSecure(true);
SetupFingerprintEnrollIntroduction activity = mController.create().resume().get();
activity.onActivityResult(FingerprintEnrollIntroduction.FINGERPRINT_FIND_SENSOR_REQUEST,
FingerprintEnrollBase.RESULT_FINISHED, null);
activity.onActivityResult(BiometricEnrollIntroduction.BIOMETRIC_FIND_SENSOR_REQUEST,
BiometricEnrollBase.RESULT_FINISHED, null);
assertThat(Shadows.shadowOf(activity).getResultIntent()).isNull();
}
@@ -189,8 +191,8 @@ public class SetupFingerprintEnrollIntroductionTest {
getShadowKeyguardManager().setIsKeyguardSecure(false);
SetupFingerprintEnrollIntroduction activity = mController.create().resume().get();
getShadowKeyguardManager().setIsKeyguardSecure(true);
activity.onActivityResult(FingerprintEnrollIntroduction.FINGERPRINT_FIND_SENSOR_REQUEST,
FingerprintEnrollBase.RESULT_FINISHED, null);
activity.onActivityResult(BiometricEnrollIntroduction.BIOMETRIC_FIND_SENSOR_REQUEST,
BiometricEnrollBase.RESULT_FINISHED, null);
assertThat(Shadows.shadowOf(activity).getResultIntent()).isNotNull();
}
@@ -198,8 +200,8 @@ public class SetupFingerprintEnrollIntroductionTest {
public void testOnResultFromFindSensor_shouldNotSetIntentDataIfLockScreenNotAdded() {
getShadowKeyguardManager().setIsKeyguardSecure(false);
SetupFingerprintEnrollIntroduction activity = mController.create().resume().get();
activity.onActivityResult(FingerprintEnrollIntroduction.FINGERPRINT_FIND_SENSOR_REQUEST,
FingerprintEnrollBase.RESULT_FINISHED, null);
activity.onActivityResult(BiometricEnrollIntroduction.BIOMETRIC_FIND_SENSOR_REQUEST,
BiometricEnrollBase.RESULT_FINISHED, null);
assertThat(Shadows.shadowOf(activity).getResultIntent()).isNull();
}

View File

@@ -16,12 +16,14 @@
package com.android.settings.password;
import static android.content.pm.PackageManager.FEATURE_FACE;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment
.HIDE_DISABLED_PREFS;
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment
.MINIMUM_QUALITY_KEY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE;
import static com.google.common.truth.Truth.assertThat;
@@ -36,6 +38,7 @@ import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
@@ -53,12 +56,15 @@ public final class SetNewPasswordControllerTest {
private static final int CURRENT_USER_ID = 101;
private static final long FINGERPRINT_CHALLENGE = -9876512313131L;
private static final long FACE_CHALLENGE = 1352057789L;
@Mock
private PackageManager mPackageManager;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private SetNewPasswordController.Ui mUi;
@@ -69,10 +75,14 @@ public final class SetNewPasswordControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mSetNewPasswordController = new SetNewPasswordController(
CURRENT_USER_ID, mPackageManager, mFingerprintManager, mDevicePolicyManager, mUi);
CURRENT_USER_ID, mPackageManager, mFingerprintManager, mFaceManager,
mDevicePolicyManager, mUi);
when(mFingerprintManager.preEnroll()).thenReturn(FINGERPRINT_CHALLENGE);
when(mPackageManager.hasSystemFeature(eq(FEATURE_FINGERPRINT))).thenReturn(true);
when(mFaceManager.preEnroll()).thenReturn(FACE_CHALLENGE);
when(mPackageManager.hasSystemFeature(eq(FEATURE_FACE))).thenReturn(true);
}
@Test
@@ -95,6 +105,26 @@ public final class SetNewPasswordControllerTest {
compareFingerprintExtras(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithFace() {
// GIVEN the device supports face.
when(mFaceManager.isHardwareDetected()).thenReturn(true);
// GIVEN there are no enrolled face.
when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(false);
// GIVEN DPC does not disallow face for keyguard usage.
when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
.thenReturn(0);
// WHEN the controller dispatches a set new password intent.
mSetNewPasswordController.dispatchSetNewPasswordIntent();
// THEN the choose lock activity is launched with face extras.
ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
// THEN the extras have all values for face setup.
compareFaceExtras(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFingerprint_noFingerprintFeature() {
// GIVEN the device does NOT support fingerprint feature.
@@ -109,6 +139,20 @@ public final class SetNewPasswordControllerTest {
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFace_no_FaceFeature() {
// GIVEN the device does NOT support face feature.
when(mPackageManager.hasSystemFeature(eq(FEATURE_FACE))).thenReturn(false);
// WHEN the controller dispatches a set new password intent.
mSetNewPasswordController.dispatchSetNewPasswordIntent();
// THEN the choose lock activity is launched without face extras.
ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFingerprint_noFingerprintSensor() {
// GIVEN the device does NOT support fingerprint.
@@ -128,6 +172,25 @@ public final class SetNewPasswordControllerTest {
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFace_noFaceSensor() {
// GIVEN the device does NOT support face.
when(mFaceManager.isHardwareDetected()).thenReturn(false);
// GIVEN there are no enrolled face.
when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(false);
// GIVEN DPC does not disallow face for keyguard usage.
when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
.thenReturn(0);
// WHEN the controller dispatches a set new password intent.
mSetNewPasswordController.dispatchSetNewPasswordIntent();
// THEN the choose lock activity is launched without a bundle contains user id only.
ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFingerprint_hasFingerprintEnrolled() {
// GIVEN the device supports fingerprint.
@@ -147,6 +210,25 @@ public final class SetNewPasswordControllerTest {
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFace_hasFaceEnrolled() {
// GIVEN the device supports face.
when(mFaceManager.isHardwareDetected()).thenReturn(true);
// GIVEN there are no enrolled face.
when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(true);
// GIVEN DPC does not disallow face for keyguard usage.
when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
.thenReturn(0);
// WHEN the controller dispatches a set new password intent.
mSetNewPasswordController.dispatchSetNewPasswordIntent();
// THEN the choose lock activity is launched without a bundle contains user id only.
ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFingerprint_deviceAdminDisallowFingerprintForKeyguard() {
// GIVEN the device supports fingerprint.
@@ -166,6 +248,25 @@ public final class SetNewPasswordControllerTest {
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
@Test
public void launchChooseLockWithoutFace_deviceAdminDisallowFaceForKeyguard() {
// GIVEN the device supports face.
when(mFaceManager.isHardwareDetected()).thenReturn(true);
// GIVEN there is an enrolled face.
when(mFaceManager.hasEnrolledFaces(CURRENT_USER_ID)).thenReturn(true);
// GIVEN DPC disallows face for keyguard usage.
when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(ComponentName.class)))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
// WHEN the controller dispatches a set new password intent.
mSetNewPasswordController.dispatchSetNewPasswordIntent();
// THEN the choose lock activity is launched without a bundle contains user id only.
ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mUi).launchChooseLock(bundleArgumentCaptor.capture());
assertBundleContainsUserIdOnly(bundleArgumentCaptor.getValue());
}
private void compareFingerprintExtras(Bundle actualBundle) {
assertEquals(
"Password quality must be something in order to config fingerprint.",
@@ -190,6 +291,30 @@ public final class SetNewPasswordControllerTest {
actualBundle.getInt(Intent.EXTRA_USER_ID));
}
private void compareFaceExtras(Bundle actualBundle) {
assertEquals(
"Password quality must be something in order to config face.",
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
actualBundle.getInt(MINIMUM_QUALITY_KEY));
assertTrue(
"All disabled preference should be removed.",
actualBundle.getBoolean(HIDE_DISABLED_PREFS));
assertTrue(
"There must be a face challenge.",
actualBundle.getBoolean(EXTRA_KEY_HAS_CHALLENGE));
assertEquals(
"The face challenge must come from the FaceManager",
FACE_CHALLENGE,
actualBundle.getLong(EXTRA_KEY_CHALLENGE));
assertTrue(
"The request must be a face set up request.",
actualBundle.getBoolean(EXTRA_KEY_FOR_FACE));
assertEquals(
"User id must be equaled to the input one.",
CURRENT_USER_ID,
actualBundle.getInt(Intent.EXTRA_USER_ID));
}
private void assertBundleContainsUserIdOnly(Bundle actualBundle) {
assertThat(actualBundle.size()).isEqualTo(1);
assertThat(actualBundle.getInt(Intent.EXTRA_USER_ID)).isEqualTo(CURRENT_USER_ID);