OSDN Git Service

2/n: Add face to ChooseLock*
authorKevin Chyn <kchyn@google.com>
Thu, 28 Jun 2018 21:59:38 +0000 (14:59 -0700)
committerKevin Chyn <kchyn@google.com>
Sat, 30 Jun 2018 01:19:34 +0000 (18:19 -0700)
Bug: 110589286

Test: manual
Test: make -j56 RunSettingsRoboTests
Test: setting up new fingerprint still works
Change-Id: I1b7d2bb6bb417dae2c99e5abeb68d3f694cb3cb8

13 files changed:
res/layout/choose_lock_generic_face_header.xml [new file with mode: 0644]
res/layout/setup_choose_lock_generic_biometrics_header.xml [moved from res/layout/setup_choose_lock_generic_fingerprint_header.xml with 92% similarity]
res/values/strings.xml
res/values/styles.xml
res/xml/security_settings_picker.xml
src/com/android/settings/EncryptionInterstitial.java
src/com/android/settings/password/ChooseLockGeneric.java
src/com/android/settings/password/ChooseLockPassword.java
src/com/android/settings/password/ChooseLockPattern.java
src/com/android/settings/password/ChooseLockSettingsHelper.java
src/com/android/settings/password/SetNewPasswordController.java
src/com/android/settings/password/SetupChooseLockGeneric.java
tests/robotests/src/com/android/settings/password/SetNewPasswordControllerTest.java

diff --git a/res/layout/choose_lock_generic_face_header.xml b/res/layout/choose_lock_generic_face_header.xml
new file mode 100644 (file)
index 0000000..b97b0d6
--- /dev/null
@@ -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" />
@@ -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" />
index 0ff5318..2d80b85 100644 (file)
     <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>
     <!-- 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>
 
     <!-- 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>
     <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]-->
     <!-- 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] -->
index ba462d8..4e5bdda 100644 (file)
         <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>
index 6b52156..2e6361a 100644 (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>
index f0115b8..9558d75 100644 (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;
             }
index 137270c..5dadd06 100644 (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;
 
@@ -53,8 +54,8 @@ import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollBase;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 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 +100,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";
@@ -151,6 +153,7 @@ public class ChooseLockGeneric extends SettingsActivity {
         private ChooseLockGenericController mController;
 
         protected boolean mForFingerprint = false;
+        protected boolean mForFace = false;
 
         @Override
         public int getMetricsCategory() {
@@ -185,6 +188,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 +253,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 +271,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 +321,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,
@@ -430,6 +443,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 +455,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 +473,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 +604,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 +619,7 @@ public class ChooseLockGeneric extends SettingsActivity {
             ChooseLockPattern.IntentBuilder builder =
                     new ChooseLockPattern.IntentBuilder(getContext())
                             .setForFingerprint(mForFingerprint)
+                            .setForFace(mForFace)
                             .setUserId(mUserId);
             if (mHasChallenge) {
                 builder.setChallenge(mChallenge);
index 81ea680..15dd72f 100644 (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);
index 95759f3..855f7ae 100644 (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);
                 }
index 4dafda6..4839154 100644 (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";
 
     /**
index a8d51ed..ecbaec7 100644 (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;
+    }
 }
index 0540bbf..190cc0f 100644 (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);
             }
index b557257..97fb777 100644 (file)
 
 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
@@ -96,6 +106,26 @@ public final class SetNewPasswordControllerTest {
     }
 
     @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.
         when(mPackageManager.hasSystemFeature(eq(FEATURE_FINGERPRINT))).thenReturn(false);
@@ -110,6 +140,20 @@ public final class SetNewPasswordControllerTest {
     }
 
     @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.
         when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
@@ -129,6 +173,25 @@ public final class SetNewPasswordControllerTest {
     }
 
     @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.
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
@@ -148,6 +211,25 @@ public final class SetNewPasswordControllerTest {
     }
 
     @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.
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
@@ -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);