diff options
Diffstat (limited to 'android/media')
24 files changed, 324 insertions, 117 deletions
diff --git a/android/media/AudioAttributes.java b/android/media/AudioAttributes.java index d4326583..9152ff2e 100644 --- a/android/media/AudioAttributes.java +++ b/android/media/AudioAttributes.java @@ -180,6 +180,7 @@ public final class AudioAttributes implements Parcelable { * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES * if applicable, as well as audioattributes.proto. * Also consider adding them to <aaudio/AAudio.h> for the NDK. + * Also consider adding them to UsageTypeConverter for service dump and etc. */ /** @@ -249,9 +250,10 @@ public final class AudioAttributes implements Parcelable { SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, SUPPRESSIBLE_MEDIA); SUPPRESSIBLE_USAGES.put(USAGE_GAME, SUPPRESSIBLE_MEDIA); SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT, SUPPRESSIBLE_MEDIA); + /** default volume assignment is STREAM_MUSIC, handle unknown usage as media */ + SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN, SUPPRESSIBLE_MEDIA); SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_SYSTEM); SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION, SUPPRESSIBLE_SYSTEM); - SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN, SUPPRESSIBLE_SYSTEM); } /** @@ -1056,8 +1058,7 @@ public final class AudioAttributes implements Parcelable { case USAGE_ASSISTANCE_ACCESSIBILITY: return AudioSystem.STREAM_ACCESSIBILITY; case USAGE_UNKNOWN: - return fromGetVolumeControlStream ? - AudioManager.USE_DEFAULT_STREAM_TYPE : AudioSystem.STREAM_MUSIC; + return AudioSystem.STREAM_MUSIC; default: if (fromGetVolumeControlStream) { throw new IllegalArgumentException("Unknown usage value " + aa.getUsage() + diff --git a/android/media/AudioFocusRequest.java b/android/media/AudioFocusRequest.java index 7104dad4..fe89b89d 100644 --- a/android/media/AudioFocusRequest.java +++ b/android/media/AudioFocusRequest.java @@ -19,6 +19,7 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.media.AudioManager.OnAudioFocusChangeListener; import android.os.Bundle; import android.os.Handler; @@ -262,6 +263,7 @@ public final class AudioFocusRequest { * Returns the focus change listener set for this {@code AudioFocusRequest}. * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set. */ + @TestApi public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() { return mFocusListener; } diff --git a/android/media/AudioFormat.java b/android/media/AudioFormat.java index f98480b2..d0a2c98f 100644 --- a/android/media/AudioFormat.java +++ b/android/media/AudioFormat.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -437,6 +438,7 @@ public final class AudioFormat implements Parcelable { * @param mask a combination of the CHANNEL_IN_* definitions, even CHANNEL_IN_DEFAULT * @return number of channels for the mask */ + @TestApi public static int channelCountFromInChannelMask(int mask) { return Integer.bitCount(mask); } @@ -446,6 +448,7 @@ public final class AudioFormat implements Parcelable { * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT * @return number of channels for the mask */ + @TestApi public static int channelCountFromOutChannelMask(int mask) { return Integer.bitCount(mask); } @@ -492,6 +495,7 @@ public final class AudioFormat implements Parcelable { // CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL /** @hide */ + @TestApi public static int getBytesPerSample(int audioFormat) { switch (audioFormat) { @@ -562,6 +566,7 @@ public final class AudioFormat implements Parcelable { } /** @hide */ + @TestApi public static boolean isEncodingLinearPcm(int audioFormat) { switch (audioFormat) { diff --git a/android/media/AudioManager.java b/android/media/AudioManager.java index aeef2158..fdb7499b 100644 --- a/android/media/AudioManager.java +++ b/android/media/AudioManager.java @@ -63,6 +63,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -4786,6 +4787,21 @@ public class AudioManager { } /** + * Add {@link MicrophoneInfo} by device information while filtering certain types. + */ + private void addMicrophonesFromAudioDeviceInfo(ArrayList<MicrophoneInfo> microphones, + HashSet<Integer> filterTypes) { + AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS); + for (AudioDeviceInfo device : devices) { + if (filterTypes.contains(device.getType())) { + continue; + } + MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device); + microphones.add(microphone); + } + } + + /** * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics * of all available microphones. The list is empty when no microphones are available * on the device. An error during the query will result in an IOException being thrown. @@ -4796,21 +4812,17 @@ public class AudioManager { public List<MicrophoneInfo> getMicrophones() throws IOException { ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>(); int status = AudioSystem.getMicrophones(microphones); + HashSet<Integer> filterTypes = new HashSet<>(); + filterTypes.add(AudioDeviceInfo.TYPE_TELEPHONY); if (status != AudioManager.SUCCESS) { - // fail and bail! + // fail and populate microphones with unknown characteristics by device information. Log.e(TAG, "getMicrophones failed:" + status); - return new ArrayList<MicrophoneInfo>(); // Always return a list. + addMicrophonesFromAudioDeviceInfo(microphones, filterTypes); + return microphones; } setPortIdForMicrophones(microphones); - AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS); - for (AudioDeviceInfo device : devices) { - if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC || - device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) { - continue; - } - MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device); - microphones.add(microphone); - } + filterTypes.add(AudioDeviceInfo.TYPE_BUILTIN_MIC); + addMicrophonesFromAudioDeviceInfo(microphones, filterTypes); return microphones; } diff --git a/android/media/AudioPlaybackConfiguration.java b/android/media/AudioPlaybackConfiguration.java index 8a36f91c..7dfdb20a 100644 --- a/android/media/AudioPlaybackConfiguration.java +++ b/android/media/AudioPlaybackConfiguration.java @@ -43,6 +43,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { /** @hide */ public static final int PLAYER_PIID_INVALID = -1; /** @hide */ + public static final int PLAYER_PIID_UNASSIGNED = 0; + /** @hide */ public static final int PLAYER_UPID_INVALID = -1; // information about the implementation diff --git a/android/media/AudioPresentation.java b/android/media/AudioPresentation.java index e39cb7db..ce71436b 100644 --- a/android/media/AudioPresentation.java +++ b/android/media/AudioPresentation.java @@ -18,8 +18,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; +import android.annotation.TestApi; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -94,7 +93,7 @@ public final class AudioPresentation { /** * @hide */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @TestApi public AudioPresentation(int presentationId, int programId, @NonNull Map<String, String> labels, @@ -119,7 +118,7 @@ public final class AudioPresentation { * decoder. Presentation id is typically sequential, but does not have to be. * @hide */ - @VisibleForTesting + @TestApi public int getPresentationId() { return mPresentationId; } @@ -129,7 +128,7 @@ public final class AudioPresentation { * Program id can be used to further uniquely identify the presentation to a decoder. * @hide */ - @VisibleForTesting + @TestApi public int getProgramId() { return mProgramId; } diff --git a/android/media/AudioRecord.java b/android/media/AudioRecord.java index 4f0dccb8..6b35dd4c 100644 --- a/android/media/AudioRecord.java +++ b/android/media/AudioRecord.java @@ -1628,7 +1628,6 @@ public class AudioRecord implements AudioRouting int status = native_get_active_microphones(activeMicrophones); if (status != AudioManager.SUCCESS) { Log.e(TAG, "getActiveMicrophones failed:" + status); - return new ArrayList<MicrophoneInfo>(); } AudioManager.setPortIdForMicrophones(activeMicrophones); diff --git a/android/media/BufferingParams.java b/android/media/BufferingParams.java index 521e8975..aaae5e7b 100644 --- a/android/media/BufferingParams.java +++ b/android/media/BufferingParams.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -63,6 +64,7 @@ import java.lang.annotation.RetentionPolicy; * <p>Users should use {@link Builder} to change {@link BufferingParams}. * @hide */ +@TestApi public final class BufferingParams implements Parcelable { private static final int BUFFERING_NO_MARK = -1; diff --git a/android/media/ExifInterface.java b/android/media/ExifInterface.java index bc0e43b5..78884367 100644 --- a/android/media/ExifInterface.java +++ b/android/media/ExifInterface.java @@ -66,7 +66,7 @@ import libcore.io.Streams; /** * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file. * <p> - * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF. + * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF and HEIF. * <p> * Attribute mutation is supported for JPEG image files. */ @@ -2524,46 +2524,46 @@ public class ExifInterface { private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { - if (mSeekableFileDescriptor != null) { - retriever.setDataSource(mSeekableFileDescriptor); - } else { - retriever.setDataSource(new MediaDataSource() { - long mPosition; - - @Override - public void close() throws IOException {} + retriever.setDataSource(new MediaDataSource() { + long mPosition; - @Override - public int readAt(long position, byte[] buffer, int offset, int size) - throws IOException { - if (size == 0) { - return 0; - } - if (position < 0) { - return -1; - } - if (mPosition != position) { - in.seek(position); - mPosition = position; - } + @Override + public void close() throws IOException {} - int bytesRead = in.read(buffer, offset, size); - if (bytesRead < 0) { - mPosition = -1; // need to seek on next read - return -1; - } - - mPosition += bytesRead; - return bytesRead; + @Override + public int readAt(long position, byte[] buffer, int offset, int size) + throws IOException { + if (size == 0) { + return 0; + } + if (position < 0) { + return -1; + } + if (mPosition != position) { + in.seek(position); + mPosition = position; } - @Override - public long getSize() throws IOException { + int bytesRead = in.read(buffer, offset, size); + if (bytesRead < 0) { + mPosition = -1; // need to seek on next read return -1; } - }); - } + mPosition += bytesRead; + return bytesRead; + } + + @Override + public long getSize() throws IOException { + return -1; + } + }); + + String exifOffsetStr = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET); + String exifLengthStr = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH); String hasImage = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE); String hasVideo = retriever.extractMetadata( @@ -2622,6 +2622,30 @@ public class ExifInterface { ExifAttribute.createUShort(orientation, mExifByteOrder)); } + if (exifOffsetStr != null && exifLengthStr != null) { + int offset = Integer.parseInt(exifOffsetStr); + int length = Integer.parseInt(exifLengthStr); + if (length <= 6) { + throw new IOException("Invalid exif length"); + } + in.seek(offset); + byte[] identifier = new byte[6]; + if (in.read(identifier) != 6) { + throw new IOException("Can't read identifier"); + } + offset += 6; + length -= 6; + if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { + throw new IOException("Invalid identifier"); + } + + byte[] bytes = new byte[length]; + if (in.read(bytes) != length) { + throw new IOException("Can't read exif"); + } + readExifSegment(bytes, IFD_TYPE_PRIMARY); + } + if (DEBUG) { Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation); } diff --git a/android/media/Image.java b/android/media/Image.java index 37c57854..9828275e 100644 --- a/android/media/Image.java +++ b/android/media/Image.java @@ -193,6 +193,13 @@ public abstract class Image implements AutoCloseable { public abstract int getTransform(); /** + * Get the scaling mode associated with this frame. + * @return The scaling mode that needs to be applied for this frame. + * @hide + */ + public abstract int getScalingMode(); + + /** * Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image * intended for GPU and/or hardware access. * <p> diff --git a/android/media/ImageReader.java b/android/media/ImageReader.java index 72d52d3d..8ec0e353 100644 --- a/android/media/ImageReader.java +++ b/android/media/ImageReader.java @@ -871,6 +871,12 @@ public class ImageReader implements AutoCloseable { } @Override + public int getScalingMode() { + throwISEIfImageIsInvalid(); + return mScalingMode; + } + + @Override public HardwareBuffer getHardwareBuffer() { throwISEIfImageIsInvalid(); return nativeGetHardwareBuffer(); @@ -1004,14 +1010,11 @@ public class ImageReader implements AutoCloseable { private long mNativeBuffer; /** - * This field is set by native code during nativeImageSetup(). + * These fields are set by native code during nativeImageSetup(). */ private long mTimestamp; - - /** - * This field is set by native code during nativeImageSetup(). - */ private int mTransform; + private int mScalingMode; private SurfacePlane[] mPlanes; private int mFormat = ImageFormat.UNKNOWN; diff --git a/android/media/ImageWriter.java b/android/media/ImageWriter.java index 8ee27ae5..397768af 100644 --- a/android/media/ImageWriter.java +++ b/android/media/ImageWriter.java @@ -371,7 +371,7 @@ public class ImageWriter implements AutoCloseable { Rect crop = image.getCropRect(); nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top, - crop.right, crop.bottom, image.getTransform()); + crop.right, crop.bottom, image.getTransform(), image.getScalingMode()); /** * Only remove and cleanup the Images that are owned by this @@ -558,7 +558,7 @@ public class ImageWriter implements AutoCloseable { Rect crop = image.getCropRect(); nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(), image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom, - image.getTransform()); + image.getTransform(), image.getScalingMode()); } /** @@ -676,6 +676,7 @@ public class ImageWriter implements AutoCloseable { private long mTimestamp = DEFAULT_TIMESTAMP; private int mTransform = 0; //Default no transform + private int mScalingMode = 0; //Default frozen scaling mode public WriterSurfaceImage(ImageWriter writer) { mOwner = writer; @@ -721,6 +722,13 @@ public class ImageWriter implements AutoCloseable { } @Override + public int getScalingMode() { + throwISEIfImageIsInvalid(); + + return mScalingMode; + } + + @Override public long getTimestamp() { throwISEIfImageIsInvalid(); @@ -866,11 +874,12 @@ public class ImageWriter implements AutoCloseable { private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi); private synchronized native void nativeQueueInputImage(long nativeCtx, Image image, - long timestampNs, int left, int top, int right, int bottom, int transform); + long timestampNs, int left, int top, int right, int bottom, int transform, + int scalingMode); private synchronized native int nativeAttachAndQueueImage(long nativeCtx, long imageNativeBuffer, int imageFormat, long timestampNs, int left, - int top, int right, int bottom, int transform); + int top, int right, int bottom, int transform, int scalingMode); private synchronized native void cancelImage(long nativeCtx, Image image); diff --git a/android/media/MediaCodec.java b/android/media/MediaCodec.java index e3fba0cd..1f00c782 100644 --- a/android/media/MediaCodec.java +++ b/android/media/MediaCodec.java @@ -3574,6 +3574,7 @@ final public class MediaCodec { private final static int TYPE_YUV = 1; private final int mTransform = 0; //Default no transform + private final int mScalingMode = 0; //Default frozen scaling mode @Override public int getFormat() { @@ -3600,6 +3601,12 @@ final public class MediaCodec { } @Override + public int getScalingMode() { + throwISEIfImageIsInvalid(); + return mScalingMode; + } + + @Override public long getTimestamp() { throwISEIfImageIsInvalid(); return mTimestamp; diff --git a/android/media/MediaCodecInfo.java b/android/media/MediaCodecInfo.java index 2a601f9b..c29300d1 100644 --- a/android/media/MediaCodecInfo.java +++ b/android/media/MediaCodecInfo.java @@ -2971,6 +2971,8 @@ public final class MediaCodecInfo { public static final int AACObjectLD = 23; public static final int AACObjectHE_PS = 29; public static final int AACObjectELD = 39; + /** xHE-AAC (includes USAC) */ + public static final int AACObjectXHE = 42; // from OMX_VIDEO_VP8LEVELTYPE public static final int VP8Level_Version0 = 0x01; diff --git a/android/media/MediaMetadataRetriever.java b/android/media/MediaMetadataRetriever.java index 0955dd63..8ab5ec44 100644 --- a/android/media/MediaMetadataRetriever.java +++ b/android/media/MediaMetadataRetriever.java @@ -890,5 +890,14 @@ public class MediaMetadataRetriever */ public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; + /** + * @hide + */ + public static final int METADATA_KEY_EXIF_OFFSET = 33; + + /** + * @hide + */ + public static final int METADATA_KEY_EXIF_LENGTH = 34; // Add more here... } diff --git a/android/media/MediaPlayer.java b/android/media/MediaPlayer.java index aef31b11..392a1eb0 100644 --- a/android/media/MediaPlayer.java +++ b/android/media/MediaPlayer.java @@ -19,6 +19,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.app.ActivityThread; import android.content.ContentProvider; import android.content.ContentResolver; @@ -1680,6 +1681,7 @@ public class MediaPlayer extends PlayerBase * @hide */ @NonNull + @TestApi public native BufferingParams getBufferingParams(); /** @@ -1696,6 +1698,7 @@ public class MediaPlayer extends PlayerBase * @throws IllegalArgumentException if params is invalid or not supported. * @hide */ + @TestApi public native void setBufferingParams(@NonNull BufferingParams params); /** @@ -2128,7 +2131,13 @@ public class MediaPlayer extends PlayerBase mTimeProvider.close(); mTimeProvider = null; } - mOnSubtitleDataListener = null; + synchronized(this) { + mSubtitleDataListenerDisabled = false; + mExtSubtitleDataListener = null; + mExtSubtitleDataHandler = null; + mOnMediaTimeDiscontinuityListener = null; + mOnMediaTimeDiscontinuityHandler = null; + } // Modular DRM clean up mOnDrmConfigHelper = null; @@ -2699,7 +2708,7 @@ public class MediaPlayer extends PlayerBase private int mSelectedSubtitleTrackIndex = -1; private Vector<InputStream> mOpenSubtitleSources; - private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() { + private final OnSubtitleDataListener mIntSubtitleDataListener = new OnSubtitleDataListener() { @Override public void onSubtitleData(MediaPlayer mp, SubtitleData data) { int index = data.getTrackIndex(); @@ -2725,7 +2734,9 @@ public class MediaPlayer extends PlayerBase } mSelectedSubtitleTrackIndex = -1; } - setOnSubtitleDataListener(null); + synchronized (this) { + mSubtitleDataListenerDisabled = true; + } if (track == null) { return; } @@ -2745,7 +2756,9 @@ public class MediaPlayer extends PlayerBase selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true); } catch (IllegalStateException e) { } - setOnSubtitleDataListener(mSubtitleDataListener); + synchronized (this) { + mSubtitleDataListenerDisabled = false; + } } // no need to select out-of-band tracks } @@ -3304,6 +3317,7 @@ public class MediaPlayer extends PlayerBase private static final int MEDIA_SUBTITLE_DATA = 201; private static final int MEDIA_META_DATA = 202; private static final int MEDIA_DRM_INFO = 210; + private static final int MEDIA_TIME_DISCONTINUITY = 211; private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000; private TimeProvider mTimeProvider; @@ -3514,15 +3528,34 @@ public class MediaPlayer extends PlayerBase return; case MEDIA_SUBTITLE_DATA: - OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener; - if (onSubtitleDataListener == null) { - return; + final OnSubtitleDataListener extSubtitleListener; + final Handler extSubtitleHandler; + synchronized(this) { + if (mSubtitleDataListenerDisabled) { + return; + } + extSubtitleListener = mExtSubtitleDataListener; + extSubtitleHandler = mExtSubtitleDataHandler; } if (msg.obj instanceof Parcel) { Parcel parcel = (Parcel) msg.obj; - SubtitleData data = new SubtitleData(parcel); + final SubtitleData data = new SubtitleData(parcel); parcel.recycle(); - onSubtitleDataListener.onSubtitleData(mMediaPlayer, data); + + mIntSubtitleDataListener.onSubtitleData(mMediaPlayer, data); + + if (extSubtitleListener != null) { + if (extSubtitleHandler == null) { + extSubtitleListener.onSubtitleData(mMediaPlayer, data); + } else { + extSubtitleHandler.post(new Runnable() { + @Override + public void run() { + extSubtitleListener.onSubtitleData(mMediaPlayer, data); + } + }); + } + } } return; @@ -3553,6 +3586,43 @@ public class MediaPlayer extends PlayerBase } return; + case MEDIA_TIME_DISCONTINUITY: + final OnMediaTimeDiscontinuityListener mediaTimeListener; + final Handler mediaTimeHandler; + synchronized(this) { + mediaTimeListener = mOnMediaTimeDiscontinuityListener; + mediaTimeHandler = mOnMediaTimeDiscontinuityHandler; + } + if (mediaTimeListener == null) { + return; + } + if (msg.obj instanceof Parcel) { + Parcel parcel = (Parcel) msg.obj; + parcel.setDataPosition(0); + long anchorMediaUs = parcel.readLong(); + long anchorRealUs = parcel.readLong(); + float playbackRate = parcel.readFloat(); + parcel.recycle(); + final MediaTimestamp timestamp; + if (anchorMediaUs != -1 && anchorRealUs != -1) { + timestamp = new MediaTimestamp( + anchorMediaUs /*Us*/, anchorRealUs * 1000 /*Ns*/, playbackRate); + } else { + timestamp = MediaTimestamp.TIMESTAMP_UNKNOWN; + } + if (mediaTimeHandler == null) { + mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp); + } else { + mediaTimeHandler.post(new Runnable() { + @Override + public void run() { + mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp); + } + }); + } + } + return; + default: Log.e(TAG, "Unknown message type " + msg.what); return; @@ -3877,13 +3947,15 @@ public class MediaPlayer extends PlayerBase private void setOnSubtitleDataListenerInt( @Nullable OnSubtitleDataListener listener, @Nullable Handler handler) { synchronized (this) { - mOnSubtitleDataListener = listener; - mOnSubtitleDataHandler = handler; + mExtSubtitleDataListener = listener; + mExtSubtitleDataHandler = handler; } } - private OnSubtitleDataListener mOnSubtitleDataListener; - private Handler mOnSubtitleDataHandler; + private boolean mSubtitleDataListenerDisabled; + /** External OnSubtitleDataListener, the one set by {@link #setOnSubtitleDataListener}. */ + private OnSubtitleDataListener mExtSubtitleDataListener; + private Handler mExtSubtitleDataHandler; /** * Interface definition of a callback to be invoked when discontinuity in the normal progression diff --git a/android/media/MediaRecorder.java b/android/media/MediaRecorder.java index 90b6bff6..82d64f30 100644 --- a/android/media/MediaRecorder.java +++ b/android/media/MediaRecorder.java @@ -1434,7 +1434,6 @@ public class MediaRecorder implements AudioRouting int status = native_getActiveMicrophones(activeMicrophones); if (status != AudioManager.SUCCESS) { Log.e(TAG, "getActiveMicrophones failed:" + status); - return new ArrayList<MicrophoneInfo>(); } AudioManager.setPortIdForMicrophones(activeMicrophones); diff --git a/android/media/MediaTimestamp.java b/android/media/MediaTimestamp.java index 938dd14a..dd43b4e0 100644 --- a/android/media/MediaTimestamp.java +++ b/android/media/MediaTimestamp.java @@ -98,4 +98,13 @@ public final class MediaTimestamp && (this.nanoTime == that.nanoTime) && (this.clockRate == that.clockRate); } + + @Override + public String toString() { + return getClass().getName() + + "{AnchorMediaTimeUs=" + mediaTimeUs + + " AnchorSystemNanoTime=" + nanoTime + + " clockRate=" + clockRate + + "}"; + } } diff --git a/android/media/PlaybackParams.java b/android/media/PlaybackParams.java index 938a953a..b85e4d01 100644 --- a/android/media/PlaybackParams.java +++ b/android/media/PlaybackParams.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -151,6 +152,7 @@ public final class PlaybackParams implements Parcelable { * @param audioStretchMode * @return this <code>PlaybackParams</code> instance. */ + @TestApi public PlaybackParams setAudioStretchMode(@AudioStretchMode int audioStretchMode) { mAudioStretchMode = audioStretchMode; mSet |= SET_AUDIO_STRETCH_MODE; @@ -163,6 +165,7 @@ public final class PlaybackParams implements Parcelable { * @return audio stretch mode * @throws IllegalStateException if the audio stretch mode is not set. */ + @TestApi public @AudioStretchMode int getAudioStretchMode() { if ((mSet & SET_AUDIO_STRETCH_MODE) == 0) { throw new IllegalStateException("audio stretch mode not set"); diff --git a/android/media/PlayerBase.java b/android/media/PlayerBase.java index 80049ba5..7c6367e8 100644 --- a/android/media/PlayerBase.java +++ b/android/media/PlayerBase.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; @@ -58,20 +59,29 @@ public abstract class PlayerBase { protected float mRightVolume = 1.0f; protected float mAuxEffectSendLevel = 0.0f; + // NEVER call into AudioService (see getService()) with mLock held: PlayerBase can run in + // the same process as AudioService, which can synchronously call back into this class, + // causing deadlocks between the two + private final Object mLock = new Object(); + // for AppOps - private IAppOpsService mAppOps; // may be null + private @Nullable IAppOpsService mAppOps; private IAppOpsCallback mAppOpsCallback; - private boolean mHasAppOpsPlayAudio = true; // sync'd on mLock - private final Object mLock = new Object(); + @GuardedBy("mLock") + private boolean mHasAppOpsPlayAudio = true; private final int mImplType; // uniquely identifies the Player Interface throughout the system (P I Id) - private int mPlayerIId; + private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_UNASSIGNED; - private int mState; // sync'd on mLock - private int mStartDelayMs = 0; // sync'd on mLock - private float mPanMultiplierL = 1.0f; // sync'd on mLock - private float mPanMultiplierR = 1.0f; // sync'd on mLock + @GuardedBy("mLock") + private int mState; + @GuardedBy("mLock") + private int mStartDelayMs = 0; + @GuardedBy("mLock") + private float mPanMultiplierL = 1.0f; + @GuardedBy("mLock") + private float mPanMultiplierR = 1.0f; /** * Constructor. Must be given audio attributes, as they are required for AppOps. @@ -134,16 +144,24 @@ public abstract class PlayerBase { } } - void baseStart() { - if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); } + private void updateState(int state) { + final int piid; + synchronized (mLock) { + mState = state; + piid = mPlayerIId; + } try { - synchronized (mLock) { - mState = AudioPlaybackConfiguration.PLAYER_STATE_STARTED; - getService().playerEvent(mPlayerIId, mState); - } + getService().playerEvent(piid, state); } catch (RemoteException e) { - Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e); + Log.e(TAG, "Error talking to audio service, " + + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state) + + " state will not be tracked for piid=" + piid, e); } + } + + void baseStart() { + if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); } + updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); synchronized (mLock) { if (isRestricted_sync()) { playerSetVolume(true/*muting*/,0, 0); @@ -165,26 +183,12 @@ public abstract class PlayerBase { void basePause() { if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); } - try { - synchronized (mLock) { - mState = AudioPlaybackConfiguration.PLAYER_STATE_PAUSED; - getService().playerEvent(mPlayerIId, mState); - } - } catch (RemoteException e) { - Log.e(TAG, "Error talking to audio service, PAUSED state will not be tracked", e); - } + updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED); } void baseStop() { if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); } - try { - synchronized (mLock) { - mState = AudioPlaybackConfiguration.PLAYER_STATE_STOPPED; - getService().playerEvent(mPlayerIId, mState); - } - } catch (RemoteException e) { - Log.e(TAG, "Error talking to audio service, STOPPED state will not be tracked", e); - } + updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED); } void baseSetPan(float pan) { @@ -228,12 +232,16 @@ public abstract class PlayerBase { */ void baseRelease() { if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId + " state=" + mState); } + boolean releasePlayer = false; + synchronized (mLock) { + if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) { + releasePlayer = true; + mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED; + } + } try { - synchronized (mLock) { - if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) { - getService().releasePlayer(mPlayerIId); - mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED; - } + if (releasePlayer) { + getService().releasePlayer(mPlayerIId); } } catch (RemoteException e) { Log.e(TAG, "Error talking to audio service, the player will still be tracked", e); diff --git a/android/media/VolumeShaper.java b/android/media/VolumeShaper.java index 30687065..b654214c 100644 --- a/android/media/VolumeShaper.java +++ b/android/media/VolumeShaper.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -843,6 +844,7 @@ public final class VolumeShaper implements AutoCloseable { * @return the same {@code Builder} instance. * @throws IllegalArgumentException if flag is not recognized. */ + @TestApi public @NonNull Builder setOptionFlags(@OptionFlag int optionFlags) { if ((optionFlags & ~OPTION_FLAG_PUBLIC_ALL) != 0) { throw new IllegalArgumentException("invalid bits in flag: " + optionFlags); diff --git a/android/media/audiofx/AudioEffect.java b/android/media/audiofx/AudioEffect.java index 21d68737..24c595f5 100644 --- a/android/media/audiofx/AudioEffect.java +++ b/android/media/audiofx/AudioEffect.java @@ -18,6 +18,7 @@ package android.media.audiofx; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.TestApi; import android.app.ActivityThread; import android.os.Handler; import android.os.Looper; @@ -133,9 +134,10 @@ public class AudioEffect { .fromString("7261676f-6d75-7369-6364-28e2fd3ac39e"); /** - * Null effect UUID. Used when the UUID for effect type of + * Null effect UUID. See {@link AudioEffect(UUID, UUID, int, int)} for use. * @hide */ + @TestApi public static final UUID EFFECT_TYPE_NULL = UUID .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210"); @@ -492,6 +494,7 @@ public class AudioEffect { * @return true if the device implements the specified effect type, false otherwise. * @hide */ + @TestApi public static boolean isEffectTypeAvailable(UUID type) { AudioEffect.Descriptor[] desc = AudioEffect.queryEffects(); if (desc == null) { @@ -544,6 +547,7 @@ public class AudioEffect { * @throws IllegalStateException * @hide */ + @TestApi public int setParameter(byte[] param, byte[] value) throws IllegalStateException { checkState("setParameter()"); @@ -556,6 +560,7 @@ public class AudioEffect { * @see #setParameter(byte[], byte[]) * @hide */ + @TestApi public int setParameter(int param, int value) throws IllegalStateException { byte[] p = intToByteArray(param); byte[] v = intToByteArray(value); @@ -569,6 +574,7 @@ public class AudioEffect { * @see #setParameter(byte[], byte[]) * @hide */ + @TestApi public int setParameter(int param, short value) throws IllegalStateException { byte[] p = intToByteArray(param); @@ -583,6 +589,7 @@ public class AudioEffect { * @see #setParameter(byte[], byte[]) * @hide */ + @TestApi public int setParameter(int param, byte[] value) throws IllegalStateException { byte[] p = intToByteArray(param); @@ -596,6 +603,7 @@ public class AudioEffect { * @see #setParameter(byte[], byte[]) * @hide */ + @TestApi public int setParameter(int[] param, int[] value) throws IllegalStateException { if (param.length > 2 || value.length > 2) { @@ -647,6 +655,7 @@ public class AudioEffect { * @see #setParameter(byte[], byte[]) * @hide */ + @TestApi public int setParameter(int[] param, byte[] value) throws IllegalStateException { if (param.length > 2) { @@ -675,6 +684,7 @@ public class AudioEffect { * @throws IllegalStateException * @hide */ + @TestApi public int getParameter(byte[] param, byte[] value) throws IllegalStateException { checkState("getParameter()"); @@ -688,6 +698,7 @@ public class AudioEffect { * @see #getParameter(byte[], byte[]) * @hide */ + @TestApi public int getParameter(int param, byte[] value) throws IllegalStateException { byte[] p = intToByteArray(param); @@ -703,6 +714,7 @@ public class AudioEffect { * In case of success, returns the number of meaningful integers in value array. * @hide */ + @TestApi public int getParameter(int param, int[] value) throws IllegalStateException { if (value.length > 2) { @@ -734,6 +746,7 @@ public class AudioEffect { * In case of success, returns the number of meaningful short integers in value array. * @hide */ + @TestApi public int getParameter(int param, short[] value) throws IllegalStateException { if (value.length > 2) { @@ -799,6 +812,7 @@ public class AudioEffect { * In case of success, returns the number of meaningful short integers in value array. * @hide */ + @TestApi public int getParameter(int[] param, short[] value) throws IllegalStateException { if (param.length > 2 || value.length > 2) { @@ -938,6 +952,7 @@ public class AudioEffect { * @param listener * @hide */ + @TestApi public void setParameterListener(OnParameterChangeListener listener) { synchronized (mListenerLock) { mParameterChangeListener = listener; @@ -999,6 +1014,7 @@ public class AudioEffect { * when a parameter is changed in the effect engine by the controlling application. * @hide */ + @TestApi public interface OnParameterChangeListener { /** * Called on the listener to notify it that a parameter value has changed. @@ -1291,6 +1307,7 @@ public class AudioEffect { /** * @hide */ + @TestApi public static boolean isError(int status) { return (status < 0); } @@ -1298,6 +1315,7 @@ public class AudioEffect { /** * @hide */ + @TestApi public static int byteArrayToInt(byte[] valueBuf) { return byteArrayToInt(valueBuf, 0); @@ -1316,6 +1334,7 @@ public class AudioEffect { /** * @hide */ + @TestApi public static byte[] intToByteArray(int value) { ByteBuffer converter = ByteBuffer.allocate(4); converter.order(ByteOrder.nativeOrder()); @@ -1326,6 +1345,7 @@ public class AudioEffect { /** * @hide */ + @TestApi public static short byteArrayToShort(byte[] valueBuf) { return byteArrayToShort(valueBuf, 0); } @@ -1343,6 +1363,7 @@ public class AudioEffect { /** * @hide */ + @TestApi public static byte[] shortToByteArray(short value) { ByteBuffer converter = ByteBuffer.allocate(2); converter.order(ByteOrder.nativeOrder()); diff --git a/android/media/session/MediaController.java b/android/media/session/MediaController.java index f16804c9..84f85e78 100644 --- a/android/media/session/MediaController.java +++ b/android/media/session/MediaController.java @@ -531,7 +531,7 @@ public final class MediaController { * * @param state The new playback state of the session */ - public void onPlaybackStateChanged(@NonNull PlaybackState state) { + public void onPlaybackStateChanged(@Nullable PlaybackState state) { } /** diff --git a/android/media/session/MediaSessionManager.java b/android/media/session/MediaSessionManager.java index 519af1ba..fbc14384 100644 --- a/android/media/session/MediaSessionManager.java +++ b/android/media/session/MediaSessionManager.java @@ -47,6 +47,7 @@ import android.view.KeyEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -342,12 +343,16 @@ public final class MediaSessionManager { } /** - * Returns whether the app is trusted. + * Checks whether the remote user is a trusted app. * <p> * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL * permission or has an enabled notification listener. * - * @param userInfo The remote user info + * @param userInfo The remote user info from either + * {@link MediaSession#getCurrentControllerInfo()} or + * {@link MediaBrowserService#getCurrentBrowserInfo()}. + * @return {@code true} if the remote user is trusted and its package name matches with the UID. + * {@code false} otherwise. */ public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) { if (userInfo.getPackageName() == null) { @@ -814,6 +819,11 @@ public final class MediaSessionManager { && mPid == otherUserInfo.mPid && mUid == otherUserInfo.mUid; } + + @Override + public int hashCode() { + return Objects.hash(mPackageName, mPid, mUid); + } } private static final class SessionsChangedWrapper { |