VideoView: option for audio focus, support for AudioAttributes

Add API for VideoView to select whether it uses audio focus during
  playback, and how.
Add support for AudioAttributes

Test: cts-tradefed run cts -m CtsWidgetTestCases -t android.widget.cts.VideoViewTest
Bug 30955183
Bug 30258418

Change-Id: I581d32c79c78b8197ded2319e0d5bfdc35b93c5e
diff --git a/api/current.txt b/api/current.txt
index 67cdc9c..e108058 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20797,6 +20797,7 @@
     field public static final int AUDIOFOCUS_LOSS = -1; // 0xffffffff
     field public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; // 0xfffffffe
     field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
+    field public static final int AUDIOFOCUS_NONE = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
     field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
@@ -50802,6 +50803,8 @@
     method public int resolveAdjustedSize(int, int);
     method public void resume();
     method public void seekTo(int);
+    method public void setAudioAttributes(android.media.AudioAttributes);
+    method public void setAudioFocusRequest(int);
     method public void setMediaController(android.widget.MediaController);
     method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
     method public void setOnErrorListener(android.media.MediaPlayer.OnErrorListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index dcd2103..f52ac42 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22461,6 +22461,7 @@
     field public static final int AUDIOFOCUS_LOSS = -1; // 0xffffffff
     field public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; // 0xfffffffe
     field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
+    field public static final int AUDIOFOCUS_NONE = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
     field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
@@ -54708,6 +54709,8 @@
     method public int resolveAdjustedSize(int, int);
     method public void resume();
     method public void seekTo(int);
+    method public void setAudioAttributes(android.media.AudioAttributes);
+    method public void setAudioFocusRequest(int);
     method public void setMediaController(android.widget.MediaController);
     method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
     method public void setOnErrorListener(android.media.MediaPlayer.OnErrorListener);
diff --git a/api/test-current.txt b/api/test-current.txt
index 319d9f3..d575dce 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20893,6 +20893,7 @@
     field public static final int AUDIOFOCUS_LOSS = -1; // 0xffffffff
     field public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; // 0xfffffffe
     field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
+    field public static final int AUDIOFOCUS_NONE = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
     field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
@@ -51181,6 +51182,8 @@
     method public int resolveAdjustedSize(int, int);
     method public void resume();
     method public void seekTo(int);
+    method public void setAudioAttributes(android.media.AudioAttributes);
+    method public void setAudioFocusRequest(int);
     method public void setMediaController(android.widget.MediaController);
     method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
     method public void setOnErrorListener(android.media.MediaPlayer.OnErrorListener);
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index b973324..7b2efea 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -16,11 +16,13 @@
 
 package android.widget;
 
+import android.annotation.NonNull;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.Cea708CaptionRenderer;
 import android.media.ClosedCaptionRenderer;
@@ -67,6 +69,14 @@
  * {@link android.app.Activity#onRestoreInstanceState}.<p>
  * Also note that the audio session id (from {@link #getAudioSessionId}) may
  * change from its previously returned value when the VideoView is restored.
+ * <p>
+ * By default, VideoView requests audio focus with {@link AudioManager#AUDIOFOCUS_GAIN}. Use
+ * {@link #setAudioFocusRequest(int)} to change this behavior.
+ * <p>
+ * The default {@link AudioAttributes} used during playback have a usage of
+ * {@link AudioAttributes#USAGE_MEDIA} and a content type of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, use {@link #setAudioAttributes(AudioAttributes)} to
+ * modify them.
  */
 public class VideoView extends SurfaceView
         implements MediaPlayerControl, SubtitleController.Anchor {
@@ -113,6 +123,9 @@
     private boolean mCanPause;
     private boolean mCanSeekBack;
     private boolean mCanSeekForward;
+    private AudioManager mAudioManager;
+    private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
+    private AudioAttributes mAudioAttributes;
 
     /** Subtitle rendering widget overlaid on top of the video. */
     private RenderingWidget mSubtitleWidget;
@@ -138,6 +151,10 @@
         mVideoWidth = 0;
         mVideoHeight = 0;
 
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mAudioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA)
+                .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build();
+
         getHolder().addCallback(mSHCallback);
         getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
 
@@ -260,6 +277,41 @@
     }
 
     /**
+     * Sets which type of audio focus will be requested during the playback, or configures playback
+     * to not request audio focus. Valid values for focus requests are
+     * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
+     * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
+     * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. Or use
+     * {@link AudioManager#AUDIOFOCUS_NONE} to express that audio focus should not be
+     * requested when playback starts. You can for instance use this when playing a silent animation
+     * through this class, and you don't want to affect other audio applications playing in the
+     * background.
+     * @param focusGain the type of audio focus gain that will be requested, or
+     *    {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during playback.
+     */
+    public void setAudioFocusRequest(int focusGain) {
+        if (focusGain != AudioManager.AUDIOFOCUS_NONE
+                && focusGain != AudioManager.AUDIOFOCUS_GAIN
+                && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
+                && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
+                && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) {
+            throw new IllegalArgumentException("Illegal audio focus type " + focusGain);
+        }
+        mAudioFocusType = focusGain;
+    }
+
+    /**
+     * Sets the {@link AudioAttributes} to be used during the playback of the video.
+     * @param attributes non-null <code>AudioAttributes</code>.
+     */
+    public void setAudioAttributes(@NonNull AudioAttributes attributes) {
+        if (attributes == null) {
+            throw new IllegalArgumentException("Illegal null AudioAttributes");
+        }
+        mAudioAttributes = attributes;
+    }
+
+    /**
      * Adds an external subtitle source file (from the provided input stream.)
      *
      * Note that a single external subtitle source may contain multiple or no
@@ -301,8 +353,7 @@
             mMediaPlayer = null;
             mCurrentState = STATE_IDLE;
             mTargetState  = STATE_IDLE;
-            AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-            am.abandonAudioFocus(null);
+            mAudioManager.abandonAudioFocus(null);
         }
     }
 
@@ -315,8 +366,10 @@
         // called start() previously
         release(false);
 
-        AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+        if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
+            // TODO this should have a focus listener
+            mAudioManager.requestAudioFocus(null, mAudioAttributes, mAudioFocusType, 0 /*flags*/);
+        }
 
         try {
             mMediaPlayer = new MediaPlayer();
@@ -345,7 +398,7 @@
             mCurrentBufferPercentage = 0;
             mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
             mMediaPlayer.setDisplay(mSurfaceHolder);
-            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mMediaPlayer.setAudioAttributes(mAudioAttributes);
             mMediaPlayer.setScreenOnWhilePlaying(true);
             mMediaPlayer.prepareAsync();
 
@@ -482,6 +535,9 @@
             if (mOnCompletionListener != null) {
                 mOnCompletionListener.onCompletion(mMediaPlayer);
             }
+            if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
+                mAudioManager.abandonAudioFocus(null);
+            }
         }
     };
 
@@ -644,8 +700,9 @@
             if (cleartargetstate) {
                 mTargetState  = STATE_IDLE;
             }
-            AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-            am.abandonAudioFocus(null);
+            if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
+                mAudioManager.abandonAudioFocus(null);
+            }
         }
     }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a4f2a7e..95d354b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1981,8 +1981,7 @@
     }
 
     /**
-     * @hide
-     * Used to indicate no audio focus has been gained or lost.
+     * Used to indicate no audio focus has been gained or lost, or requested.
      */
     public static final int AUDIOFOCUS_NONE = 0;
 
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index dd9c6a7..6b8a279 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -366,11 +366,18 @@
      * @param pw
      */
     public void dump(PrintWriter pw) {
-        pw.println("  ID:" + mPlayerIId
-                + " -- type:" + toLogFriendlyPlayerType(mPlayerType)
-                + " -- u/pid:" + mClientUid +"/" + mClientPid
-                + " -- state:" + toLogFriendlyPlayerState(mPlayerState)
-                + " -- attr:" + mPlayerAttr);
+        pw.println("  " + toLogFriendlyString(this));
+    }
+
+    /**
+     * @hide
+     */
+    public static String toLogFriendlyString(AudioPlaybackConfiguration apc) {
+        return new String("ID:" + apc.mPlayerIId
+                + " -- type:" + toLogFriendlyPlayerType(apc.mPlayerType)
+                + " -- u/pid:" + apc.mClientUid +"/" + apc.mClientPid
+                + " -- state:" + toLogFriendlyPlayerState(apc.mPlayerState)
+                + " -- attr:" + apc.mPlayerAttr);
     }
 
     public static final Parcelable.Creator<AudioPlaybackConfiguration> CREATOR
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 1f64b65..e8ff510 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -47,6 +47,7 @@
         implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer {
 
     public final static String TAG = "AudioService.PlaybackActivityMonitor";
+
     private final static boolean DEBUG = false;
     private final static int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;