summaryrefslogtreecommitdiff
path: root/android/media
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2018-04-15 00:41:15 -0400
committerJustin Klaassen <justinklaassen@google.com>2018-04-15 00:41:15 -0400
commitb8042fc9b036db0a6692ca853428fc6ab1e60892 (patch)
tree82669ea5d75238758e22d379a42baeada526219e /android/media
parent4d01eeaffaa720e4458a118baa137a11614f00f7 (diff)
downloadandroid-28-b8042fc9b036db0a6692ca853428fc6ab1e60892.tar.gz
/google/data/ro/projects/android/fetch_artifact \ --bid 4719250 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4719250.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: I9ec0a12c9251b8449dba0d86b0cfdbcca16b0a7c
Diffstat (limited to 'android/media')
-rw-r--r--android/media/AudioAttributes.java7
-rw-r--r--android/media/AudioFocusRequest.java2
-rw-r--r--android/media/AudioFormat.java5
-rw-r--r--android/media/AudioManager.java34
-rw-r--r--android/media/AudioPlaybackConfiguration.java2
-rw-r--r--android/media/AudioPresentation.java9
-rw-r--r--android/media/AudioRecord.java1
-rw-r--r--android/media/BufferingParams.java2
-rw-r--r--android/media/ExifInterface.java92
-rw-r--r--android/media/Image.java7
-rw-r--r--android/media/ImageReader.java13
-rw-r--r--android/media/ImageWriter.java17
-rw-r--r--android/media/MediaCodec.java7
-rw-r--r--android/media/MediaCodecInfo.java2
-rw-r--r--android/media/MediaMetadataRetriever.java9
-rw-r--r--android/media/MediaPlayer.java98
-rw-r--r--android/media/MediaRecorder.java1
-rw-r--r--android/media/MediaTimestamp.java9
-rw-r--r--android/media/PlaybackParams.java3
-rw-r--r--android/media/PlayerBase.java80
-rw-r--r--android/media/VolumeShaper.java2
-rw-r--r--android/media/audiofx/AudioEffect.java23
-rw-r--r--android/media/session/MediaController.java2
-rw-r--r--android/media/session/MediaSessionManager.java14
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 {