Fix bug 2605504 Don't leak a metric ton of NotificationPlayer threads
The Looper on the thread created in order to be notified of the
playback completion of notification sounds, was never stopped, causing
the threads to stay around in wait state.
The fix consists in quitting the Looper used for the previous sound
when a new sound is started.

Change-Id: Ia4b3d16b5447ffafacc093db3a7fe05574089935
diff --git a/services/java/com/android/server/NotificationPlayer.java b/services/java/com/android/server/NotificationPlayer.java
index acc4c85..0b1a03b 100644
--- a/services/java/com/android/server/NotificationPlayer.java
+++ b/services/java/com/android/server/NotificationPlayer.java
@@ -69,9 +69,9 @@
      * OnCompletionListener, to be called at the end of the playback, the MediaPlayer needs to
      * be created with a looper running so its event handler is not null.
      */
-    private final class PlayerCreationThread extends Thread {
+    private final class CreationAndCompletionThread extends Thread {
         public Command mCmd;
-        public PlayerCreationThread(Command cmd) {
+        public CreationAndCompletionThread(Command cmd) {
             super();
             mCmd = cmd;
         }
@@ -121,10 +121,19 @@
             //-----------------------------------
             // This is were we deviate from the AsyncPlayer implementation and create the
             // MediaPlayer in a new thread with which we're synchronized
-            PlayerCreationThread t = new PlayerCreationThread(cmd);
-            synchronized(t) {
-                t.start();
-                t.wait();
+            synchronized(mCompletionHandlingLock) {
+                // if another sound was already playing, it doesn't matter we won't get notified
+                // of the completion, since only the completion notification of the last sound
+                // matters
+                if((mLooper != null)
+                        && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+                    mLooper.quit();
+                }
+                mCompletionThread = new CreationAndCompletionThread(cmd);
+                synchronized(mCompletionThread) {
+                    mCompletionThread.start();
+                    mCompletionThread.wait();
+                }
             }
             //-----------------------------------
 
@@ -169,7 +178,10 @@
                         mPlayer = null;
                         mAudioManager.abandonAudioFocus(null);
                         mAudioManager = null;
-                        mLooper.quit();
+                        if((mLooper != null)
+                                && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+                            mLooper.quit();
+                        }
                     } else {
                         Log.w(mTag, "STOP command without a player");
                     }
@@ -195,10 +207,23 @@
         if (mAudioManager != null) {
             mAudioManager.abandonAudioFocus(null);
         }
+        // if there are no more sounds to play, end the Looper to listen for media completion
+        synchronized (mCmdQueue) {
+            if (mCmdQueue.size() == 0) {
+                synchronized(mCompletionHandlingLock) {
+                    if(mLooper != null) {
+                        mLooper.quit();
+                    }
+                    mCompletionThread = null;
+                }
+            }
+        }
     }
 
     private String mTag;
     private CmdThread mThread;
+    private CreationAndCompletionThread mCompletionThread;
+    private final Object mCompletionHandlingLock = new Object();
     private MediaPlayer mPlayer;
     private PowerManager.WakeLock mWakeLock;
     private AudioManager mAudioManager;