Merge "Focus gain sends focus loss through the focus stack"
diff --git a/media/java/android/media/FocusRequester.java b/media/java/android/media/FocusRequester.java
index 6b34b56..020f3e1 100644
--- a/media/java/android/media/FocusRequester.java
+++ b/media/java/android/media/FocusRequester.java
@@ -37,9 +37,10 @@
 
     // on purpose not using this classe's name, as it will only be used from MediaFocusControl
     private static final String TAG = "MediaFocusControl";
+    private static final boolean DEBUG = false;
 
     private AudioFocusDeathHandler mDeathHandler;
-    private final IAudioFocusDispatcher mFocusDispatcher;
+    private final IAudioFocusDispatcher mFocusDispatcher; // may be null
     private final IBinder mSourceRef;
     private final String mClientId;
     private final String mPackageName;
@@ -73,10 +74,6 @@
     }
 
 
-    boolean canDispatchFocus() {
-        return (mFocusDispatcher != null);
-    }
-
     boolean hasSameClient(String otherClient) {
         try {
             return mClientId.compareTo(otherClient) == 0;
@@ -197,7 +194,7 @@
                 switch(mFocusLossReceived) {
                     case AUDIOFOCUS_NONE:
                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
-                        return AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
+                        return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                     case AudioManager.AUDIOFOCUS_LOSS:
@@ -210,20 +207,19 @@
     }
 
     void handleExternalFocusGain(int focusGain) {
-        try {
-            int focusLoss = focusLossForGainRequest(focusGain);
-            if (focusLoss != mFocusLossReceived) {
-                mFocusDispatcher.dispatchAudioFocusChange(focusLoss, mClientId);
-                mFocusLossReceived = focusLoss;
-            }
-        } catch (android.os.RemoteException e) {
-            Log.e(TAG, "Failure to signal loss of focus: ", e);
-        }
+        int focusLoss = focusLossForGainRequest(focusGain);
+        handleFocusLoss(focusLoss);
     }
 
     void handleFocusGain(int focusGain) {
         try {
-            mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId);
+            if (mFocusDispatcher != null) {
+                if (DEBUG) {
+                    Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
+                        + mClientId);
+                }
+                mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId);
+            }
             mFocusLossReceived = AUDIOFOCUS_NONE;
         } catch (android.os.RemoteException e) {
             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
@@ -232,9 +228,16 @@
 
     void handleFocusLoss(int focusLoss) {
         try {
-            mFocusDispatcher.dispatchAudioFocusChange(
-                    focusLoss, mClientId);
-            mFocusLossReceived = focusLoss;
+            if (focusLoss != mFocusLossReceived) {
+                if (mFocusDispatcher != null) {
+                    if (DEBUG) {
+                        Log.v(TAG, "dispatching " + focusChangeToString(focusLoss) + " to "
+                            + mClientId);
+                    }
+                    mFocusDispatcher.dispatchAudioFocusChange(focusLoss, mClientId);
+                }
+                mFocusLossReceived = focusLoss;
+            }
         } catch (android.os.RemoteException e) {
             Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
         }
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index ca57b92..c5b1101 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -265,7 +265,7 @@
      */
     protected void discardAudioFocusOwner() {
         synchronized(mAudioFocusLock) {
-            if (!mFocusStack.empty() && mFocusStack.peek().canDispatchFocus()) {
+            if (!mFocusStack.empty()) {
                 // notify the current focus owner it lost focus after removing it from stack
                 final FocusRequester exFocusOwner = mFocusStack.pop();
                 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS);
@@ -280,13 +280,26 @@
 
     private void notifyTopOfAudioFocusStack() {
         // notify the top of the stack it gained focus
-        if (!mFocusStack.empty() && mFocusStack.peek().canDispatchFocus()) {
+        if (!mFocusStack.empty()) {
             if (canReassignAudioFocus()) {
                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
             }
         }
     }
 
+    /**
+     * Focus is requested, propagate the associated loss throughout the stack.
+     * @param focusGain the new focus gain that will later be added at the top of the stack
+     */
+    private void propagateFocusLossFromGain_syncAf(int focusGain) {
+        // going through the audio focus stack to signal new focus, traversing order doesn't
+        // matter as all entries respond to the same external focus gain
+        Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
+        while(stackIterator.hasNext()) {
+            stackIterator.next().handleExternalFocusGain(focusGain);
+        }
+    }
+
     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
 
     /**
@@ -462,14 +475,14 @@
                 fr.release();
             }
 
-            // notify current top of stack it is losing focus
-            if (!mFocusStack.empty() && mFocusStack.peek().canDispatchFocus()) {
-                mFocusStack.peek().handleExternalFocusGain(focusChangeHint);
-            }
-
             // focus requester might already be somewhere below in the stack, remove it
             removeFocusStackEntry(clientId, false /* signal */);
 
+            // propagate the focus change through the stack
+            if (!mFocusStack.empty()) {
+                propagateFocusLossFromGain_syncAf(focusChangeHint);
+            }
+
             // push focus requester at the top of the audio focus stack
             mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb,
                     clientId, afdh, callingPackageName, Binder.getCallingUid()));