Fix StatusBarService deadlock for real this time.

The lock is now only held long enough to swap the queue with a new ArrayList.

Bug: 2542233
Change-Id: I8c1c3d4d0b5b53166cc239fc0069d69929b43f91
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index bcaf0a4..93c8d34 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -153,6 +153,7 @@
     StatusBarView mStatusBarView;
     int mPixelFormat;
     H mHandler = new H();
+    Object mQueueLock = new Object();
     ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
     NotificationCallbacks mNotificationCallbacks;
     
@@ -440,7 +441,7 @@
     }
 
     private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
-        synchronized (mQueue) {
+        synchronized (mQueueLock) {
             PendingOp op = new PendingOp();
             op.key = key;
             op.code = code;
@@ -455,7 +456,7 @@
     }
 
     private void addPendingOp(int code, IBinder key, boolean visible) {
-        synchronized (mQueue) {
+        synchronized (mQueueLock) {
             PendingOp op = new PendingOp();
             op.key = key;
             op.code = code;
@@ -468,7 +469,7 @@
     }
 
     private void addPendingOp(int code, int integer) {
-        synchronized (mQueue) {
+        synchronized (mQueueLock) {
             PendingOp op = new PendingOp();
             op.code = code;
             op.integer = integer;
@@ -557,94 +558,94 @@
                 doRevealAnimation();
                 return;
             }
-            boolean expand = false;
+
+            ArrayList<PendingOp> queue;
+            synchronized (mQueueLock) {
+                queue = mQueue;
+                mQueue = new ArrayList<PendingOp>();
+            }
+
+            boolean wasExpanded = mExpanded;
+
+            // for each one in the queue, find all of the ones with the same key
+            // and collapse that down into a final op and/or call to setVisibility, etc
+            boolean expand = wasExpanded;
             boolean doExpand = false;
             boolean doDisable = false;
             int disableWhat = 0;
+            int N = queue.size();
+            while (N > 0) {
+                PendingOp op = queue.get(0);
+                boolean doOp = false;
+                boolean visible = false;
+                boolean doVisibility = false;
+                if (op.code == OP_SET_VISIBLE) {
+                    doVisibility = true;
+                    visible = op.visible;
+                }
+                else if (op.code == OP_EXPAND) {
+                    doExpand = true;
+                    expand = op.visible;
+                }
+                else if (op.code == OP_TOGGLE) {
+                    doExpand = true;
+                    expand = !expand;
+                }
+                else {
+                    doOp = true;
+                }
 
-            synchronized (mQueue) {
-                boolean wasExpanded = mExpanded;
-
-                // for each one in the queue, find all of the ones with the same key
-                // and collapse that down into a final op and/or call to setVisibility, etc
-                expand = wasExpanded;
-                int N = mQueue.size();
-                while (N > 0) {
-                    PendingOp op = mQueue.get(0);
-                    boolean doOp = false;
-                    boolean visible = false;
-                    boolean doVisibility = false;
-                    if (op.code == OP_SET_VISIBLE) {
-                        doVisibility = true;
-                        visible = op.visible;
-                    }
-                    else if (op.code == OP_EXPAND) {
-                        doExpand = true;
-                        expand = op.visible;
-                    }
-                    else if (op.code == OP_TOGGLE) {
-                        doExpand = true;
-                        expand = !expand;
-                    }
-                    else {
-                        doOp = true;
-                    }
-
-                    if (alwaysHandle(op.code)) {
-                        // coalesce these
-                        for (int i=1; i<N; i++) {
-                            PendingOp o = mQueue.get(i);
-                            if (!alwaysHandle(o.code) && o.key == op.key) {
-                                if (o.code == OP_SET_VISIBLE) {
-                                    visible = o.visible;
-                                    doVisibility = true;
-                                }
-                                else if (o.code == OP_EXPAND) {
-                                    expand = o.visible;
-                                    doExpand = true;
-                                }
-                                else {
-                                    op.code = o.code;
-                                    op.iconData = o.iconData;
-                                    op.notificationData = o.notificationData;
-                                }
-                                mQueue.remove(i);
-                                i--;
-                                N--;
+                if (alwaysHandle(op.code)) {
+                    // coalesce these
+                    for (int i=1; i<N; i++) {
+                        PendingOp o = queue.get(i);
+                        if (!alwaysHandle(o.code) && o.key == op.key) {
+                            if (o.code == OP_SET_VISIBLE) {
+                                visible = o.visible;
+                                doVisibility = true;
                             }
+                            else if (o.code == OP_EXPAND) {
+                                expand = o.visible;
+                                doExpand = true;
+                            }
+                            else {
+                                op.code = o.code;
+                                op.iconData = o.iconData;
+                                op.notificationData = o.notificationData;
+                            }
+                            queue.remove(i);
+                            i--;
+                            N--;
                         }
                     }
-
-                    mQueue.remove(0);
-                    N--;
-
-                    if (doOp) {
-                        switch (op.code) {
-                            case OP_ADD_ICON:
-                            case OP_UPDATE_ICON:
-                                performAddUpdateIcon(op.key, op.iconData, op.notificationData);
-                                break;
-                            case OP_REMOVE_ICON:
-                                performRemoveIcon(op.key);
-                                break;
-                            case OP_DISABLE:
-                                doDisable = true;
-                                disableWhat = op.integer;
-                                break;
-                        }
-                    }
-                    if (doVisibility && op.code != OP_REMOVE_ICON) {
-                        performSetIconVisibility(op.key, visible);
-                    }
                 }
 
-                if (mQueue.size() != 0) {
-                    throw new RuntimeException("Assertion failed: mQueue.size=" + mQueue.size());
+                queue.remove(0);
+                N--;
+
+                if (doOp) {
+                    switch (op.code) {
+                        case OP_ADD_ICON:
+                        case OP_UPDATE_ICON:
+                            performAddUpdateIcon(op.key, op.iconData, op.notificationData);
+                            break;
+                        case OP_REMOVE_ICON:
+                            performRemoveIcon(op.key);
+                            break;
+                        case OP_DISABLE:
+                            doDisable = true;
+                            disableWhat = op.integer;
+                            break;
+                    }
+                }
+                if (doVisibility && op.code != OP_REMOVE_ICON) {
+                    performSetIconVisibility(op.key, visible);
                 }
             }
-            // This must be done outside the synchronized block above to prevent a deadlock where
-            // we call into the NotificationManagerService and it is in turn attempting to post a
-            // message to our queue.
+
+            if (queue.size() != 0) {
+                throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
+            }
             if (doExpand) {
                 // this is last so that we capture all of the pending changes before doing it
                 if (expand) {
@@ -1397,7 +1398,7 @@
             return;
         }
         
-        synchronized (mQueue) {
+        synchronized (mQueueLock) {
             pw.println("Current Status Bar state:");
             pw.println("  mExpanded=" + mExpanded
                     + ", mExpandedVisible=" + mExpandedVisible);