Merge "Use ColorDisplayManager for night display"
diff --git a/res/layout/face_enroll_introduction.xml b/res/layout/face_enroll_introduction.xml
index 69b00fe..4096925 100644
--- a/res/layout/face_enroll_introduction.xml
+++ b/res/layout/face_enroll_introduction.xml
@@ -85,12 +85,6 @@
                     android:layout_height="wrap_content"
                     FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_diversity"/>
 
-                <com.android.settings.biometrics.face.FaceEnrollAccessibilityToggle
-                    android:id="@+id/toggle_vision"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_vision"/>
-
             </LinearLayout>
         </FrameLayout>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f77c10d..b1edbb4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1430,6 +1430,16 @@
     <!-- Title shown on security settings to allow the user to change their lockscreen password [CHAR LIMIT=22]-->
     <string name="unlock_change_lock_password_title">Change unlock password</string>
 
+    <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a strong PIN or password  [CHAR LIMIT=NONE] -->
+    <string name="unlock_footer_high_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a strong PIN or password.</string>
+    <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a medium strength PIN or password [CHAR LIMIT=NONE] -->
+    <string name="unlock_footer_medium_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new PIN or password.</string>
+    <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and it requests for any screen lock [CHAR LIMIT=NONE] -->
+    <string name="unlock_footer_low_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new pattern, PIN or password.</string>
+    <!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock [CHAR LIMIT=NONE] -->
+    <string name="unlock_footer_none_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new screen lock.</string>
+
+
     <!-- Message shown on the lock screen when the user incorrectly enters their lock and it counts towards the max attempts before their data on the device is wiped. [CHAR LIMIT=NONE] -->
     <string name="lock_failed_attempts_before_wipe">Try again. Attempt <xliff:g id="current_attempts">%1$d</xliff:g> of <xliff:g id="total_attempts">%2$d</xliff:g>.</string>
 
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index 5ab543f..0485a0f 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -55,40 +55,7 @@
 import sun.security.x509.AlgorithmId;
 
 /**
- * CredentialStorage handles KeyStore reset, unlock, and install.
- *
- * CredentialStorage has a pretty convoluted state machine to migrate
- * from the old style separate keystore password to a new key guard
- * based password, as well as to deal with setting up the key guard if
- * necessary.
- *
- * KeyStore: UNINITALIZED
- * KeyGuard: OFF
- * Action:   set up key guard
- * Notes:    factory state
- *
- * KeyStore: UNINITALIZED
- * KeyGuard: ON
- * Action:   confirm key guard
- * Notes:    user had key guard but no keystore and upgraded from pre-ICS
- * OR user had key guard and pre-ICS keystore password which was then reset
- *
- * KeyStore: LOCKED
- * KeyGuard: OFF/ON
- * Action:   confirm key guard
- * Notes:    request normal unlock to unlock the keystore.
- * if unlock, ensure key guard before install.
- * if reset, treat as UNINITALIZED/OFF
- *
- * KeyStore: UNLOCKED
- * KeyGuard: OFF
- * Action:   set up key guard
- * Notes:    ensure key guard, then proceed
- *
- * KeyStore: UNLOCKED
- * keyguard: ON
- * Action:   normal unlock/install
- * Notes:    this is the common case
+ * CredentialStorage handles resetting and installing keys into KeyStore.
  */
 public final class CredentialStorage extends FragmentActivity {
 
@@ -102,8 +69,7 @@
     // lower than this, keystore should not be activated.
     public static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 
-    private static final int CONFIRM_KEY_GUARD_REQUEST = 1;
-    private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 2;
+    private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1;
 
     private final KeyStore mKeyStore = KeyStore.getInstance();
     private LockPatternUtils mUtils;
@@ -133,75 +99,26 @@
                 if (ACTION_INSTALL.equals(action) && checkCallerIsCertInstallerOrSelfInProfile()) {
                     mInstallBundle = intent.getExtras();
                 }
-                // ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
-                handleUnlockOrInstall();
+                handleInstall();
             }
         } else {
-            // Users can set a screen lock if there is none even if they can't modify the
-            // credentials store.
-            if (ACTION_UNLOCK.equals(action) && mKeyStore.state() == KeyStore.State.UNINITIALIZED) {
-                ensureKeyGuard();
-            } else {
-                finish();
-            }
+            finish();
         }
     }
 
     /**
-     * Based on the current state of the KeyStore and key guard, try to
-     * make progress on unlocking or installing to the keystore.
+     * Install credentials from mInstallBundle into Keystore.
      */
-    private void handleUnlockOrInstall() {
+    private void handleInstall() {
         // something already decided we are done, do not proceed
         if (isFinishing()) {
             return;
         }
-        switch (mKeyStore.state()) {
-            case UNINITIALIZED: {
-                ensureKeyGuard();
-                return;
-            }
-            case LOCKED: {
-                // Force key guard confirmation
-                confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST);
-                return;
-            }
-            case UNLOCKED: {
-                if (!mUtils.isSecure(UserHandle.myUserId())) {
-                    final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
-                    dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG);
-                    return;
-                }
-                if (installIfAvailable()) {
-                    finish();
-                }
-                return;
-            }
+        if (installIfAvailable()) {
+            finish();
         }
     }
 
-    /**
-     * Make sure the user enters the key guard to set or change the
-     * keystore password. This can be used in UNINITIALIZED to set the
-     * keystore password or UNLOCKED to change the password (as is the
-     * case after unlocking with an old-style password).
-     */
-    private void ensureKeyGuard() {
-        if (!mUtils.isSecure(UserHandle.myUserId())) {
-            // key guard not setup, doing so will initialize keystore
-            final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
-            dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG);
-            // will return to onResume after Activity
-            return;
-        }
-        // force key guard confirmation
-        if (confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST)) {
-            // will return password value via onActivityResult
-            return;
-        }
-        finish();
-    }
-
     private boolean isHardwareBackedKey(byte[] keyData) {
         try {
             final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
@@ -254,15 +171,7 @@
             final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
             final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
 
-            int flags = KeyStore.FLAG_ENCRYPTED;
-            if (uid == Process.WIFI_UID && isHardwareBackedKey(value)) {
-                // Hardware backed keystore is secure enough to allow for WIFI stack
-                // to enable access to secure networks without user intervention
-                Log.d(TAG, "Saving private key with FLAG_NONE for WIFI_UID");
-                flags = KeyStore.FLAG_NONE;
-            }
-
-            if (!mKeyStore.importKey(key, value, uid, flags)) {
+            if (!mKeyStore.importKey(key, value, uid, KeyStore.FLAG_NONE)) {
                 Log.e(TAG, "Failed to install " + key + " as uid " + uid);
                 return true;
             }
@@ -475,20 +384,7 @@
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        // Receive key guard password initiated by confirmKeyGuard.
-        if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
-            if (resultCode == Activity.RESULT_OK) {
-                final String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
-                if (!TextUtils.isEmpty(password)) {
-                    // success
-                    mKeyStore.unlock(password);
-                    // return to onResume
-                    return;
-                }
-            }
-            // failed confirmation, bail
-            finish();
-        } else if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) {
+        if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) {
             if (resultCode == Activity.RESULT_OK) {
                 new ResetKeyStoreAndKeyChain().execute();
                 return;
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index fc4f6ce..71bd02f 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -43,7 +43,6 @@
     private static final String TAG = "FaceIntro";
 
     private FaceManager mFaceManager;
-    private FaceEnrollAccessibilityToggle mSwitchVision;
     private FaceEnrollAccessibilityToggle mSwitchDiversity;
 
     @Override
@@ -57,7 +56,6 @@
             accessibilityLayout.setVisibility(View.VISIBLE);
         });
 
-        mSwitchVision = findViewById(R.id.toggle_vision);
         mSwitchDiversity = findViewById(R.id.toggle_diversity);
 
         mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
@@ -167,7 +165,6 @@
         } else {
             intent.setClass(this, FaceEnrollEnrolling.class);
         }
-        intent.putExtra(EXTRA_KEY_REQUIRE_VISION, mSwitchVision.isChecked());
         intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, mSwitchDiversity.isChecked());
         return intent;
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index c3d49e9..6ff4309 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -109,12 +109,14 @@
         mCachedDevice = getCachedDevice(mDeviceAddress);
         super.onAttach(context);
 
-        if (FeatureFlagUtils.isEnabled(context, FeatureFlags.SLICE_INJECTION)) {
-            final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(context)
-                    .getBluetoothFeatureProvider(context);
-            use(BlockingSlicePrefController.class).setSliceUri(
-                    featureProvider.getBluetoothDeviceSettingsUri(mDeviceAddress));
-        }
+        final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(
+                context).getBluetoothFeatureProvider(context);
+        final boolean injectionEnabled = FeatureFlagUtils.isEnabled(context,
+                FeatureFlags.SLICE_INJECTION);
+
+        use(BlockingSlicePrefController.class).setSliceUri(injectionEnabled
+                ? featureProvider.getBluetoothDeviceSettingsUri(mDeviceAddress)
+                : null);
     }
 
     @Override
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java
index ff26888..1602f56 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorker.java
@@ -30,16 +30,11 @@
 
     private static final String TAG = "BluetoothUpdateWorker";
 
-    private final Context mContext;
-    private final Uri mUri;
     private final LocalBluetoothManager mLocalBluetoothManager;
 
     public BluetoothUpdateWorker(Context context, Uri uri) {
         super(context, uri);
-
-        mContext = context;
-        mUri = uri;
-        mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+        mLocalBluetoothManager = Utils.getLocalBtManager(context);
     }
 
     @Override
@@ -89,8 +84,4 @@
             int bluetoothProfile) {
         notifySliceChange();
     }
-
-    private void notifySliceChange() {
-        mContext.getContentResolver().notifyChange(mUri, null);
-    }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
index 2d84e21..3adc489 100644
--- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
@@ -13,6 +13,8 @@
  */
 package com.android.settings.location;
 
+import static java.util.concurrent.TimeUnit.DAYS;
+
 import android.Manifest;
 import android.content.Context;
 import android.content.Intent;
@@ -74,6 +76,7 @@
                     final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
                     intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
                             Manifest.permission.ACCESS_FINE_LOCATION);
+                    intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
                     mContext.startActivity(intent);
                 });
     }
diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java
index 13ec543..171b61e 100644
--- a/src/com/android/settings/password/BiometricFragment.java
+++ b/src/com/android/settings/password/BiometricFragment.java
@@ -43,6 +43,7 @@
     private static final String KEY_SUBTITLE = "subtitle";
     private static final String KEY_DESCRIPTION = "description";
     private static final String KEY_NEGATIVE_TEXT = "negative_text";
+    private static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation";
 
     // Re-set by the application. Should be done upon orientation changes, etc
     private Executor mClientExecutor;
@@ -127,6 +128,7 @@
             .setDescription(mPromptInfo.getDescription())
             .setNegativeButton(mPromptInfo.getNegativeButtonText(), mClientExecutor,
                     mNegativeButtonListener)
+            .setRequireConfirmation(mPromptInfo.getRequireConfirmation())
             .build();
         mCancellationSignal = new CancellationSignal();
 
@@ -171,6 +173,10 @@
             return mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
         }
 
+        public boolean getRequireConfirmation() {
+            return mBundle.getBoolean(KEY_REQUIRE_CONFIRMATION);
+        }
+
         public static class Builder {
             private final Bundle mBundle = new Bundle();
 
@@ -194,6 +200,11 @@
                 return this;
             }
 
+            public Builder setRequireConfirmation(boolean requireConfirmation) {
+                mBundle.putBoolean(KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+                return this;
+            }
+
             public PromptInfo build() {
                 return new PromptInfo(mBundle);
             }
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index ca96344..cae7d33 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -18,13 +18,20 @@
 
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 
 import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
@@ -66,6 +73,8 @@
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.RestrictedPreference;
+import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.FooterPreferenceMixinCompat;
 
 import java.util.List;
 
@@ -152,6 +161,14 @@
         private UserManager mUserManager;
         private ChooseLockGenericController mController;
 
+        /**
+         * From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_REQUESTED_MIN_COMPLEXITY}.
+         */
+        @PasswordComplexity private int mRequestedMinComplexity;
+
+        /** From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_CALLER_APP_NAME}. */
+        private String mCallerAppName = null;
+
         protected boolean mForFingerprint = false;
         protected boolean mForFace = false;
 
@@ -195,6 +212,10 @@
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
             mForFace = getActivity().getIntent().getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
+            mRequestedMinComplexity = getActivity().getIntent()
+                    .getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+            mCallerAppName =
+                    getActivity().getIntent().getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
             mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
             mUserManager = UserManager.get(getActivity());
@@ -217,7 +238,8 @@
                     UserManager.get(getActivity()),
                     getArguments(),
                     getActivity().getIntent().getExtras()).getIdentifier();
-            mController = new ChooseLockGenericController(getContext(), mUserId);
+            mController =
+                    new ChooseLockGenericController(getContext(), mUserId, mRequestedMinComplexity);
             if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
                     && UserManager.get(getActivity()).isManagedProfile(mUserId)
                     && mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
@@ -291,6 +313,9 @@
                 // Forward the target user id to  ChooseLockGeneric.
                 chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
                 chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
+                chooseLockGenericIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
+                        mRequestedMinComplexity);
+                chooseLockGenericIntent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
                 if (mUserPassword != null) {
                     chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
                             mUserPassword);
@@ -461,6 +486,13 @@
         protected void addPreferences() {
             addPreferencesFromResource(R.xml.security_settings_picker);
 
+            if (!TextUtils.isEmpty(mCallerAppName)) {
+                FooterPreferenceMixinCompat footerMixin =
+                        new FooterPreferenceMixinCompat(this, getSettingsLifecycle());
+                FooterPreference footer = footerMixin.createFooterPreference();
+                footer.setTitle(getFooterString());
+            }
+
             // Used for testing purposes
             findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
             findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
@@ -469,6 +501,27 @@
             findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
         }
 
+        private String getFooterString() {
+            @StringRes int stringId;
+            switch (mRequestedMinComplexity) {
+                case PASSWORD_COMPLEXITY_HIGH:
+                    stringId = R.string.unlock_footer_high_complexity_requested;
+                    break;
+                case PASSWORD_COMPLEXITY_MEDIUM:
+                    stringId = R.string.unlock_footer_medium_complexity_requested;
+                    break;
+                case PASSWORD_COMPLEXITY_LOW:
+                    stringId = R.string.unlock_footer_low_complexity_requested;
+                    break;
+                case PASSWORD_COMPLEXITY_NONE:
+                default:
+                    stringId = R.string.unlock_footer_none_complexity_requested;
+                    break;
+            }
+
+            return getResources().getString(stringId, mCallerAppName);
+        }
+
         private void updatePreferenceText() {
             if (mForFingerprint) {
                 setPreferenceTitle(ScreenLockType.PATTERN,
@@ -624,6 +677,7 @@
             ChooseLockPassword.IntentBuilder builder =
                     new ChooseLockPassword.IntentBuilder(getContext())
                             .setPasswordQuality(quality)
+                            .setRequestedMinComplexity(mRequestedMinComplexity)
                             .setForFingerprint(mForFingerprint)
                             .setForFace(mForFace)
                             .setUserId(mUserId);
diff --git a/src/com/android/settings/password/ChooseLockGenericController.java b/src/com/android/settings/password/ChooseLockGenericController.java
index eb7ff4e..91ca957 100644
--- a/src/com/android/settings/password/ChooseLockGenericController.java
+++ b/src/com/android/settings/password/ChooseLockGenericController.java
@@ -16,7 +16,11 @@
 
 package com.android.settings.password;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
+import android.app.admin.PasswordMetrics;
 import android.content.Context;
 import android.os.UserHandle;
 
@@ -36,6 +40,7 @@
 
     private final Context mContext;
     private final int mUserId;
+    @PasswordComplexity private final int mRequestedMinComplexity;
     private ManagedLockPasswordProvider mManagedPasswordProvider;
     private DevicePolicyManager mDpm;
 
@@ -43,6 +48,19 @@
         this(
                 context,
                 userId,
+                PASSWORD_COMPLEXITY_NONE);
+    }
+
+    /**
+     * @param requestedMinComplexity specifies the min password complexity to be taken into account
+     *                               when determining the available screen lock types
+     */
+    public ChooseLockGenericController(Context context, int userId,
+            @PasswordComplexity int requestedMinComplexity) {
+        this(
+                context,
+                userId,
+                requestedMinComplexity,
                 context.getSystemService(DevicePolicyManager.class),
                 ManagedLockPasswordProvider.get(context, userId));
     }
@@ -51,21 +69,26 @@
     ChooseLockGenericController(
             Context context,
             int userId,
+            @PasswordComplexity int requestedMinComplexity,
             DevicePolicyManager dpm,
             ManagedLockPasswordProvider managedLockPasswordProvider) {
         mContext = context;
         mUserId = userId;
+        mRequestedMinComplexity = requestedMinComplexity;
         mManagedPasswordProvider = managedLockPasswordProvider;
         mDpm = dpm;
     }
 
     /**
-     * @return The higher quality of either the specified {@code quality} or the quality required
-     *         by {@link DevicePolicyManager#getPasswordQuality}.
+     * Returns the highest quality among the specified {@code quality}, the quality required by
+     * {@link DevicePolicyManager#getPasswordQuality}, and the quality required by min password
+     * complexity.
      */
     public int upgradeQuality(int quality) {
-        // Compare min allowed password quality
-        return Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
+        // Compare specified quality and dpm quality
+        int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
+        return Math.max(dpmUpgradedQuality,
+                PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
     }
 
     /**
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 0ae664c..129f9ac 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -16,14 +16,18 @@
 
 package com.android.settings.password;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
 
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.PasswordMetrics;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
@@ -56,6 +60,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
 import com.android.internal.widget.TextViewInputDisabler;
@@ -133,6 +138,11 @@
             return this;
         }
 
+        public IntentBuilder setRequestedMinComplexity(@PasswordComplexity int level) {
+            mIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, level);
+            return this;
+        }
+
         public Intent build() {
             return mIntent;
         }
@@ -190,12 +200,10 @@
         private int mPasswordMinNumeric = 0;
         private int mPasswordMinNonLetter = 0;
         private int mPasswordMinLengthToFulfillAllPolicies = 0;
+        private boolean mPasswordNumSequenceAllowed = true;
+        @PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
         protected int mUserId;
         private byte[] mPasswordHistoryHashFactor;
-        /**
-         * Password requirements that we need to verify.
-         */
-        private int[] mPasswordRequirements;
 
         private LockPatternUtils mLockPatternUtils;
         private SaveAndFinishWorker mSaveAndFinishWorker;
@@ -372,7 +380,13 @@
             mForFingerprint = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
             mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
-            processPasswordRequirements(intent);
+            mRequestedMinComplexity = intent.getIntExtra(
+                    EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+            mRequestedQuality = Math.max(
+                    intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality),
+                    mLockPatternUtils.getRequestedPasswordQuality(mUserId));
+
+            loadDpmPasswordRequirements();
             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
 
             if (intent.getBooleanExtra(
@@ -504,31 +518,6 @@
         }
 
         private void setupPasswordRequirementsView(View view) {
-            final List<Integer> passwordRequirements = new ArrayList<>();
-            if (mPasswordMinUpperCase > 0) {
-                passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD);
-            }
-            if (mPasswordMinLowerCase > 0) {
-                passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD);
-            }
-            if (mPasswordMinLetters > 0) {
-                if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) {
-                    passwordRequirements.add(MIN_LETTER_IN_PASSWORD);
-                }
-            }
-            if (mPasswordMinNumeric > 0) {
-                passwordRequirements.add(MIN_NUMBER_IN_PASSWORD);
-            }
-            if (mPasswordMinSymbols > 0) {
-                passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD);
-            }
-            if (mPasswordMinNonLetter > 0) {
-                if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) {
-                    passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD);
-                }
-            }
-            // Convert list to array.
-            mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray();
             mPasswordRestrictionView = view.findViewById(R.id.password_requirements_view);
             mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
             mPasswordRequirementAdapter = new PasswordRequirementAdapter();
@@ -603,13 +592,12 @@
 
         /**
          * Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
-         *
-         * @param intent the incoming intent
          */
-        private void processPasswordRequirements(Intent intent) {
+        private void loadDpmPasswordRequirements() {
             final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
-            mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
-                    mRequestedQuality), dpmPasswordQuality);
+            if (dpmPasswordQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+                mPasswordNumSequenceAllowed = false;
+            }
             mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
                     mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
             mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality);
@@ -620,7 +608,7 @@
             mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId);
             mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId);
 
-            // Modify the value based on dpm policy.
+            // Modify the value based on dpm policy
             switch (dpmPasswordQuality) {
                 case PASSWORD_QUALITY_ALPHABETIC:
                     if (mPasswordMinLetters == 0) {
@@ -646,19 +634,88 @@
                     mPasswordMinSymbols = 0;
                     mPasswordMinNonLetter = 0;
             }
+
             mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
         }
 
         /**
+         * Merges the dpm requirements and the min complexity requirements.
+         *
+         * <p>Since there are more than one set of metrics to meet the min complexity requirement,
+         * and we are not hard-coding any one of them to be the requirements the user must fulfil,
+         * we are taking what the user has already entered into account when compiling the list of
+         * requirements from min complexity. Then we merge this list with the DPM requirements, and
+         * present the merged set as validation results to the user on the UI.
+         *
+         * <p>For example, suppose min complexity requires either ALPHABETIC(8+), or
+         * ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
+         * would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
+         * an alphanumeric password so we would update the min complexity required min length to 6.
+         * This might result in a little confusion for the user but the UI does not support showing
+         * multiple sets of requirements / validation results as options to users, this is the best
+         * we can do now.
+         */
+        private void mergeMinComplexityAndDpmRequirements(int userEnteredPasswordQuality) {
+            if (mRequestedMinComplexity == PASSWORD_COMPLEXITY_NONE) {
+                // dpm requirements are dominant if min complexity is none
+                return;
+            }
+
+            // reset dpm requirements
+            loadDpmPasswordRequirements();
+
+            PasswordMetrics minMetrics = PasswordMetrics.getMinimumMetrics(
+                    mRequestedMinComplexity, userEnteredPasswordQuality, mRequestedQuality,
+                    requiresNumeric(), requiresLettersOrSymbols());
+            mPasswordNumSequenceAllowed = mPasswordNumSequenceAllowed
+                    && minMetrics.quality != PASSWORD_QUALITY_NUMERIC_COMPLEX;
+            mPasswordMinLength = Math.max(mPasswordMinLength, minMetrics.length);
+            mPasswordMinLetters = Math.max(mPasswordMinLetters, minMetrics.letters);
+            mPasswordMinUpperCase = Math.max(mPasswordMinUpperCase, minMetrics.upperCase);
+            mPasswordMinLowerCase = Math.max(mPasswordMinLowerCase, minMetrics.lowerCase);
+            mPasswordMinNumeric = Math.max(mPasswordMinNumeric, minMetrics.numeric);
+            mPasswordMinSymbols = Math.max(mPasswordMinSymbols, minMetrics.symbols);
+            mPasswordMinNonLetter = Math.max(mPasswordMinNonLetter, minMetrics.nonLetter);
+
+            if (minMetrics.quality == PASSWORD_QUALITY_ALPHABETIC) {
+                if (!requiresLettersOrSymbols()) {
+                    mPasswordMinLetters = 1;
+                }
+            }
+            if (minMetrics.quality == PASSWORD_QUALITY_ALPHANUMERIC) {
+                if (!requiresLettersOrSymbols()) {
+                    mPasswordMinLetters = 1;
+                }
+                if (!requiresNumeric()) {
+                    mPasswordMinNumeric = 1;
+                }
+            }
+
+            mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
+        }
+
+        private boolean requiresLettersOrSymbols() {
+            // This is the condition for the password to be considered ALPHABETIC according to
+            // PasswordMetrics.computeForPassword()
+            return mPasswordMinLetters + mPasswordMinUpperCase
+                    + mPasswordMinLowerCase + mPasswordMinSymbols + mPasswordMinNonLetter > 0;
+        }
+
+        private boolean requiresNumeric() {
+            return mPasswordMinNumeric > 0;
+        }
+
+        /**
          * Validates PIN/Password and returns the validation result.
          *
          * @param password the raw password the user typed in
          * @return the validation result.
          */
-        private int validatePassword(String password) {
+        @VisibleForTesting
+        int validatePassword(String password) {
             int errorCode = NO_ERROR;
             final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
-
+            mergeMinComplexityAndDpmRequirements(metrics.quality);
 
             if (password.length() < mPasswordMinLength) {
                 if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
@@ -668,14 +725,25 @@
                 errorCode |= TOO_LONG;
             } else {
                 // The length requirements are fulfilled.
-                final int dpmQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
-                if (dpmQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX &&
-                        metrics.numeric == password.length()) {
+                if (!mPasswordNumSequenceAllowed
+                        && !requiresLettersOrSymbols()
+                        && metrics.numeric == password.length()) {
                     // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
-                    // if DevicePolicyManager requires a complex numeric password. There can be
-                    // two cases in the UI: 1. User chooses to enroll a PIN, 2. User chooses to
-                    // enroll a password but enters a numeric-only pin. We should carry out the
-                    // sequence check in both cases.
+                    // if DevicePolicyManager or min password complexity requires a complex numeric
+                    // password. There can be two cases in the UI: 1. User chooses to enroll a
+                    // PIN, 2. User chooses to enroll a password but enters a numeric-only pin. We
+                    // should carry out the sequence check in both cases.
+                    //
+                    // Conditions for the !requiresLettersOrSymbols() to be necessary:
+                    // - DPM requires NUMERIC_COMPLEX
+                    // - min complexity not NONE, user picks PASSWORD type so ALPHABETIC or
+                    // ALPHANUMERIC is required
+                    // Imagine user has entered "12345678", if we don't skip the sequence check, the
+                    // validation result would show both "requires a letter" and "sequence not
+                    // allowed", while the only requirement the user needs to know is "requires a
+                    // letter" because once the user has fulfilled the alphabetic requirement, the
+                    // password would not be containing only digits so this check would not be
+                    // performed anyway.
                     final int sequence = PasswordMetrics.maxLengthSequence(password);
                     if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
                         errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
@@ -706,43 +774,24 @@
                 }
             }
 
-            // Check the requirements one by one.
-            for (int i = 0; i < mPasswordRequirements.length; i++) {
-                int passwordRestriction = mPasswordRequirements[i];
-                switch (passwordRestriction) {
-                    case MIN_LETTER_IN_PASSWORD:
-                        if (metrics.letters < mPasswordMinLetters) {
-                            errorCode |= NOT_ENOUGH_LETTER;
-                        }
-                        break;
-                    case MIN_UPPER_LETTERS_IN_PASSWORD:
-                        if (metrics.upperCase < mPasswordMinUpperCase) {
-                            errorCode |= NOT_ENOUGH_UPPER_CASE;
-                        }
-                        break;
-                    case MIN_LOWER_LETTERS_IN_PASSWORD:
-                        if (metrics.lowerCase < mPasswordMinLowerCase) {
-                            errorCode |= NOT_ENOUGH_LOWER_CASE;
-                        }
-                        break;
-                    case MIN_SYMBOLS_IN_PASSWORD:
-                        if (metrics.symbols < mPasswordMinSymbols) {
-                            errorCode |= NOT_ENOUGH_SYMBOLS;
-                        }
-                        break;
-                    case MIN_NUMBER_IN_PASSWORD:
-                        if (metrics.numeric < mPasswordMinNumeric) {
-                            errorCode |= NOT_ENOUGH_DIGITS;
-                        }
-                        break;
-                    case MIN_NON_LETTER_IN_PASSWORD:
-                        if (metrics.nonLetter < mPasswordMinNonLetter) {
-                            errorCode |= NOT_ENOUGH_NON_LETTER;
-                        }
-                        break;
-                }
+            if (metrics.letters < mPasswordMinLetters) {
+                errorCode |= NOT_ENOUGH_LETTER;
             }
-
+            if (metrics.upperCase < mPasswordMinUpperCase) {
+                errorCode |= NOT_ENOUGH_UPPER_CASE;
+            }
+            if (metrics.lowerCase < mPasswordMinLowerCase) {
+                errorCode |= NOT_ENOUGH_LOWER_CASE;
+            }
+            if (metrics.symbols < mPasswordMinSymbols) {
+                errorCode |= NOT_ENOUGH_SYMBOLS;
+            }
+            if (metrics.numeric < mPasswordMinNumeric) {
+                errorCode |= NOT_ENOUGH_DIGITS;
+            }
+            if (metrics.nonLetter < mPasswordMinNonLetter) {
+                errorCode |= NOT_ENOUGH_NON_LETTER;
+            }
             return errorCode;
         }
 
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 8d0fa60..32e8eaf 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -46,6 +46,18 @@
     public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
 
     /**
+     * Intent extra for passing the requested min password complexity to later steps in the set new
+     * screen lock flow.
+     */
+    public static final String EXTRA_KEY_REQUESTED_MIN_COMPLEXITY = "requested_min_complexity";
+
+    /**
+     * Intent extra for passing the label of the calling app to later steps in the set new screen
+     * lock flow.
+     */
+    public static final String EXTRA_KEY_CALLER_APP_NAME = "caller_app_name";
+
+    /**
      * When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag
      * controls if we relax the enforcement of
      * {@link Utils#enforceSameOwner(android.content.Context, int)}.
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 5eb1f32..0d9b21d 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -136,6 +136,10 @@
         Intent intent = getIntent();
         mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
         mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
+
+        final boolean requireConfirmation =
+                !intent.getBooleanExtra(KeyguardManager.EXTRA_USE_IMPLICIT, true);
+
         String alternateButton = intent.getStringExtra(
                 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
         boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
@@ -170,7 +174,7 @@
                 && !lockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
             mCredentialMode = CREDENTIAL_MANAGED;
             if (isBiometricAllowed(effectiveUserId)) {
-                showBiometricPrompt();
+                showBiometricPrompt(requireConfirmation);
                 launchedBiometric = true;
             } else {
                 showConfirmCredentials();
@@ -181,7 +185,7 @@
             if (isBiometricAllowed(effectiveUserId)) {
                 // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
                 // onAuthenticationError and do the right thing automatically.
-                showBiometricPrompt();
+                showBiometricPrompt(requireConfirmation);
                 launchedBiometric = true;
             } else {
                 showConfirmCredentials();
@@ -242,7 +246,7 @@
                 && !isBiometricDisabledByAdmin(effectiveUserId);
     }
 
-    private void showBiometricPrompt() {
+    private void showBiometricPrompt(boolean requireConfirmation) {
         mBiometricManager.setActiveUser(mUserId);
 
         mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
@@ -255,6 +259,7 @@
                     .setSubtitle(mDetails)
                     .setNegativeButtonText(getResources()
                             .getString(R.string.confirm_device_credential_use_alternate_method))
+                    .setRequireConfirmation(requireConfirmation)
                     .build();
             mBiometricFragment = BiometricFragment.newInstance(info);
             newFragment = true;
diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java
new file mode 100644
index 0000000..5f118cf
--- /dev/null
+++ b/src/com/android/settings/password/PasswordUtils.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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.password;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.settings.Utils;
+
+public final class PasswordUtils extends com.android.settingslib.Utils {
+
+    private static final String TAG = "Settings";
+
+    private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
+
+    /**
+     * Returns whether the uid which the activity with {@code activityToken} is launched from has
+     * been granted the {@code permission}.
+     */
+    public static boolean isCallingAppPermitted(Context context, IBinder activityToken,
+            String permission) {
+        try {
+            return context.checkPermission(permission, /* pid= */ -1,
+                    ActivityManager.getService().getLaunchedFromUid(activityToken))
+                    == PackageManager.PERMISSION_GRANTED;
+        } catch (RemoteException e) {
+            Log.v(TAG, "Could not talk to activity manager.", e);
+            return false;
+        }
+    }
+
+    /**
+     * Returns the label of the package which the activity with {@code activityToken} is launched
+     * from or {@code null} if it is launched from the settings app itself.
+     */
+    @Nullable
+    public static CharSequence getCallingAppLabel(Context context, IBinder activityToken) {
+        String pkg = getCallingAppPackageName(activityToken);
+        if (pkg == null || pkg.equals(SETTINGS_PACKAGE_NAME)) {
+            return null;
+        }
+
+        return Utils.getApplicationLabel(context, pkg);
+    }
+
+    /**
+     * Returns the package name which the activity with {@code activityToken} is launched from.
+     */
+    @Nullable
+    private static String getCallingAppPackageName(IBinder activityToken) {
+        String pkg = null;
+        try {
+            pkg = ActivityManager.getService().getLaunchedFromPackage(activityToken);
+        } catch (RemoteException e) {
+            Log.v(TAG, "Could not talk to activity manager.", e);
+        }
+        return pkg;
+    }
+
+    /** Crashes the calling application and provides it with {@code message}. */
+    public static void crashCallingApplication(IBinder activityToken, String message) {
+        IActivityManager am = ActivityManager.getService();
+        try {
+            int uid = am.getLaunchedFromUid(activityToken);
+            int userId = UserHandle.getUserId(uid);
+            am.crashApplication(
+                    uid,
+                    /* initialPid= */ -1,
+                    getCallingAppPackageName(activityToken),
+                    userId,
+                    message);
+        } catch (RemoteException e) {
+            Log.v(TAG, "Could not talk to activity manager.", e);
+        }
+    }
+}
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index 99f67cb..8ea8514 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -16,13 +16,22 @@
 
 package com.android.settings.password;
 
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
 
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
+import android.app.admin.PasswordMetrics;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.util.Log;
 
 import com.android.settings.Utils;
@@ -37,6 +46,21 @@
     private String mNewPasswordAction;
     private SetNewPasswordController mSetNewPasswordController;
 
+    /**
+     * From intent extra {@link DevicePolicyManager#EXTRA_PASSWORD_COMPLEXITY}.
+     *
+     * <p>This is used only if caller has the required permission and activity is launched by
+     * {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD}.
+     */
+    private @PasswordComplexity int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
+
+    /**
+     * Label of the app which launches this activity.
+     *
+     * <p>Value would be {@code null} if launched from settings app.
+     */
+    private String mCallerAppName = null;
+
     @Override
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
@@ -48,6 +72,25 @@
             finish();
             return;
         }
+
+        IBinder activityToken = getActivityToken();
+        mCallerAppName = (String) PasswordUtils.getCallingAppLabel(this, activityToken);
+        if (ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
+                && getIntent().hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
+            boolean hasPermission = PasswordUtils.isCallingAppPermitted(
+                    this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+            if (hasPermission) {
+                mRequestedMinComplexity = PasswordMetrics.sanitizeComplexityLevel(getIntent()
+                        .getIntExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE));
+            } else {
+                PasswordUtils.crashCallingApplication(activityToken,
+                        "Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
+                                + " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
+                finish();
+                return;
+            }
+        }
+
         mSetNewPasswordController = SetNewPasswordController.create(
                 this, this, getIntent(), getActivityToken());
         mSetNewPasswordController.dispatchSetNewPasswordIntent();
@@ -60,6 +103,12 @@
                 : new Intent(this, ChooseLockGeneric.class);
         intent.setAction(mNewPasswordAction);
         intent.putExtras(chooseLockFingerprintExtras);
+        if (mCallerAppName != null) {
+            intent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
+        }
+        if (mRequestedMinComplexity != PASSWORD_COMPLEXITY_NONE) {
+            intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, mRequestedMinComplexity);
+        }
         startActivity(intent);
         finish();
     }
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index a0f8bae..33c3edb 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -16,11 +16,17 @@
 
 package com.android.settings.password;
 
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -48,6 +54,7 @@
  * Other changes should be done to ChooseLockGeneric class instead and let this class inherit
  * those changes.
  */
+// TODO(b/123225425): Restrict SetupChooseLockGeneric to be accessible by SUW only
 public class SetupChooseLockGeneric extends ChooseLockGeneric {
 
     private static final String KEY_UNLOCK_SET_DO_LATER = "unlock_set_do_later";
@@ -71,6 +78,20 @@
     @Override
     protected void onCreate(Bundle savedInstance) {
         super.onCreate(savedInstance);
+
+        if(getIntent().hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)) {
+            IBinder activityToken = getActivityToken();
+            boolean hasPermission = PasswordUtils.isCallingAppPermitted(
+                    this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+            if (!hasPermission) {
+                PasswordUtils.crashCallingApplication(activityToken,
+                        "Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
+                                + " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
+                finish();
+                return;
+            }
+        }
+
         LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
         layout.setFitsSystemWindows(false);
     }
diff --git a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
index 3fac672..e43140f 100644
--- a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
+++ b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java
@@ -76,6 +76,7 @@
                 .setEmptyText(R.string.permission_bar_chart_empty_text)
                 .setDetailsOnClickListener((View v) -> {
                     final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
+                    intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
                     mContext.startActivity(intent);
                 })
                 .build();
@@ -102,7 +103,7 @@
 
     private void retrievePermissionUsageData() {
         mContext.getSystemService(PermissionControllerManager.class).getPermissionUsages(
-                false /* countSystem */, (int) DAYS.toSeconds(1),
+                false /* countSystem */, (int) DAYS.toMillis(1),
                 mContext.getMainExecutor() /* executor */, this /* callback */);
     }
 
@@ -111,6 +112,9 @@
             return null;
         }
 
+        // STOPSHIP: Ignore the STORAGE group since it's going away.
+        usageInfos.removeIf(usage -> usage.getName().equals("android.permission-group.STORAGE"));
+
         final BarViewInfo[] barViewInfos = new BarViewInfo[
                 Math.min(BarChartPreference.MAXIMUM_BAR_VIEWS, usageInfos.size())];
 
diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java
index 284fd23..6df45ba 100644
--- a/src/com/android/settings/slices/SliceBackgroundWorker.java
+++ b/src/com/android/settings/slices/SliceBackgroundWorker.java
@@ -59,10 +59,6 @@
         mUri = uri;
     }
 
-    protected Uri getUri() {
-        return mUri;
-    }
-
     /**
      * Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link Uri}
      * if exists
@@ -148,7 +144,14 @@
 
         if (needNotify) {
             mCachedResults = results;
-            mContext.getContentResolver().notifyChange(mUri, null);
+            notifySliceChange();
         }
     }
-}
+
+    /**
+     * Notify that data was updated and attempt to sync changes to the Slice.
+     */
+    protected void notifySliceChange() {
+        mContext.getContentResolver().notifyChange(mUri, null);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/slices/SlicePreferenceController.java b/src/com/android/settings/slices/SlicePreferenceController.java
index d7fcc18..89294c7 100644
--- a/src/com/android/settings/slices/SlicePreferenceController.java
+++ b/src/com/android/settings/slices/SlicePreferenceController.java
@@ -82,6 +82,8 @@
 
     @Override
     public void onChanged(Slice slice) {
-        mSlicePreference.onSliceUpdated(slice);
+        if (slice != null) {
+            mSlicePreference.onSliceUpdated(slice);
+        }
     }
 }
diff --git a/src/com/android/settings/vpn2/AppDialogFragment.java b/src/com/android/settings/vpn2/AppDialogFragment.java
index 0d0022f..2f9cd7a 100644
--- a/src/com/android/settings/vpn2/AppDialogFragment.java
+++ b/src/com/android/settings/vpn2/AppDialogFragment.java
@@ -164,7 +164,8 @@
         final int userId = getUserId();
         try {
             if (mPackageInfo.packageName.equals(VpnUtils.getConnectedPackage(mService, userId))) {
-                mService.setAlwaysOnVpnPackage(userId, null, /* lockdownEnabled */ false);
+                mService.setAlwaysOnVpnPackage(userId, null, /* lockdownEnabled */ false,
+                        /* lockdownWhitelist */ null);
                 mService.prepareVpn(mPackageInfo.packageName, VpnConfig.LEGACY_VPN, userId);
             }
         } catch (RemoteException e) {
diff --git a/src/com/android/settings/vpn2/AppManagementFragment.java b/src/com/android/settings/vpn2/AppManagementFragment.java
index 1571216..5f46446 100644
--- a/src/com/android/settings/vpn2/AppManagementFragment.java
+++ b/src/com/android/settings/vpn2/AppManagementFragment.java
@@ -225,7 +225,7 @@
 
     private boolean setAlwaysOnVpn(boolean isEnabled, boolean isLockdown) {
         return mConnectivityManager.setAlwaysOnVpnPackageForUser(mUserId,
-                isEnabled ? mPackageName : null, isLockdown);
+                isEnabled ? mPackageName : null, isLockdown, /* lockdownWhitelist */ null);
     }
 
     private void updateUI() {
diff --git a/src/com/android/settings/vpn2/ConfigDialogFragment.java b/src/com/android/settings/vpn2/ConfigDialogFragment.java
index ec927ae..01b20f0 100644
--- a/src/com/android/settings/vpn2/ConfigDialogFragment.java
+++ b/src/com/android/settings/vpn2/ConfigDialogFragment.java
@@ -199,7 +199,7 @@
 
             final ConnectivityManager conn = ConnectivityManager.from(mContext);
             conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null,
-                    /* lockdownEnabled */ false);
+                    /* lockdownEnabled */ false, /* lockdownWhitelist */ null);
             VpnUtils.setLockdownVpn(mContext, profile.key);
         } else {
             // update only if lockdown vpn has been changed
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 1c79c1d..88fa8b2 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -292,11 +292,12 @@
 
         @Override
         public void onWifiStateChanged(int state) {
-            mContext.getContentResolver().notifyChange(getUri(), null);
+            notifySliceChange();
         }
 
         @Override
         public void onConnectedChanged() {
+            notifySliceChange();
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java
index bf4dec6..c6a48a8 100644
--- a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarPreferenceControllerTest.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.ArraySet;
 
 import com.android.settingslib.RestrictedSwitchPreference;
 
@@ -45,6 +46,9 @@
 import org.robolectric.Shadows;
 import org.robolectric.shadows.ShadowDevicePolicyManager;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 @RunWith(RobolectricTestRunner.class)
 public class CrossProfileCalendarPreferenceControllerTest {
 
@@ -123,7 +127,17 @@
     @Test
     public void updateState_somePackagesAllowed_preferenceShouldNotBeDisabled() throws Exception {
         dpm.setProfileOwner(TEST_COMPONENT_NAME);
-        dpm.addCrossProfileCalendarPackage(TEST_COMPONENT_NAME, TEST_PACKAGE_NAME);
+        dpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME,
+                Collections.singleton(TEST_PACKAGE_NAME));
+
+        mController.updateState(mPreference);
+        verify(mPreference).setDisabledByAdmin(null);
+    }
+
+    @Test
+    public void updateState_allPackagesAllowed_preferenceShouldNotBeDisabled() throws Exception {
+        dpm.setProfileOwner(TEST_COMPONENT_NAME);
+        dpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME, null);
 
         mController.updateState(mPreference);
         verify(mPreference).setDisabledByAdmin(null);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
index 1da97f5..b6bbe8a 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiverTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.settings.connecteddevice.usb;
 
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
@@ -101,7 +103,8 @@
         final Intent intent = new Intent();
         intent.setAction(UsbManager.ACTION_USB_PORT_CHANGED);
         final UsbPortStatus status = new UsbPortStatus(0, POWER_ROLE_SINK,
-                DATA_ROLE_DEVICE, 0);
+                DATA_ROLE_DEVICE, 0, CONTAMINANT_PROTECTION_NONE,
+                CONTAMINANT_DETECTION_NOT_SUPPORTED);
         intent.putExtra(UsbManager.EXTRA_PORT_STATUS, status);
 
         mReceiver.onReceive(mContext, intent);
diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
index 3b33558..8cba1de 100644
--- a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java
@@ -36,6 +36,7 @@
 import com.android.settingslib.widget.LayoutPreference;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -77,6 +78,7 @@
 
     /** Verifies the title text, details text are correct, and the click listener is set. */
     @Test
+    @Ignore
     public void updateState_whenAppListIsEmpty_shouldDisplayTitleTextAndDetailsText() {
         doReturn(new ArrayList<>()).when(mRecentLocationApps).getAppListSorted();
         mController.displayPreference(mScreen);
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
index cbc5765..2b7bdeb 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericControllerTest.java
@@ -16,15 +16,22 @@
 
 package com.android.settings.password;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.content.ComponentName;
 
 import com.android.settings.R;
@@ -58,11 +65,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mController = new ChooseLockGenericController(
-                application,
-                0 /* userId */,
-                mDevicePolicyManager,
-                mManagedLockPasswordProvider);
+        mController = createController(PASSWORD_COMPLEXITY_NONE);
         SettingsShadowResources.overrideResource(R.bool.config_hide_none_security_option, false);
         SettingsShadowResources.overrideResource(R.bool.config_hide_swipe_security_option, false);
     }
@@ -225,4 +228,44 @@
         assertThat(upgradedQuality).named("upgradedQuality")
                 .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
     }
+
+    @Test
+    public void upgradeQuality_complexityHigh_minQualityNumericComplex() {
+        when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
+                .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_HIGH);
+
+        assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
+                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
+    }
+
+    @Test
+    public void upgradeQuality_complexityMedium_minQualityNumericComplex() {
+        when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
+                .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_MEDIUM);
+
+        assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
+                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX);
+    }
+
+    @Test
+    public void upgradeQuality_complexityLow_minQualitySomething() {
+        when(mDevicePolicyManager.getPasswordQuality(nullable(ComponentName.class), anyInt()))
+                .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+        ChooseLockGenericController controller = createController(PASSWORD_COMPLEXITY_LOW);
+
+        assertThat(controller.upgradeQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED))
+                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+    }
+
+    private ChooseLockGenericController createController(
+            @PasswordComplexity int minPasswordComplexity) {
+        return new ChooseLockGenericController(
+                application,
+                0 /* userId */,
+                minPasswordComplexity,
+                mDevicePolicyManager,
+                mManagedLockPasswordProvider);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
index e324214..a1db12c 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
@@ -16,6 +16,15 @@
 
 package com.android.settings.password;
 
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.robolectric.RuntimeEnvironment.application;
@@ -27,8 +36,10 @@
 import android.provider.Settings.Global;
 
 import androidx.annotation.Nullable;
+import androidx.preference.Preference;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
 import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
 import com.android.settings.search.SearchFeatureProvider;
@@ -36,6 +47,7 @@
 import com.android.settings.testutils.shadow.ShadowStorageManager;
 import com.android.settings.testutils.shadow.ShadowUserManager;
 import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.widget.FooterPreference;
 
 import org.junit.After;
 import org.junit.Before;
@@ -113,6 +125,70 @@
     }
 
     @Test
+    public void updatePreferencesOrFinish_footerPreferenceAddedHighComplexityText() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        Intent intent = new Intent()
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        initActivity(intent);
+        CharSequence expectedTitle =
+              mActivity.getString(R.string.unlock_footer_high_complexity_requested, "app name");
+
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+        FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+        assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+    }
+
+    @Test
+    public void updatePreferencesOrFinish_footerPreferenceAddedMediumComplexityText() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        Intent intent = new Intent()
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_MEDIUM);
+        initActivity(intent);
+        CharSequence expectedTitle =
+                mActivity.getString(R.string.unlock_footer_medium_complexity_requested, "app name");
+
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+        FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+        assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+    }
+
+    @Test
+    public void updatePreferencesOrFinish_footerPreferenceAddedLowComplexityText() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        Intent intent = new Intent()
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
+        initActivity(intent);
+        CharSequence expectedTitle =
+                mActivity.getString(R.string.unlock_footer_low_complexity_requested, "app name");
+
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+        FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+        assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+    }
+
+    @Test
+    public void updatePreferencesOrFinish_footerPreferenceAddedNoneComplexityText() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        Intent intent = new Intent()
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+        initActivity(intent);
+        CharSequence expectedTitle =
+                mActivity.getString(R.string.unlock_footer_none_complexity_requested, "app name");
+
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+        FooterPreference footer = mFragment.findPreference(FooterPreference.KEY_FOOTER);
+
+        assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+    }
+
+    @Test
     public void onActivityResult_requestcode0_shouldNotFinish() {
         initActivity(null);
 
@@ -165,6 +241,48 @@
         assertThat(mActivity.isFinishing()).isTrue();
     }
 
+    @Test
+    public void onPreferenceTreeClick_fingerprintPassesMinComplexityInfoOntoNextActivity() {
+        Intent intent = new Intent(ACTION_SET_NEW_PASSWORD)
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH)
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name");
+        initActivity(intent);
+
+        Preference fingerprintPref = new Preference(application);
+        fingerprintPref.setKey("unlock_skip_fingerprint");
+        boolean result = mFragment.onPreferenceTreeClick(fingerprintPref);
+
+        assertThat(result).isTrue();
+        Intent actualIntent = shadowOf(mActivity).getNextStartedActivityForResult().intent;
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+        assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+                .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+        assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME))
+                .isEqualTo("app name");
+    }
+
+    @Test
+    public void onPreferenceTreeClick_facePassesMinComplexityInfoOntoNextActivity() {
+        Intent intent = new Intent(ACTION_SET_NEW_PASSWORD)
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH)
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name");
+        initActivity(intent);
+
+        Preference facePref = new Preference(application);
+        facePref.setKey("unlock_skip_face");
+        boolean result = mFragment.onPreferenceTreeClick(facePref);
+
+        assertThat(result).isTrue();
+        Intent actualIntent = shadowOf(mActivity).getNextStartedActivityForResult().intent;
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+        assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+                .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+        assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME))
+                .isEqualTo("app name");
+    }
+
     private void initActivity(@Nullable Intent intent) {
         if (intent == null) {
             intent = new Intent();
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 367cb4c..404d205 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -16,19 +16,37 @@
 
 package com.android.settings.password;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
+import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.content.Intent;
 import android.os.UserHandle;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment;
 import com.android.settings.password.ChooseLockPassword.IntentBuilder;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
 import com.google.android.setupdesign.GlifLayout;
@@ -44,13 +62,21 @@
 import org.robolectric.shadows.ShadowDrawable;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {SettingsShadowResources.class, ShadowUtils.class})
+@Config(shadows = {
+        SettingsShadowResources.class,
+        ShadowUtils.class,
+        ShadowDevicePolicyManager.class,
+})
 public class ChooseLockPasswordTest {
 
+    private ShadowDevicePolicyManager mShadowDpm;
+
     @Before
     public void setUp() {
         SettingsShadowResources.overrideResource(
                 com.android.internal.R.string.config_headlineFontFamily, "");
+        mShadowDpm = ShadowDevicePolicyManager.getShadow();
+        mShadowDpm.setPasswordMaximumLength(16);
     }
 
     @After
@@ -72,7 +98,7 @@
         assertThat(intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD))
                 .named("EXTRA_KEY_PASSWORD")
                 .isEqualTo("password");
-        assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0))
+        assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0))
                 .named("PASSWORD_TYPE_KEY")
                 .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
         assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0))
@@ -84,7 +110,7 @@
     public void intentBuilder_setChallenge_shouldAddExtras() {
         Intent intent = new IntentBuilder(application)
                 .setChallenge(12345L)
-                .setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC)
+                .setPasswordQuality(PASSWORD_QUALITY_ALPHANUMERIC)
                 .setUserId(123)
                 .build();
 
@@ -94,15 +120,214 @@
         assertThat(intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0L))
                 .named("EXTRA_KEY_CHALLENGE")
                 .isEqualTo(12345L);
-        assertThat(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 0))
+        assertThat(intent.getIntExtra(PASSWORD_TYPE_KEY, 0))
                 .named("PASSWORD_TYPE_KEY")
-                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+                .isEqualTo(PASSWORD_QUALITY_ALPHANUMERIC);
         assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0))
                 .named("EXTRA_USER_ID")
                 .isEqualTo(123);
     }
 
     @Test
+    public void intentBuilder_setMinComplexityMedium_hasMinComplexityExtraMedium() {
+        Intent intent = new IntentBuilder(application)
+                .setRequestedMinComplexity(PASSWORD_COMPLEXITY_MEDIUM)
+                .build();
+
+        assertThat(intent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+        assertThat(intent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+                .isEqualTo(PASSWORD_COMPLEXITY_MEDIUM);
+    }
+
+    @Test
+    public void intentBuilder_setMinComplexityNotCalled() {
+        Intent intent = new IntentBuilder(application).build();
+
+        assertThat(intent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_noMinPasswordComplexity() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_ALPHABETIC);
+        mShadowDpm.setPasswordMinimumLength(10);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "",
+                "Must contain at least 1 letter",
+                "Must be at least 10 characters");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_minPasswordComplexityStricter_pin() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_SOMETHING);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ "",
+                "PIN must be at least 8 digits");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_minPasswordComplexityStricter_password() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_SOMETHING);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "",
+                "Must contain at least 1 letter",
+                "Must be at least 4 characters");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_dpmRestrictionsStricter_password() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_ALPHANUMERIC);
+        mShadowDpm.setPasswordMinimumLength(9);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "",
+                "Must contain at least 1 letter",
+                "Must contain at least 1 numerical digit",
+                "Must be at least 9 characters");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_dpmLengthLonger_pin() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC);
+        mShadowDpm.setPasswordMinimumLength(11);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_MEDIUM,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ "",
+                "PIN must be at least 11 digits");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_dpmQualityComplex() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_COMPLEX);
+        mShadowDpm.setPasswordMinimumSymbols(2);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "",
+                "Must contain at least 2 special symbols",
+                "Must be at least 6 characters");
+    }
+
+    @Test
+    @Config(shadows = ShadowLockPatternUtils.class)
+    public void processAndValidatePasswordRequirements_numericComplexNoMinComplexity_pinRequested() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ "12345678",
+                "Ascending, descending, or repeated sequence of digits isn't allowed");
+    }
+
+    @Test
+    @Config(shadows = ShadowLockPatternUtils.class)
+    public void processAndValidatePasswordRequirements_numericComplexNoMinComplexity_passwordRequested() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "12345678",
+                "Ascending, descending, or repeated sequence of digits isn't allowed");
+    }
+
+    @Test
+    @Config(shadows = ShadowLockPatternUtils.class)
+    public void processAndValidatePasswordRequirements_numericComplexHighComplexity_pinRequested() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ "12345678",
+                "Ascending, descending, or repeated sequence of digits isn't allowed");
+    }
+
+    @Test
+    @Config(shadows = ShadowLockPatternUtils.class)
+    public void processAndValidatePasswordRequirements_numericHighComplexity_pinRequested() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ "12345678",
+                "Ascending, descending, or repeated sequence of digits isn't allowed");
+    }
+
+    @Test
+    @Config(shadows = ShadowLockPatternUtils.class)
+    public void processAndValidatePasswordRequirements_numericComplexLowComplexity_passwordRequested() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_LOW,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "12345678",
+                "Must contain at least 1 letter");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_empty() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "",
+                "Must contain at least 1 letter",
+                "Must be at least 6 characters");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_numeric() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "1",
+                "Must contain at least 1 letter",
+                "Must be at least 6 characters");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_alphabetic() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "b",
+                "Must be at least 6 characters");
+    }
+
+    @Test
+    public void processAndValidatePasswordRequirements_requirementsUpdateAccordingToMinComplexityAndUserInput_alphanumeric() {
+        mShadowDpm.setPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC,
+                /* userEnteredPassword= */ "b1",
+                "Must be at least 6 characters");
+    }
+
+    @Test
     public void assertThat_chooseLockIconChanged_WhenFingerprintExtraSet() {
         ShadowDrawable drawable = setActivityAndGetIconDrawable(true);
         assertThat(drawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_fingerprint_header);
@@ -132,4 +357,18 @@
         ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
         return Shadows.shadowOf(((GlifLayout) fragment.getView()).getIcon());
     }
+
+    private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
+            int passwordType, String userEnteredPassword, String... expectedValidationResult) {
+        Intent intent = new Intent();
+        intent.putExtra(CONFIRM_CREDENTIALS, false);
+        intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
+        intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
+        ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
+        ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
+        int validateResult = fragment.validatePassword(userEnteredPassword);
+        String[] messages = fragment.convertErrorCodeToMessages(validateResult);
+
+        assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java b/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java
new file mode 100644
index 0000000..845d346
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/PasswordUtilsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 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.password;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static com.android.settings.password.PasswordUtils.getCallingAppLabel;
+import static com.android.settings.password.PasswordUtils.isCallingAppPermitted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.settings.testutils.shadow.ShadowActivityManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowActivityManager.class})
+public class PasswordUtilsTest {
+
+    private static final String PACKAGE_NAME = "com.android.app";
+    private static final String PERMISSION = "com.testing.permission";
+    private static final int UID = 1234;
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private IActivityManager mActivityService;
+    @Mock
+    private IBinder mActivityToken;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        ShadowActivityManager.setService(mActivityService);
+    }
+
+    @Test
+    public void getCallingAppLabel_activityServiceThrowsRemoteException_returnsNull()
+            throws Exception {
+        when(mActivityService.getLaunchedFromPackage(mActivityToken))
+                .thenThrow(new RemoteException());
+
+        assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
+    }
+
+    @Test
+    public void getCallingAppLabel_activityServiceReturnsSettingsApp_returnsNull()
+            throws Exception {
+        when(mActivityService.getLaunchedFromPackage(mActivityToken))
+                .thenReturn("com.android.settings");
+
+        assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
+    }
+
+    @Test
+    public void getCallingAppLabel_packageManagerThrowsNameNotFound_returnsNull() throws Exception {
+        when(mActivityService.getLaunchedFromPackage(mActivityToken))
+                .thenReturn(PACKAGE_NAME);
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        assertThat(getCallingAppLabel(mContext, mActivityToken)).isNull();
+    }
+
+    @Test
+    public void getCallingAppLabel_returnsLabel() throws Exception {
+        when(mActivityService.getLaunchedFromPackage(mActivityToken))
+                .thenReturn(PACKAGE_NAME);
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(mApplicationInfo);
+        when(mApplicationInfo.loadLabel(mPackageManager)).thenReturn("label");
+
+        assertThat(getCallingAppLabel(mContext, mActivityToken)).isEqualTo("label");
+    }
+
+    @Test
+    public void isCallingAppPermitted_permissionGranted_returnsTrue() throws Exception {
+        when(mActivityService.getLaunchedFromUid(mActivityToken)).thenReturn(UID);
+        when(mContext.checkPermission(PERMISSION, -1, UID)).thenReturn(PERMISSION_GRANTED);
+
+        assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isTrue();
+    }
+
+    @Test
+    public void isCallingAppPermitted_permissionDenied_returnsFalse() throws Exception {
+        when(mActivityService.getLaunchedFromUid(mActivityToken)).thenReturn(UID);
+        when(mContext.checkPermission(PERMISSION, -1, UID)).thenReturn(PERMISSION_DENIED);
+
+        assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isFalse();
+    }
+
+    @Test
+    public void isCallingAppPermitted_throwsRemoteException_returnsFalse() throws Exception {
+        when(mActivityService.getLaunchedFromUid(mActivityToken)).thenThrow(new RemoteException());
+
+        assertThat(isCallingAppPermitted(mContext, mActivityToken, PERMISSION)).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
index 99738e7..d1b2b74 100644
--- a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
@@ -16,6 +16,16 @@
 
 package com.android.settings.password;
 
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.ComponentName;
@@ -23,6 +33,8 @@
 import android.os.Bundle;
 import android.provider.Settings;
 
+import com.android.settings.testutils.shadow.ShadowPasswordUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -31,11 +43,14 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowActivity;
 
 @RunWith(RobolectricTestRunner.class)
 public class SetNewPasswordActivityTest {
 
+    private static final String APP_LABEL = "label";
+
     private int mProvisioned;
 
     @Before
@@ -48,6 +63,7 @@
     public void tearDown() {
         Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
                 Settings.Global.DEVICE_PROVISIONED, mProvisioned);
+        ShadowPasswordUtils.reset();
     }
 
     @Test
@@ -77,4 +93,106 @@
         assertThat(intent.getComponent())
                 .isEqualTo(new ComponentName(activity, SetupChooseLockGeneric.class));
     }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void testLaunchChooseLock_setNewPasswordExtraWithoutPermission() {
+        ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+        Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        SetNewPasswordActivity activity =
+                Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        assertThat(shadowActivity.getNextStartedActivityForResult()).isNull();
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void testLaunchChooseLock_setNewPasswordExtraWithPermission() {
+        ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+        ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+        Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        SetNewPasswordActivity activity =
+                Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+        assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isTrue();
+        assertThat(actualIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE))
+                .isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+        assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void testLaunchChooseLock_setNewPasswordExtraInvalidValue() {
+        ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+        ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+        Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, -1);
+        SetNewPasswordActivity activity =
+                Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+        assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+        assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void testLaunchChooseLock_setNewPasswordExtraNoneComplexity() {
+        ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+        ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+        Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
+        SetNewPasswordActivity activity =
+                Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+        assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PASSWORD);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+        assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void testLaunchChooseLock_setNewParentProfilePasswordExtraWithPermission() {
+        ShadowPasswordUtils.setCallingAppLabel(APP_LABEL);
+        ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+        Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        Intent intent = new Intent(ACTION_SET_NEW_PARENT_PROFILE_PASSWORD);
+        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        SetNewPasswordActivity activity =
+                Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        Intent actualIntent = shadowActivity.getNextStartedActivityForResult().intent;
+        assertThat(actualIntent.getAction()).isEqualTo(ACTION_SET_NEW_PARENT_PROFILE_PASSWORD);
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)).isFalse();
+        assertThat(actualIntent.hasExtra(EXTRA_KEY_CALLER_APP_NAME)).isTrue();
+        assertThat(actualIntent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME)).isEqualTo(APP_LABEL);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
new file mode 100644
index 0000000..63bdc38
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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.password;
+
+import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
+
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+
+import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
+import com.android.settings.testutils.shadow.ShadowPasswordUtils;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.testutils.shadow.ShadowUtils;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowActivity;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        ShadowUserManager.class,
+        ShadowUtils.class,
+        ShadowLockPatternUtils.class,
+})
+public class SetupChooseLockGenericTest {
+
+    @After
+    public void tearDown() {
+        ShadowPasswordUtils.reset();
+    }
+
+    @Test
+    public void setupChooseLockGenericPasswordComplexityExtraWithoutPermission() {
+        Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
+        intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        SetupChooseLockGeneric activity =
+                Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        assertThat(shadowActivity.isFinishing()).isTrue();
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void setupChooseLockGenericPasswordComplexityExtraWithPermission() {
+        ShadowPasswordUtils.addGrantedPermission(GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
+
+        Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
+        intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        SetupChooseLockGeneric activity =
+                Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).create().get();
+
+        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
+        assertThat(shadowActivity.isFinishing()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java
index 38d658c..76bdaef 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowActivityManager.java
@@ -17,6 +17,7 @@
 package com.android.settings.testutils.shadow;
 
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
@@ -24,6 +25,7 @@
 @Implements(ActivityManager.class)
 public class ShadowActivityManager {
     private static int sCurrentUserId = 0;
+    private static IActivityManager sService = null;
 
     @Implementation
     protected static int getCurrentUser() {
@@ -33,4 +35,13 @@
     public static void setCurrentUser(int userId) {
         sCurrentUserId = userId;
     }
+
+    @Implementation
+    public static IActivityManager getService() {
+        return sService;
+    }
+
+    public static void setService(IActivityManager service) {
+        sService = service;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
index 6d2dbef..ca75916 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManager.java
@@ -1,5 +1,7 @@
 package com.android.settings.testutils.shadow;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -23,6 +25,10 @@
     private boolean mIsAdminActiveAsUser = false;
     private ComponentName mDeviceOwnerComponentName;
     private int mDeviceOwnerUserId = -1;
+    private int mPasswordMinQuality = PASSWORD_QUALITY_UNSPECIFIED;
+    private int mPasswordMaxLength = 16;
+    private int mPasswordMinLength = 0;
+    private int mPasswordMinSymbols = 0;
 
     public void setShortSupportMessageForUser(ComponentName admin, int userHandle, String message) {
         mSupportMessagesMap.put(Objects.hash(admin, userHandle), message);
@@ -70,6 +76,42 @@
         mDeviceOwnerComponentName = admin;
     }
 
+    @Implementation
+    public int getPasswordQuality(ComponentName admin, int userHandle) {
+        return mPasswordMinQuality;
+    }
+
+    public void setPasswordQuality(int quality) {
+        mPasswordMinQuality = quality;
+    }
+
+    @Implementation
+    public int getPasswordMinimumLength(ComponentName admin, int userHandle) {
+        return mPasswordMinLength;
+    }
+
+    public void setPasswordMinimumLength(int length) {
+        mPasswordMinLength = length;
+    }
+
+    @Implementation
+    public int getPasswordMinimumSymbols(ComponentName admin, int userHandle) {
+        return mPasswordMinSymbols;
+    }
+
+    public void setPasswordMinimumSymbols(int numOfSymbols) {
+        mPasswordMinSymbols = numOfSymbols;
+    }
+
+    @Implementation
+    public int getPasswordMaximumLength(int quality) {
+        return mPasswordMaxLength;
+    }
+
+    public void setPasswordMaximumLength(int length) {
+        mPasswordMaxLength = length;
+    }
+
     public static ShadowDevicePolicyManager getShadow() {
         return (ShadowDevicePolicyManager) Shadow.extract(
                 RuntimeEnvironment.application.getSystemService(DevicePolicyManager.class));
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
index 663ab91..7ce098d 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
@@ -59,4 +59,14 @@
     public static void setDeviceEncryptionEnabled(boolean deviceEncryptionEnabled) {
         sDeviceEncryptionEnabled = deviceEncryptionEnabled;
     }
+
+    @Implementation
+    protected byte[] getPasswordHistoryHashFactor(String currentPassword, int userId) {
+        return null;
+    }
+
+    @Implementation
+    protected boolean checkPasswordHistory(String passwordToCheck, byte[] hashFactor, int userId) {
+        return false;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPasswordUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPasswordUtils.java
new file mode 100644
index 0000000..6a5c4ae
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPasswordUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.testutils.shadow;
+
+import android.content.Context;
+import android.os.IBinder;
+
+import com.android.settings.password.PasswordUtils;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@Implements(PasswordUtils.class)
+public class ShadowPasswordUtils {
+
+    private static String sCallingAppLabel;
+    private static Set<String> sGrantedPermissions;
+
+    public static void reset() {
+        sCallingAppLabel = null;
+        sGrantedPermissions = null;
+    }
+
+    @Implementation
+    protected static boolean isCallingAppPermitted(Context context, IBinder activityToken,
+            String permission) {
+        if (sGrantedPermissions == null) {
+            return false;
+        }
+        return sGrantedPermissions.contains(permission);
+    }
+
+    public static void addGrantedPermission(String... permissions) {
+        if (sGrantedPermissions == null) {
+            sGrantedPermissions = new HashSet<>();
+        }
+        sGrantedPermissions.addAll(Arrays.asList(permissions));
+    }
+
+    @Implementation
+    protected static String getCallingAppLabel(Context context, IBinder activityToken) {
+        return sCallingAppLabel;
+    }
+
+    public static void setCallingAppLabel(String label) {
+        sCallingAppLabel = label;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
index ae352cc..dd99e55 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
@@ -139,4 +139,11 @@
 
         verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
     }
+
+    @Test
+    public void onConnectedChanged_shouldNotifyChange() {
+        mWifiScanWorker.onConnectedChanged();
+
+        verify(mResolver).notifyChange(WIFI_SLICE_URI, null);
+    }
 }