Eliminate deadlock by deferring wipe data call

The call to RecoverySystem.rebootWipeUserData() was made while
holding the lock to DevicePolicyManagerService. But it blocks
waiting for system_process' main thread to receive the ordered
broadcast complete callback. It won't receive that callback
because Keyguard is running on the main thread and is concurrently
blocked on DevicePolicyManagerService.

By moving the call to rebootWipeUserData() out of the synchronized
block the deadlock is eliminated.

Fixes bug 16870054.

Change-Id: I3eb587211e5484859cc9dab7e80e5a1f6c85225d
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 564a3df..72e69e9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2998,10 +2998,12 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
-        synchronized (this) {
-            DevicePolicyData policy = getUserData(userHandle);
-            long ident = Binder.clearCallingIdentity();
-            try {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            boolean wipeData = false;
+            int identifier = 0;
+            synchronized (this) {
+                DevicePolicyData policy = getUserData(userHandle);
                 policy.mFailedPasswordAttempts++;
                 saveSettingsLocked(userHandle);
                 if (mHasFeature) {
@@ -3013,15 +3015,20 @@
                         // Wipe the user/profile associated with the policy that was violated. This
                         // is not necessarily calling user: if the policy that fired was from a
                         // managed profile rather than the main user profile, we wipe former only.
-                        wipeDeviceOrUserLocked(0, strictestAdmin.getUserHandle().getIdentifier());
+                        wipeData = true;
+                        identifier = strictestAdmin.getUserHandle().getIdentifier();
                     }
                     sendAdminCommandToSelfAndProfilesLocked(
                             DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
                             DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
                 }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
             }
+            if (wipeData) {
+                // Call without holding lock.
+                wipeDeviceOrUserLocked(0, identifier);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }