diff --git a/api/current.txt b/api/current.txt index e58aa0c44717c..568baf54ac3c7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20798,6 +20798,7 @@ package android.media { 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 @@ -50825,6 +50826,8 @@ package android.widget { 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 367d471fdc161..6c593160c1361 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -22465,6 +22465,7 @@ package android.media { 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 @@ -54734,6 +54735,8 @@ package android.widget { 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 78c2d504da009..6a75188af74e0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -20894,6 +20894,7 @@ package android.media { 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 @@ -51204,6 +51205,8 @@ package android.widget { 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 b973324dee8c9..7b2efeac75fc8 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 @@ import java.util.Vector; * {@link android.app.Activity#onRestoreInstanceState}.

* Also note that the audio session id (from {@link #getAudioSessionId}) may * change from its previously returned value when the VideoView is restored. + *

+ * By default, VideoView requests audio focus with {@link AudioManager#AUDIOFOCUS_GAIN}. Use + * {@link #setAudioFocusRequest(int)} to change this behavior. + *

+ * 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 @@ public class VideoView extends SurfaceView 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 @@ public class VideoView extends SurfaceView 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); @@ -259,6 +276,41 @@ public class VideoView extends SurfaceView invalidate(); } + /** + * 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 AudioAttributes. + */ + 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.) * @@ -301,8 +353,7 @@ public class VideoView extends SurfaceView 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 @@ public class VideoView extends SurfaceView // 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 @@ public class VideoView extends SurfaceView 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 @@ public class VideoView extends SurfaceView if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(mMediaPlayer); } + if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { + mAudioManager.abandonAudioFocus(null); + } } }; @@ -644,8 +700,9 @@ public class VideoView extends SurfaceView 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 a4f2a7ea49f09..95d354bebc955 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1981,8 +1981,7 @@ public class AudioManager { } /** - * @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 dd9c6a784ff11..6b8a2797ad528 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -366,11 +366,18 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @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 CREATOR diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 1f64b65c821c0..e8ff510242ff4 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 @@ public final class PlaybackActivityMonitor 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;