Fix DPM.resetPassword("")

While we're at it, also fix some multi-user issues in
LockPatternUtils.

Bug: 17496766
Change-Id: I8e557ea640fa589817c8f8f818c91463585d5ea7
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 86f9060..d00cbb7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1381,13 +1381,16 @@
      * characters when the requested quality is only numeric), in which case
      * the currently active quality will be increased to match.
      *
+     * <p>Calling with a null or empty password will clear any existing PIN,
+     * pattern or password if the current password constraints allow it.
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
      *
      * <p>Calling this from a managed profile will throw a security exception.
      *
-     * @param password The new password for the user.
+     * @param password The new password for the user. Null or empty clears the password.
      * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}.
      * @return Returns true if the password was applied, or false if it is
      * not acceptable for the current constraints.
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 8d3db5b..f6c42af 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -191,9 +191,6 @@
         return trust;
     }
 
-    /**
-     * @param contentResolver Used to look up and save settings.
-     */
     public LockPatternUtils(Context context) {
         mContext = context;
         mContentResolver = context.getContentResolver();
@@ -490,17 +487,23 @@
         return activePasswordQuality;
     }
 
+    public void clearLock(boolean isFallback) {
+        clearLock(isFallback, getCurrentOrCallingUserId());
+    }
+
     /**
      * Clear any lock pattern or password.
      */
-    public void clearLock(boolean isFallback) {
-        if(!isFallback) deleteGallery();
-        saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
-        setLockPatternEnabled(false);
-        saveLockPattern(null);
-        setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
-        setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
-        onAfterChangingPassword();
+    public void clearLock(boolean isFallback, int userHandle) {
+        if(!isFallback) deleteGallery(userHandle);
+        saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, isFallback,
+                userHandle);
+        setLockPatternEnabled(false, userHandle);
+        saveLockPattern(null, isFallback, userHandle);
+        setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
+        setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+                userHandle);
+        onAfterChangingPassword(userHandle);
     }
 
     /**
@@ -547,11 +550,11 @@
     /**
      * Calls back SetupFaceLock to delete the gallery file when the lock type is changed
     */
-    void deleteGallery() {
-        if(usingBiometricWeak()) {
+    void deleteGallery(int userId) {
+        if(usingBiometricWeak(userId)) {
             Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY");
             intent.putExtra("deleteGallery", true);
-            mContext.sendBroadcast(intent);
+            mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
         }
     }
 
@@ -566,11 +569,20 @@
     /**
      * Save a lock pattern.
      * @param pattern The new pattern to save.
-     * @param isFallback Specifies if this is a fallback to biometric weak
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) {
+        this.saveLockPattern(pattern, isFallback, getCurrentOrCallingUserId());
+    }
+
+    /**
+     * Save a lock pattern.
+     * @param pattern The new pattern to save.
+     * @param isFallback Specifies if this is a fallback to biometric weak
+     * @param userId the user whose pattern is to be saved.
+     */
+    public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback,
+            int userId) {
         try {
-            int userId = getCurrentOrCallingUserId();
             getLockSettings().setLockPattern(patternToString(pattern), userId);
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (pattern != null) {
@@ -586,17 +598,17 @@
                     }
                 }
 
-                setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
+                setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId);
                 if (!isFallback) {
-                    deleteGallery();
-                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+                    deleteGallery(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);
                 } else {
-                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK);
+                    setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, userId);
                     setLong(PASSWORD_TYPE_ALTERNATE_KEY,
-                            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
-                    finishBiometricWeak();
+                            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
+                    finishBiometricWeak(userId);
                     dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
                             0, 0, 0, 0, 0, 0, 0, userId);
                 }
@@ -604,7 +616,7 @@
                 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
                         0, 0, 0, 0, 0, userId);
             }
-            onAfterChangingPassword();
+            onAfterChangingPassword(userId);
         } catch (RemoteException re) {
             Log.e(TAG, "Couldn't save lock pattern " + re);
         }
@@ -822,7 +834,7 @@
                 }
 
                 if (!isFallback) {
-                    deleteGallery();
+                    deleteGallery(userHandle);
                     setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
                     if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                         int letters = 0;
@@ -862,7 +874,7 @@
                             userHandle);
                     setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality),
                             userHandle);
-                    finishBiometricWeak();
+                    finishBiometricWeak(userHandle);
                     dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK,
                             0, 0, 0, 0, 0, 0, 0, userHandle);
                 }
@@ -870,7 +882,7 @@
                 // password hashes have the same length for simplicity of implementation.
                 String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
                 if (passwordHistory == null) {
-                    passwordHistory = new String();
+                    passwordHistory = "";
                 }
                 int passwordHistoryLength = getRequestedPasswordHistoryLength();
                 if (passwordHistoryLength == 0) {
@@ -897,7 +909,7 @@
                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
                         userHandle);
             }
-            onAfterChangingPassword();
+            onAfterChangingPassword(userHandle);
         } catch (RemoteException re) {
             // Cant do much
             Log.e(TAG, "Unable to save lock password " + re);
@@ -1190,7 +1202,14 @@
      * Set whether the lock pattern is enabled.
      */
     public void setLockPatternEnabled(boolean enabled) {
-        setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
+        setLockPatternEnabled(enabled, getCurrentOrCallingUserId());
+    }
+
+    /**
+     * Set whether the lock pattern is enabled.
+     */
+    public void setLockPatternEnabled(boolean enabled, int userHandle) {
+        setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled, userHandle);
     }
 
     /**
@@ -1584,15 +1603,15 @@
         return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
     }
 
-    private void finishBiometricWeak() {
-        setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true);
+    private void finishBiometricWeak(int userId) {
+        setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true, userId);
 
         // Launch intent to show final screen, this also
         // moves the temporary gallery to the actual gallery
         Intent intent = new Intent();
         intent.setClassName("com.android.facelock",
                 "com.android.facelock.SetupEndScreen");
-        mContext.startActivity(intent);
+        mContext.startActivityAsUser(intent, new UserHandle(userId));
     }
 
     public void setPowerButtonInstantlyLocks(boolean enabled) {
@@ -1686,8 +1705,8 @@
         getTrustManager().reportRequireCredentialEntry(userId);
     }
 
-    private void onAfterChangingPassword() {
-        getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId());
+    private void onAfterChangingPassword(int userHandle) {
+        getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
     }
 
     public boolean isCredentialRequiredToDecrypt(boolean defaultValue) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ac5fb18..6331dfe 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -77,6 +77,7 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -2575,13 +2576,15 @@
         return strictestAdmin;
     }
 
-    public boolean resetPassword(String password, int flags, int userHandle) {
+    public boolean resetPassword(String passwordOrNull, int flags, int userHandle) {
         if (!mHasFeature) {
             return false;
         }
         enforceCrossUserPermission(userHandle);
         enforceNotManagedProfile(userHandle, "reset the password");
 
+        String password = passwordOrNull != null ? passwordOrNull : "";
+
         int quality;
         synchronized (this) {
             // This api can only be called by an active device admin,
@@ -2685,7 +2688,11 @@
         long ident = Binder.clearCallingIdentity();
         try {
             LockPatternUtils utils = new LockPatternUtils(mContext);
-            utils.saveLockPassword(password, quality, false, userHandle);
+            if (!TextUtils.isEmpty(password)) {
+                utils.saveLockPassword(password, quality, false, userHandle);
+            } else {
+                utils.clearLock(false, userHandle);
+            }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
                 utils.requireCredentialEntry(UserHandle.USER_ALL);