resolve merge conflicts of ad4aa1ce7d3d to nyc-mr1-dev

Change-Id: I97ef31536cd06495a08a3f94f81df2d1376186e0
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a2f9bdd..e23f142 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2016,7 +2016,8 @@
      * Determine whether the current password the user has set is sufficient to meet the policy
      * requirements (e.g. quality, minimum length) that have been requested by the admins of this
      * user and its participating profiles. Restrictions on profiles that have a separate challenge
-     * are not taken into account.
+     * are not taken into account. If the user has a password, it must have been entered in order to
+     * perform this check.
      * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
@@ -2029,6 +2030,7 @@
      * @return Returns true if the password meets the current requirements, else false.
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+     * @throws IllegalStateException if the user has a password but has not entered it yet.
      */
     public boolean isActivePasswordSufficient() {
         if (mService != null) {
@@ -3541,6 +3543,19 @@
     /**
      * @hide
      */
+    public void reportPasswordChanged(int userId) {
+        if (mService != null) {
+            try {
+                mService.reportPasswordChanged(userId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
     public void reportFailedPasswordAttempt(int userHandle) {
         if (mService != null) {
             try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f39cb5a..90632a1 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -119,6 +119,7 @@
 
     void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
         int numbers, int symbols, int nonletter, int userHandle);
+    void reportPasswordChanged(int userId);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
     void reportFailedFingerprintAttempt(int userHandle);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 479b3b7..d49d433 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -593,9 +593,6 @@
             setCredentialRequiredToDecrypt(false);
         }
 
-        getDevicePolicyManager().setActivePasswordState(
-                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
-
         onAfterChangingPassword(userHandle);
     }
 
@@ -642,6 +639,7 @@
                         + MIN_LOCK_PATTERN_SIZE + " dots long.");
             }
 
+            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
             getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
             DevicePolicyManager dpm = getDevicePolicyManager();
 
@@ -658,9 +656,6 @@
 
             setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
 
-            setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
-            dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
-                    pattern.size(), 0, 0, 0, 0, 0, 0, userId);
             onAfterChangingPassword(userId);
         } catch (RemoteException re) {
             Log.e(TAG, "Couldn't save lock pattern " + re);
@@ -863,9 +858,9 @@
                         + "of length " + MIN_LOCK_PASSWORD_SIZE);
             }
 
+            final int computedQuality = computePasswordQuality(password);
+            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
             getLockSettings().setLockPassword(password, savedPassword, userHandle);
-            getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
-            int computedQuality = computePasswordQuality(password);
 
             // Update the device encryption password.
             if (userHandle == UserHandle.USER_SYSTEM
@@ -883,40 +878,6 @@
                 }
             }
 
-            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
-            if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                int letters = 0;
-                int uppercase = 0;
-                int lowercase = 0;
-                int numbers = 0;
-                int symbols = 0;
-                int nonletter = 0;
-                for (int i = 0; i < password.length(); i++) {
-                    char c = password.charAt(i);
-                    if (c >= 'A' && c <= 'Z') {
-                        letters++;
-                        uppercase++;
-                    } else if (c >= 'a' && c <= 'z') {
-                        letters++;
-                        lowercase++;
-                    } else if (c >= '0' && c <= '9') {
-                        numbers++;
-                        nonletter++;
-                    } else {
-                        symbols++;
-                        nonletter++;
-                    }
-                }
-                dpm.setActivePasswordState(Math.max(quality, computedQuality),
-                        password.length(), letters, uppercase, lowercase,
-                        numbers, symbols, nonletter, userHandle);
-            } else {
-                // The password is not anything.
-                dpm.setActivePasswordState(
-                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
-                        0, 0, 0, 0, 0, 0, 0, userHandle);
-            }
-
             // Add the password to the password history. We assume all
             // password hashes have the same length for simplicity of implementation.
             String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 03744f8..651fb66 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -913,6 +913,7 @@
         synchronized (mSeparateChallengeLock) {
             setLockPatternInternal(pattern, savedCredential, userId);
             setSeparateProfileChallengeEnabled(userId, true, null);
+            notifyPasswordChanged(userId);
         }
     }
 
@@ -927,6 +928,7 @@
             setKeystorePassword(null, userId);
             fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
+            notifyActivePasswordMetricsAvailable(null, userId);
             return;
         }
 
@@ -976,6 +978,7 @@
         synchronized (mSeparateChallengeLock) {
             setLockPasswordInternal(password, savedCredential, userId);
             setSeparateProfileChallengeEnabled(userId, true, null);
+            notifyPasswordChanged(userId);
         }
     }
 
@@ -989,6 +992,7 @@
             setKeystorePassword(null, userId);
             fixateNewestUserKeyAuth(userId);
             onUserLockChanged(userId);
+            notifyActivePasswordMetricsAvailable(null, userId);
             return;
         }
 
@@ -1398,6 +1402,7 @@
                 // migrate credential to GateKeeper
                 credentialUtil.setCredential(credential, null, userId);
                 if (!hasChallenge) {
+                    notifyActivePasswordMetricsAvailable(credential, userId);
                     return VerifyCredentialResponse.OK;
                 }
                 // Fall through to get the auth token. Technically this should never happen,
@@ -1437,6 +1442,7 @@
             if (progressCallback != null) {
                 progressCallback.onCredentialVerified();
             }
+            notifyActivePasswordMetricsAvailable(credential, userId);
             unlockKeystore(credential, userId);
 
             Slog.i(TAG, "Unlocking user " + userId +
@@ -1460,6 +1466,60 @@
         return response;
     }
 
+    private void notifyActivePasswordMetricsAvailable(final String password, int userId) {
+        final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(userId);
+
+        // Asynchronous to avoid dead lock
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                int length = 0;
+                int letters = 0;
+                int uppercase = 0;
+                int lowercase = 0;
+                int numbers = 0;
+                int symbols = 0;
+                int nonletter = 0;
+                if (password != null) {
+                    length = password.length();
+                    for (int i = 0; i < length; i++) {
+                        char c = password.charAt(i);
+                        if (c >= 'A' && c <= 'Z') {
+                            letters++;
+                            uppercase++;
+                        } else if (c >= 'a' && c <= 'z') {
+                            letters++;
+                            lowercase++;
+                        } else if (c >= '0' && c <= '9') {
+                            numbers++;
+                            nonletter++;
+                        } else {
+                            symbols++;
+                            nonletter++;
+                        }
+                    }
+                }
+                DevicePolicyManager dpm = (DevicePolicyManager)
+                        mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+                dpm.setActivePasswordState(quality, length, letters, uppercase, lowercase, numbers,
+                        symbols, nonletter, userId);
+            }
+        });
+    }
+
+    /**
+     * Call after {@link #notifyActivePasswordMetricsAvailable} so metrics are updated before
+     * reporting the password changed.
+     */
+    private void notifyPasswordChanged(int userId) {
+        // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
+        mHandler.post(() -> {
+            DevicePolicyManager dpm = (DevicePolicyManager)
+                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            dpm.reportPasswordChanged(userId);
+        });
+    }
+
     @Override
     public boolean checkVoldPassword(int userId) throws RemoteException {
         if (!mFirstCallToVold) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 92aa1b9..fbb865b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2259,10 +2259,13 @@
                 out.endTag(null, "failed-password-attempts");
             }
 
-            if (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0
+            // Don't save metrics for FBE devices
+            if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()
+                    && (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0
                     || policy.mActivePasswordUpperCase != 0 || policy.mActivePasswordLowerCase != 0
                     || policy.mActivePasswordLetters != 0 || policy.mActivePasswordNumeric != 0
-                    || policy.mActivePasswordSymbols != 0 || policy.mActivePasswordNonLetter != 0) {
+                    || policy.mActivePasswordSymbols != 0
+                    || policy.mActivePasswordNonLetter != 0)) {
                 out.startTag(null, "active-password");
                 out.attribute(null, "quality", Integer.toString(policy.mActivePasswordQuality));
                 out.attribute(null, "length", Integer.toString(policy.mActivePasswordLength));
@@ -2355,6 +2358,7 @@
         JournaledFile journal = makeJournaledFile(userHandle);
         FileInputStream stream = null;
         File file = journal.chooseForRead();
+        boolean needsRewrite = false;
         try {
             stream = new FileInputStream(file);
             XmlPullParser parser = Xml.newPullParser();
@@ -2437,23 +2441,6 @@
                 } else if ("password-owner".equals(tag)) {
                     policy.mPasswordOwner = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
-                } else if ("active-password".equals(tag)) {
-                    policy.mActivePasswordQuality = Integer.parseInt(
-                            parser.getAttributeValue(null, "quality"));
-                    policy.mActivePasswordLength = Integer.parseInt(
-                            parser.getAttributeValue(null, "length"));
-                    policy.mActivePasswordUpperCase = Integer.parseInt(
-                            parser.getAttributeValue(null, "uppercase"));
-                    policy.mActivePasswordLowerCase = Integer.parseInt(
-                            parser.getAttributeValue(null, "lowercase"));
-                    policy.mActivePasswordLetters = Integer.parseInt(
-                            parser.getAttributeValue(null, "letters"));
-                    policy.mActivePasswordNumeric = Integer.parseInt(
-                            parser.getAttributeValue(null, "numeric"));
-                    policy.mActivePasswordSymbols = Integer.parseInt(
-                            parser.getAttributeValue(null, "symbols"));
-                    policy.mActivePasswordNonLetter = Integer.parseInt(
-                            parser.getAttributeValue(null, "nonletter"));
                 } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
                     policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
                 } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
@@ -2470,6 +2457,28 @@
                     policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
                 } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) {
                     policy.mInitBundle = PersistableBundle.restoreFromXml(parser);
+                } else if ("active-password".equals(tag)) {
+                    if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+                        // Remove this from FBE devices
+                        needsRewrite = true;
+                    } else {
+                        policy.mActivePasswordQuality = Integer.parseInt(
+                            parser.getAttributeValue(null, "quality"));
+                        policy.mActivePasswordLength = Integer.parseInt(
+                                parser.getAttributeValue(null, "length"));
+                        policy.mActivePasswordUpperCase = Integer.parseInt(
+                                parser.getAttributeValue(null, "uppercase"));
+                        policy.mActivePasswordLowerCase = Integer.parseInt(
+                                parser.getAttributeValue(null, "lowercase"));
+                        policy.mActivePasswordLetters = Integer.parseInt(
+                                parser.getAttributeValue(null, "letters"));
+                        policy.mActivePasswordNumeric = Integer.parseInt(
+                                parser.getAttributeValue(null, "numeric"));
+                        policy.mActivePasswordSymbols = Integer.parseInt(
+                                parser.getAttributeValue(null, "symbols"));
+                        policy.mActivePasswordNonLetter = Integer.parseInt(
+                                parser.getAttributeValue(null, "nonletter"));
+                    }
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -2489,34 +2498,14 @@
             // Ignore
         }
 
+        // Might need to upgrade the file by rewriting it
+        if (needsRewrite) {
+            saveSettingsLocked(userHandle);
+        }
+
         // Generate a list of admins from the admin map
         policy.mAdminList.addAll(policy.mAdminMap.values());
 
-        // Validate that what we stored for the password quality matches
-        // sufficiently what is currently set.  Note that this is only
-        // a sanity check in case the two get out of sync; this should
-        // never normally happen.
-        final long identity = mInjector.binderClearCallingIdentity();
-        try {
-            int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle);
-            if (actualPasswordQuality < policy.mActivePasswordQuality) {
-                Slog.w(LOG_TAG, "Active password quality 0x"
-                        + Integer.toHexString(policy.mActivePasswordQuality)
-                        + " does not match actual quality 0x"
-                        + Integer.toHexString(actualPasswordQuality));
-                policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-                policy.mActivePasswordLength = 0;
-                policy.mActivePasswordUpperCase = 0;
-                policy.mActivePasswordLowerCase = 0;
-                policy.mActivePasswordLetters = 0;
-                policy.mActivePasswordNumeric = 0;
-                policy.mActivePasswordSymbols = 0;
-                policy.mActivePasswordNonLetter = 0;
-            }
-        } finally {
-            mInjector.binderRestoreCallingIdentity(identity);
-        }
-
         validatePasswordOwnerLocked(policy);
         updateMaximumTimeToLockLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
@@ -3740,6 +3729,8 @@
 
     private boolean isActivePasswordSufficientForUserLocked(
             DevicePolicyData policy, int userHandle, boolean parent) {
+        enforceUserUnlocked(userHandle, parent);
+
         final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
         if (policy.mActivePasswordQuality < requiredPasswordQuality) {
             return false;
@@ -4814,40 +4805,66 @@
             return;
         }
         enforceFullCrossUsersPermission(userHandle);
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+        // If the managed profile doesn't have a separate password, set the metrics to default
+        if (isManagedProfile(userHandle) && !isSeparateProfileChallengeEnabled(userHandle)) {
+            quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+            length = 0;
+            letters = 0;
+            uppercase = 0;
+            lowercase = 0;
+            numbers = 0;
+            symbols = 0;
+            nonletter = 0;
+        }
+
+        validateQualityConstant(quality);
+        DevicePolicyData policy = getUserData(userHandle);
+        synchronized (this) {
+            policy.mActivePasswordQuality = quality;
+            policy.mActivePasswordLength = length;
+            policy.mActivePasswordLetters = letters;
+            policy.mActivePasswordLowerCase = lowercase;
+            policy.mActivePasswordUpperCase = uppercase;
+            policy.mActivePasswordNumeric = numbers;
+            policy.mActivePasswordSymbols = symbols;
+            policy.mActivePasswordNonLetter = nonletter;
+        }
+    }
+
+    @Override
+    public void reportPasswordChanged(int userId) {
+        if (!mHasFeature) {
+            return;
+        }
+        enforceFullCrossUsersPermission(userId);
 
         // Managed Profile password can only be changed when it has a separate challenge.
-        if (!isSeparateProfileChallengeEnabled(userHandle)) {
-            enforceNotManagedProfile(userHandle, "set the active password");
+        if (!isSeparateProfileChallengeEnabled(userId)) {
+            enforceNotManagedProfile(userId, "set the active password");
         }
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        validateQualityConstant(quality);
 
-        DevicePolicyData policy = getUserData(userHandle);
+        DevicePolicyData policy = getUserData(userId);
 
         long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (this) {
-                policy.mActivePasswordQuality = quality;
-                policy.mActivePasswordLength = length;
-                policy.mActivePasswordLetters = letters;
-                policy.mActivePasswordLowerCase = lowercase;
-                policy.mActivePasswordUpperCase = uppercase;
-                policy.mActivePasswordNumeric = numbers;
-                policy.mActivePasswordSymbols = symbols;
-                policy.mActivePasswordNonLetter = nonletter;
                 policy.mFailedPasswordAttempts = 0;
-                saveSettingsLocked(userHandle);
-                updatePasswordExpirationsLocked(userHandle);
-                setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
+                saveSettingsLocked(userId);
+                updatePasswordExpirationsLocked(userId);
+                setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
 
                 // Send a broadcast to each profile using this password as its primary unlock.
                 sendAdminCommandForLockscreenPoliciesLocked(
                         DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
-                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
+                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
             }
-            removeCaApprovalsIfNeeded(userHandle);
+            removeCaApprovalsIfNeeded(userId);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -6421,6 +6438,14 @@
                 "User must be running and unlocked");
     }
 
+    private void enforceUserUnlocked(int userId, boolean parent) {
+        if (parent) {
+            enforceUserUnlocked(getProfileParentId(userId));
+        } else {
+            enforceUserUnlocked(userId);
+        }
+    }
+
     private void enforceManageUsers() {
         final int callingUid = mInjector.binderGetCallingUid();
         if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {