Support focus requests where ducking of the loser of audio focus
is acceptable for the new audio focus owner.

Change-Id: I965483f12eeb717115a8f6992d8f1ab7fafa4e45
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 47a9004..ad4dd09 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1244,16 +1244,6 @@
     }
 
     /**
-     * Used to indicate a loss of audio focus of unknown duration.
-     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
-     */
-    public static final int AUDIOFOCUS_LOSS = -1;
-    /**
-     * Used to indicate a transient loss of audio focus.
-     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
-     */
-    public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
-    /**
      * Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
      * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
      * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
@@ -1267,6 +1257,35 @@
      * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
      */
     public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
+    /**
+     * @hide
+     * Used to indicate a temporary request of audio focus, anticipated to last a short
+     * amount of time, and where it is acceptable for other audio applications to duck.
+     * Examples of temporary changes are the playback of driving directions where playback of music
+     * in the background is acceptable.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
+     */
+    public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
+    /**
+     * Used to indicate a loss of audio focus of unknown duration.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     */
+    public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
+    /**
+     * Used to indicate a transient loss of audio focus.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     */
+    public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
+    /**
+     * @hide
+     * Used to indicate a transient loss of audio focus where the loser of the audio focus can
+     * duck if it wants to continue playing, as the new focus owner doesn't require others
+     * to be silent.
+     * @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
+     */
+    public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
+            -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
 
     /**
      * Interface definition for a callback to be invoked when the audio focus of the system is
@@ -1282,7 +1301,8 @@
          * behavior to adopt when losing focus. A music player could for instance elect to duck its
          * music stream for transient focus losses, and pause otherwise.
          * @param focusChange one of {@link AudioManager#AUDIOFOCUS_GAIN},
-         *   {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}.
+         *   {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
+         *   and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
          */
         public void onAudioFocusChanged(int focusChange);
     }
@@ -1308,14 +1328,7 @@
      */
     private FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
             new FocusEventHandlerDelegate();
-    /**
-     * Event id denotes a loss of focus
-     */
-    private static final int AUDIOFOCUS_EVENT_LOSS  = 0;
-    /**
-     * Event id denotes a gain of focus
-     */
-    private static final int AUDIOFOCUS_EVENT_GAIN  = 1;
+
     /**
      * Helper class to handle the forwarding of audio focus events to the appropriate listener
      */
@@ -1416,8 +1429,10 @@
      *  @param streamType the main audio stream type affected by the focus request
      *  @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
      *      is temporary, and focus will be abandonned shortly. Examples of transient requests are
-     *      for the playback of driving directions, or notifications sounds. Use
-     *      {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
+     *      for the playback of driving directions, or notifications sounds.
+     *      Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
+     *      the previous focus owner to keep playing if it ducks its audio output.
+     *      Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
      *      as the playback of a song or a video.
      *  @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
      */
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 75e51f9..1992c93 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1822,7 +1822,7 @@
         public IAudioFocusDispatcher mFocusDispatcher = null;
         public IBinder mSourceRef = null;
         public String mClientId;
-        public int mDurationHint;
+        public int mFocusChangeType;
 
         public FocusStackEntry() {
         }
@@ -1834,7 +1834,7 @@
             mFocusDispatcher = afl;
             mSourceRef = source;
             mClientId = id;
-            mDurationHint = duration;
+            mFocusChangeType = duration;
         }
     }
 
@@ -1851,7 +1851,7 @@
             while(stackIterator.hasNext()) {
                 FocusStackEntry fse = stackIterator.next();
                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
-                        + " -- duration: " +fse.mDurationHint);
+                        + " -- duration: " +fse.mFocusChangeType);
             }
         }
     }
@@ -1953,7 +1953,7 @@
 
 
     /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
-    public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId) {
         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
         // the main stream type for the audio focus request is currently not used. It may
@@ -1970,7 +1970,7 @@
 
         synchronized(mFocusStack) {
             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
-                mFocusStack.peek().mDurationHint = durationHint;
+                mFocusStack.peek().mFocusChangeType = focusChangeHint;
                 // if focus is already owned by this client, don't do anything
                 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
             }
@@ -1979,9 +1979,7 @@
             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
                 try {
                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
-                            (durationHint == AudioManager.AUDIOFOCUS_GAIN) ?
-                                    AudioManager.AUDIOFOCUS_LOSS :
-                                        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
+                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
                             mFocusStack.peek().mClientId);
                 } catch (RemoteException e) {
                     Log.e(TAG, " Failure to signal loss of focus due to "+ e);
@@ -1990,7 +1988,7 @@
             }
 
             // push focus requester at the top of the audio focus stack
-            mFocusStack.push(new FocusStackEntry(mainStreamType, durationHint, false, fd, cb,
+            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
                     clientId));
         }//synchronized(mFocusStack)