From 4217cf85c20565a3446a662a7f07f26137b26b7f Mon Sep 17 00:00:00 2001 From: Justin Klaassen Date: Thu, 30 Nov 2017 18:18:21 -0500 Subject: Import Android SDK Platform P [4477446] /google/data/ro/projects/android/fetch_artifact \ --bid 4477446 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4477446.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: If0559643d7c328e36aafca98f0c114641d33642c --- android/media/ImageReader.java | 1 - android/media/MediaDrm.java | 68 ++++++++------- android/media/MediaFormat.java | 8 +- android/media/MediaMetadata.java | 66 ++++++++++++++ android/media/MediaMuxer.java | 16 ++-- android/media/MediaRecorder.java | 165 ++++++++++++++++++++++++++++++++++- android/media/tv/TvInputManager.java | 4 + 7 files changed, 285 insertions(+), 43 deletions(-) (limited to 'android/media') diff --git a/android/media/ImageReader.java b/android/media/ImageReader.java index c78c99f7..10195805 100644 --- a/android/media/ImageReader.java +++ b/android/media/ImageReader.java @@ -640,7 +640,6 @@ public class ImageReader implements AutoCloseable { * The ImageReader continues to be usable after this call, but may need to reallocate buffers * when more buffers are needed for rendering. *

- * @hide */ public void discardFreeBuffers() { synchronized (mCloseLock) { diff --git a/android/media/MediaDrm.java b/android/media/MediaDrm.java index 1feea890..12e5744d 100644 --- a/android/media/MediaDrm.java +++ b/android/media/MediaDrm.java @@ -91,10 +91,10 @@ import android.util.Log; * are only decrypted when the samples are delivered to the decoder. *

* MediaDrm methods throw {@link android.media.MediaDrm.MediaDrmStateException} - * when a method is called on a MediaDrm object that has had an unrecoverable failure - * in the DRM plugin or security hardware. - * {@link android.media.MediaDrm.MediaDrmStateException} extends - * {@link java.lang.IllegalStateException} with the addition of a developer-readable + * when a method is called on a MediaDrm object that has had an unrecoverable failure + * in the DRM plugin or security hardware. + * {@link android.media.MediaDrm.MediaDrmStateException} extends + * {@link java.lang.IllegalStateException} with the addition of a developer-readable * diagnostic information string associated with the exception. *

* In the event of a mediaserver process crash or restart while a MediaDrm object @@ -102,9 +102,9 @@ import android.util.Log; * To recover, the app must release the MediaDrm object, then create and initialize * a new one. *

- * As {@link android.media.MediaDrmResetException} and - * {@link android.media.MediaDrm.MediaDrmStateException} both extend - * {@link java.lang.IllegalStateException}, they should be in an earlier catch() + * As {@link android.media.MediaDrmResetException} and + * {@link android.media.MediaDrm.MediaDrmStateException} both extend + * {@link java.lang.IllegalStateException}, they should be in an earlier catch() * block than {@link java.lang.IllegalStateException} if handled separately. *

* @@ -165,7 +165,7 @@ public final class MediaDrm { /** * Query if the given scheme identified by its UUID is supported on - * this device, and whether the drm plugin is able to handle the + * this device, and whether the DRM plugin is able to handle the * media container format specified by mimeType. * @param uuid The UUID of the crypto scheme. * @param mimeType The MIME type of the media container, e.g. "video/mp4" @@ -745,7 +745,7 @@ public final class MediaDrm { * returned in KeyRequest.defaultUrl. *

* After the app has received the key request response from the server, - * it should deliver to the response to the DRM engine plugin using the method + * it should deliver to the response to the MediaDrm instance using the method * {@link #provideKeyResponse}. * * @param scope may be a sessionId or a keySetId, depending on the specified keyType. @@ -781,7 +781,7 @@ public final class MediaDrm { /** * A key response is received from the license server by the app, then it is - * provided to the DRM engine plugin using provideKeyResponse. When the + * provided to the MediaDrm instance using provideKeyResponse. When the * response is for an offline key request, a keySetId is returned that can be * used to later restore the keys to a new session with the method * {@link #restoreKeys}. @@ -829,7 +829,7 @@ public final class MediaDrm { * in the form of {name, value} pairs. Since DRM license policies vary by vendor, * the specific status field names are determined by each DRM vendor. Refer to your * DRM provider documentation for definitions of the field names for a particular - * DRM engine plugin. + * DRM plugin. * * @param sessionId the session ID for the DRM session */ @@ -897,11 +897,11 @@ public final class MediaDrm { @NonNull String certAuthority); /** - * After a provision response is received by the app, it is provided to the DRM - * engine plugin using this method. + * After a provision response is received by the app, it is provided to the + * MediaDrm instance using this method. * * @param response the opaque provisioning response byte array to provide to the - * DRM engine plugin. + * MediaDrm instance. * * @throws DeniedByServerException if the response indicates that the * server rejected the request @@ -912,7 +912,6 @@ public final class MediaDrm { } @NonNull - /* could there be a valid response with 0-sized certificate or key? */ private native Certificate provideProvisionResponseNative(@NonNull byte[] response) throws DeniedByServerException; @@ -953,26 +952,26 @@ public final class MediaDrm { /** * Remove all secure stops without requiring interaction with the server. */ - public native void releaseAllSecureStops(); + public native void releaseAllSecureStops(); /** - * String property name: identifies the maker of the DRM engine plugin + * String property name: identifies the maker of the DRM plugin */ public static final String PROPERTY_VENDOR = "vendor"; /** - * String property name: identifies the version of the DRM engine plugin + * String property name: identifies the version of the DRM plugin */ public static final String PROPERTY_VERSION = "version"; /** - * String property name: describes the DRM engine plugin + * String property name: describes the DRM plugin */ public static final String PROPERTY_DESCRIPTION = "description"; /** * String property name: a comma-separated list of cipher and mac algorithms - * supported by CryptoSession. The list may be empty if the DRM engine + * supported by CryptoSession. The list may be empty if the DRM * plugin does not support CryptoSession operations. */ public static final String PROPERTY_ALGORITHMS = "algorithms"; @@ -988,7 +987,7 @@ public final class MediaDrm { public @interface StringProperty {} /** - * Read a DRM engine plugin String property value, given the property name string. + * Read a MediaDrm String property value, given the property name string. *

* Standard fields names are: * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION}, @@ -997,6 +996,13 @@ public final class MediaDrm { @NonNull public native String getPropertyString(@NonNull @StringProperty String propertyName); + /** + * Set a MediaDrm String property value, given the property name string + * and new value for the property. + */ + public native void setPropertyString(@NonNull @StringProperty String propertyName, + @NonNull String value); + /** * Byte array property name: the device unique identifier is established during * device provisioning and provides a means of uniquely identifying each device. @@ -1011,7 +1017,7 @@ public final class MediaDrm { public @interface ArrayProperty {} /** - * Read a DRM engine plugin byte array property value, given the property name string. + * Read a MediaDrm byte array property value, given the property name string. *

* Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID} */ @@ -1019,17 +1025,13 @@ public final class MediaDrm { public native byte[] getPropertyByteArray(@ArrayProperty String propertyName); /** - * Set a DRM engine plugin String property value. - */ - public native void setPropertyString( - String propertyName, @NonNull String value); - - /** - * Set a DRM engine plugin byte array property value. - */ - public native void setPropertyByteArray( + * Set a MediaDrm byte array property value, given the property name string + * and new value for the property. + */ + public native void setPropertyByteArray(@NonNull @ArrayProperty String propertyName, @NonNull byte[] value); + private static final native void setCipherAlgorithmNative( @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); @@ -1158,7 +1160,7 @@ public final class MediaDrm { * The algorithm string conforms to JCA Standard Names for Mac * Algorithms and is case insensitive. For example "HmacSHA256". *

- * The list of supported algorithms for a DRM engine plugin can be obtained + * The list of supported algorithms for a DRM plugin can be obtained * using the method {@link #getPropertyString} with the property name * "algorithms". */ @@ -1272,7 +1274,7 @@ public final class MediaDrm { * storage, and used when invoking the signRSA method. * * @param response the opaque certificate response byte array to provide to the - * DRM engine plugin. + * MediaDrm instance. * * @throws DeniedByServerException if the response indicates that the * server rejected the request diff --git a/android/media/MediaFormat.java b/android/media/MediaFormat.java index c475e122..306ed83c 100644 --- a/android/media/MediaFormat.java +++ b/android/media/MediaFormat.java @@ -721,14 +721,16 @@ public final class MediaFormat { /** * A key for boolean DEFAULT behavior for the track. The track with DEFAULT=true is * selected in the absence of a specific user choice. - * This is currently only used for subtitle tracks, when the user selected - * 'Default' for the captioning locale. + * This is currently used in two scenarios: + * 1) for subtitle tracks, when the user selected 'Default' for the captioning locale. + * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track, indicating the image is the + * primary item in the file. + * The associated value is an integer, where non-0 means TRUE. This is an optional * field; if not specified, DEFAULT is considered to be FALSE. */ public static final String KEY_IS_DEFAULT = "is-default"; - /** * A key for the FORCED field for subtitle tracks. True if it is a * forced subtitle track. Forced subtitle tracks are essential for the diff --git a/android/media/MediaMetadata.java b/android/media/MediaMetadata.java index bdc0fda6..31eb948d 100644 --- a/android/media/MediaMetadata.java +++ b/android/media/MediaMetadata.java @@ -34,6 +34,7 @@ import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Set; +import java.util.Objects; /** * Contains metadata about an item, such as the title, artist, etc. @@ -615,6 +616,71 @@ public final class MediaMetadata implements Parcelable { } }; + /** + * Compares the contents of this object to another MediaMetadata object. It + * does not compare Bitmaps and Ratings as the media player can choose to + * forgo these fields depending on how you retrieve the MediaMetadata. + * + * @param o The Metadata object to compare this object against + * @return Whether or not the two objects have matching fields (excluding + * Bitmaps and Ratings) + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof MediaMetadata)) { + return false; + } + + final MediaMetadata m = (MediaMetadata) o; + + for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { + String key = METADATA_KEYS_TYPE.keyAt(i); + switch (METADATA_KEYS_TYPE.valueAt(i)) { + case METADATA_TYPE_TEXT: + if (!Objects.equals(getString(key), m.getString(key))) { + return false; + } + break; + case METADATA_TYPE_LONG: + if (getLong(key) != m.getLong(key)) { + return false; + } + break; + default: + // Ignore ratings and bitmaps when comparing + break; + } + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 17; + + for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { + String key = METADATA_KEYS_TYPE.keyAt(i); + switch (METADATA_KEYS_TYPE.valueAt(i)) { + case METADATA_TYPE_TEXT: + hashCode = 31 * hashCode + Objects.hash(getString(key)); + break; + case METADATA_TYPE_LONG: + hashCode = 31 * hashCode + Long.hashCode(getLong(key)); + break; + default: + // Ignore ratings and bitmaps when comparing + break; + } + } + + return hashCode; + } + /** * Use to build MediaMetadata objects. The system defined metadata keys must * use the appropriate data type. diff --git a/android/media/MediaMuxer.java b/android/media/MediaMuxer.java index 91e57ee0..02c71b28 100644 --- a/android/media/MediaMuxer.java +++ b/android/media/MediaMuxer.java @@ -258,12 +258,18 @@ final public class MediaMuxer { * in include/media/stagefright/MediaMuxer.h! */ private OutputFormat() {} + /** @hide */ + public static final int MUXER_OUTPUT_FIRST = 0; /** MPEG4 media file format*/ - public static final int MUXER_OUTPUT_MPEG_4 = 0; + public static final int MUXER_OUTPUT_MPEG_4 = MUXER_OUTPUT_FIRST; /** WEBM media file format*/ - public static final int MUXER_OUTPUT_WEBM = 1; + public static final int MUXER_OUTPUT_WEBM = MUXER_OUTPUT_FIRST + 1; /** 3GPP media file format*/ - public static final int MUXER_OUTPUT_3GPP = 2; + public static final int MUXER_OUTPUT_3GPP = MUXER_OUTPUT_FIRST + 2; + /** HEIF media file format*/ + public static final int MUXER_OUTPUT_HEIF = MUXER_OUTPUT_FIRST + 3; + /** @hide */ + public static final int MUXER_OUTPUT_LAST = MUXER_OUTPUT_HEIF; }; /** @hide */ @@ -271,6 +277,7 @@ final public class MediaMuxer { OutputFormat.MUXER_OUTPUT_MPEG_4, OutputFormat.MUXER_OUTPUT_WEBM, OutputFormat.MUXER_OUTPUT_3GPP, + OutputFormat.MUXER_OUTPUT_HEIF, }) @Retention(RetentionPolicy.SOURCE) public @interface Format {} @@ -347,8 +354,7 @@ final public class MediaMuxer { } private void setUpMediaMuxer(@NonNull FileDescriptor fd, @Format int format) throws IOException { - if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 && format != OutputFormat.MUXER_OUTPUT_WEBM - && format != OutputFormat.MUXER_OUTPUT_3GPP) { + if (format < OutputFormat.MUXER_OUTPUT_FIRST || format > OutputFormat.MUXER_OUTPUT_LAST) { throw new IllegalArgumentException("format: " + format + " is invalid"); } mNativeObject = nativeSetup(fd, format); diff --git a/android/media/MediaRecorder.java b/android/media/MediaRecorder.java index 76784904..3c49b80b 100644 --- a/android/media/MediaRecorder.java +++ b/android/media/MediaRecorder.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; +import android.util.ArrayMap; import android.util.Log; import android.view.Surface; @@ -34,6 +35,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; +import com.android.internal.annotations.GuardedBy; + /** * Used to record audio and video. The recording control is based on a * simple state machine (see below). @@ -76,7 +79,7 @@ import java.lang.ref.WeakReference; * Audio Capture developer guide.

* */ -public class MediaRecorder +public class MediaRecorder implements AudioRouting { static { System.loadLibrary("media_jni"); @@ -1243,6 +1246,7 @@ public class MediaRecorder private static final int MEDIA_RECORDER_TRACK_EVENT_INFO = 101; private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_END = 1000; + private static final int MEDIA_RECORDER_AUDIO_ROUTING_CHANGED = 10000; @Override public void handleMessage(Message msg) { @@ -1265,6 +1269,16 @@ public class MediaRecorder return; + case MEDIA_RECORDER_AUDIO_ROUTING_CHANGED: + AudioManager.resetAudioPortGeneration(); + synchronized (mRoutingChangeListeners) { + for (NativeRoutingEventHandlerDelegate delegate + : mRoutingChangeListeners.values()) { + delegate.notifyClient(); + } + } + return; + default: Log.e(TAG, "Unknown message type " + msg.what); return; @@ -1272,6 +1286,155 @@ public class MediaRecorder } } + //-------------------------------------------------------------------------- + // Explicit Routing + //-------------------- + private AudioDeviceInfo mPreferredDevice = null; + + /** + * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route + * the input from this MediaRecorder. + * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source. + * If deviceInfo is null, default routing is restored. + * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and + * does not correspond to a valid audio input device. + */ + @Override + public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) { + if (deviceInfo != null && !deviceInfo.isSource()) { + return false; + } + int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; + boolean status = native_setInputDevice(preferredDeviceId); + if (status == true) { + synchronized (this) { + mPreferredDevice = deviceInfo; + } + } + return status; + } + + /** + * Returns the selected input device specified by {@link #setPreferredDevice}. Note that this + * is not guaranteed to correspond to the actual device being used for recording. + */ + @Override + public AudioDeviceInfo getPreferredDevice() { + synchronized (this) { + return mPreferredDevice; + } + } + + /** + * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaRecorder + * Note: The query is only valid if the MediaRecorder is currently recording. + * If the recorder is not recording, the returned device can be null or correspond to previously + * selected device when the recorder was last active. + */ + @Override + public AudioDeviceInfo getRoutedDevice() { + int deviceId = native_getRoutedDeviceId(); + if (deviceId == 0) { + return null; + } + AudioDeviceInfo[] devices = + AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS); + for (int i = 0; i < devices.length; i++) { + if (devices[i].getId() == deviceId) { + return devices[i]; + } + } + return null; + } + + /* + * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler. + */ + private void enableNativeRoutingCallbacksLocked(boolean enabled) { + if (mRoutingChangeListeners.size() == 0) { + native_enableDeviceCallback(enabled); + } + } + + /** + * The list of AudioRouting.OnRoutingChangedListener interfaces added (with + * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)} + * by an app to receive (re)routing notifications. + */ + @GuardedBy("mRoutingChangeListeners") + private ArrayMap mRoutingChangeListeners = new ArrayMap<>(); + + /** + * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing + * changes on this MediaRecorder. + * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive + * notifications of rerouting events. + * @param handler Specifies the {@link Handler} object for the thread on which to execute + * the callback. If null, the handler on the main looper will be used. + */ + @Override + public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, + Handler handler) { + synchronized (mRoutingChangeListeners) { + if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { + enableNativeRoutingCallbacksLocked(true); + mRoutingChangeListeners.put( + listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); + } + } + } + + /** + * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added + * to receive rerouting notifications. + * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface + * to remove. + */ + @Override + public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) { + synchronized (mRoutingChangeListeners) { + if (mRoutingChangeListeners.containsKey(listener)) { + mRoutingChangeListeners.remove(listener); + enableNativeRoutingCallbacksLocked(false); + } + } + } + + /** + * Helper class to handle the forwarding of native events to the appropriate listener + * (potentially) handled in a different thread + */ + private class NativeRoutingEventHandlerDelegate { + private MediaRecorder mMediaRecorder; + private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; + private Handler mHandler; + + NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder, + final AudioRouting.OnRoutingChangedListener listener, Handler handler) { + mMediaRecorder = mediaRecorder; + mOnRoutingChangedListener = listener; + mHandler = handler != null ? handler : mEventHandler; + } + + void notifyClient() { + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mOnRoutingChangedListener != null) { + mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder); + } + } + }); + } + } + } + + private native final boolean native_setInputDevice(int deviceId); + private native final int native_getRoutedDeviceId(); + private native final void native_enableDeviceCallback(boolean enabled); + /** * Called from native code when an interesting event happens. This method * just uses the EventHandler system to post the event back to the main app thread. diff --git a/android/media/tv/TvInputManager.java b/android/media/tv/TvInputManager.java index fd1f2cf6..143182f8 100644 --- a/android/media/tv/TvInputManager.java +++ b/android/media/tv/TvInputManager.java @@ -1330,6 +1330,7 @@ public final class TvInputManager { * * @return the list of content ratings blocked by the user. */ + @SystemApi public List getBlockedRatings() { try { List ratings = new ArrayList<>(); @@ -1585,8 +1586,10 @@ public final class TvInputManager { * @param info The TV input which will use the acquired Hardware. * @return Hardware on success, {@code null} otherwise. * + * @hide * @removed */ + @SystemApi @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback, TvInputInfo info) { @@ -2591,6 +2594,7 @@ public final class TvInputManager { } /** @removed */ + @SystemApi public boolean dispatchKeyEventToHdmi(KeyEvent event) { return false; } -- cgit v1.2.3