Merge "Fix issue 2349345: Media sound output stuck on earpiece rather than speaker."
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bb16215a..c2e6142 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -797,7 +797,7 @@
     public void setMode(int mode) {
         IAudioService service = getService();
         try {
-            service.setMode(mode);
+            service.setMode(mode, mICallBack);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in setMode", e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 482fc4f..9362305 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -54,7 +54,6 @@
 import java.util.Map;
 import java.util.Set;
 
-
 /**
  * The implementation of the volume manager service.
  * <p>
@@ -231,6 +230,9 @@
     // Forced device usage for communications
     private int mForcedUseForComm;
 
+    // List of binder death handlers for setMode() client processes.
+    // The last process to have called setMode() is at the top of the list.
+    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
 
     ///////////////////////////////////////////////////////////////////////////
     // Construction
@@ -248,11 +250,13 @@
 
         mVolumePanel = new VolumePanel(context, this);
         mSettingsObserver = new SettingsObserver();
-        mMode = AudioSystem.MODE_NORMAL;
         mForcedUseForComm = AudioSystem.FORCE_NONE;
         createAudioSystemThread();
         readPersistedSettings();
         createStreamStates();
+        // Call setMode() to initialize mSetModeDeathHandlers
+        mMode = AudioSystem.MODE_INVALID;
+        setMode(AudioSystem.MODE_NORMAL, null);
         mMediaServerOk = true;
         AudioSystem.setErrorCallback(mAudioSystemCallback);
         loadSoundEffects();
@@ -582,8 +586,54 @@
         return existingValue;
     }
 
+    private class SetModeDeathHandler implements IBinder.DeathRecipient {
+        private IBinder mCb; // To be notified of client's death
+        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
+
+        SetModeDeathHandler(IBinder cb) {
+            mCb = cb;
+        }
+
+        public void binderDied() {
+            synchronized(mSetModeDeathHandlers) {
+                Log.w(TAG, "setMode() client died");
+                int index = mSetModeDeathHandlers.indexOf(this);
+                if (index < 0) {
+                    Log.w(TAG, "unregistered setMode() client died");
+                } else {
+                    mSetModeDeathHandlers.remove(this);
+                    // If dead client was a the top of client list,
+                    // apply next mode in the stack
+                    if (index == 0) {
+                        // mSetModeDeathHandlers is never empty as the initial entry
+                        // created when AudioService starts is never removed
+                        SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
+                        int mode = hdlr.getMode();
+                        if (AudioService.this.mMode != mode) {
+                            if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
+                                AudioService.this.mMode = mode;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        public void setMode(int mode) {
+            mMode = mode;
+        }
+
+        public int getMode() {
+            return mMode;
+        }
+
+        public IBinder getBinder() {
+            return mCb;
+        }
+    }
+
     /** @see AudioManager#setMode(int) */
-    public void setMode(int mode) {
+    public void setMode(int mode, IBinder cb) {
         if (!checkAudioSettingsPermission("setMode()")) {
             return;
         }
@@ -599,6 +649,37 @@
             if (mode != mMode) {
                 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
                     mMode = mode;
+
+                    synchronized(mSetModeDeathHandlers) {
+                        SetModeDeathHandler hdlr = null;
+                        Iterator iter = mSetModeDeathHandlers.iterator();
+                        while (iter.hasNext()) {
+                            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
+                            if (h.getBinder() == cb) {
+                                hdlr = h;
+                                // Remove from client list so that it is re-inserted at top of list
+                                iter.remove();
+                                break;
+                            }
+                        }
+                        if (hdlr == null) {
+                            hdlr = new SetModeDeathHandler(cb);
+                            // cb is null when setMode() is called by AudioService constructor
+                            if (cb != null) {
+                                // Register for client death notification
+                                try {
+                                    cb.linkToDeath(hdlr, 0);
+                                } catch (RemoteException e) {
+                                    // Client has died!
+                                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
+                                }
+                            }
+                        }
+                        // Last client to call setMode() is always at top of client list
+                        // as required by SetModeDeathHandler.binderDied()
+                        mSetModeDeathHandlers.add(0, hdlr);
+                        hdlr.setMode(mode);
+                    }
                 }
             }
             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d3d2d29..83581d2 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -47,7 +47,7 @@
     
     boolean shouldVibrate(int vibrateType);
 
-    void setMode(int mode);
+    void setMode(int mode, IBinder cb);
 
     int getMode();