diff options
author | Lajos Molnar <lajos@google.com> | 2015-04-22 17:08:22 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-04-22 17:08:23 +0000 |
commit | ce510d5f62a0e4dc46d3d3c05fafe9b08eb5b497 (patch) | |
tree | 44db6799c372a47ec1fb65eda6979524a64ce319 | |
parent | 6bac04abc9f945911498a182cea52b48e0b43f23 (diff) | |
parent | 99f8072386ce9891a5973d591dc1a30e45b50bc6 (diff) | |
download | frameworks_base-ce510d5f62a0e4dc46d3d3c05fafe9b08eb5b497.zip frameworks_base-ce510d5f62a0e4dc46d3d3c05fafe9b08eb5b497.tar.gz frameworks_base-ce510d5f62a0e4dc46d3d3c05fafe9b08eb5b497.tar.bz2 |
Merge "media: add annotations to low-level Media* classes"
-rw-r--r-- | api/current.txt | 2 | ||||
-rw-r--r-- | api/system-current.txt | 2 | ||||
-rw-r--r-- | media/java/android/media/MediaCodec.java | 212 | ||||
-rw-r--r-- | media/java/android/media/MediaCrypto.java | 16 | ||||
-rw-r--r-- | media/java/android/media/MediaCryptoException.java | 4 | ||||
-rw-r--r-- | media/java/android/media/MediaDrm.java | 344 | ||||
-rw-r--r-- | media/java/android/media/MediaExtractor.java | 69 | ||||
-rw-r--r-- | media/java/android/media/MediaMuxer.java | 41 | ||||
-rw-r--r-- | media/java/android/media/MediaSync.java | 34 |
9 files changed, 532 insertions, 192 deletions
diff --git a/api/current.txt b/api/current.txt index c7fdc6a..a126699 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15733,7 +15733,7 @@ package android.media { method public final void release(); method public void seekTo(long, int); method public void selectTrack(int); - method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException; + method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException; method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; method public final void setDataSource(java.lang.String) throws java.io.IOException; diff --git a/api/system-current.txt b/api/system-current.txt index 35dc610..e149048 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -16946,7 +16946,7 @@ package android.media { method public final void release(); method public void seekTo(long, int); method public void selectTrack(int); - method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException; + method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException; method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; method public final void setDataSource(java.lang.String) throws java.io.IOException; diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index d82afdf..b0cd3e4 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -16,6 +16,9 @@ package android.media; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.ImageFormat; import android.graphics.Rect; import android.media.Image; @@ -30,6 +33,8 @@ import android.os.Message; import android.view.Surface; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.util.Arrays; @@ -253,7 +258,7 @@ final public class MediaCodec { * {@link #BUFFER_FLAG_END_OF_STREAM}. */ public void set( - int newOffset, int newSize, long newTimeUs, int newFlags) { + int newOffset, int newSize, long newTimeUs, @BufferFlag int newFlags) { offset = newOffset; size = newSize; presentationTimeUs = newTimeUs; @@ -293,6 +298,7 @@ final public class MediaCodec { * be an empty buffer, whose sole purpose is to carry the end-of-stream * marker. */ + @BufferFlag public int flags; }; @@ -325,6 +331,18 @@ final public class MediaCodec { */ public static final int BUFFER_FLAG_END_OF_STREAM = 4; + /** @hide */ + @IntDef( + flag = true, + value = { + BUFFER_FLAG_SYNC_FRAME, + BUFFER_FLAG_KEY_FRAME, + BUFFER_FLAG_CODEC_CONFIG, + BUFFER_FLAG_END_OF_STREAM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BufferFlag {} + private EventHandler mEventHandler; private Callback mCallback; @@ -339,13 +357,13 @@ final public class MediaCodec { private class EventHandler extends Handler { private MediaCodec mCodec; - public EventHandler(MediaCodec codec, Looper looper) { + public EventHandler(@NonNull MediaCodec codec, @NonNull Looper looper) { super(looper); mCodec = codec; } @Override - public void handleMessage(Message msg) { + public void handleMessage(@NonNull Message msg) { switch (msg.what) { case EVENT_CALLBACK: { @@ -364,7 +382,7 @@ final public class MediaCodec { } } - private void handleCallback(Message msg) { + private void handleCallback(@NonNull Message msg) { if (mCallback == null) { return; } @@ -438,7 +456,8 @@ final public class MediaCodec { * @throws IllegalArgumentException if type is not a valid mime type. * @throws NullPointerException if type is null. */ - public static MediaCodec createDecoderByType(String type) + @NonNull + public static MediaCodec createDecoderByType(@NonNull String type) throws IOException { return new MediaCodec(type, true /* nameIsType */, false /* encoder */); } @@ -450,7 +469,8 @@ final public class MediaCodec { * @throws IllegalArgumentException if type is not a valid mime type. * @throws NullPointerException if type is null. */ - public static MediaCodec createEncoderByType(String type) + @NonNull + public static MediaCodec createEncoderByType(@NonNull String type) throws IOException { return new MediaCodec(type, true /* nameIsType */, true /* encoder */); } @@ -464,14 +484,15 @@ final public class MediaCodec { * @throws IllegalArgumentException if name is not valid. * @throws NullPointerException if name is null. */ - public static MediaCodec createByCodecName(String name) + @NonNull + public static MediaCodec createByCodecName(@NonNull String name) throws IOException { return new MediaCodec( name, false /* nameIsType */, false /* unused */); } private MediaCodec( - String name, boolean nameIsType, boolean encoder) { + @NonNull String name, boolean nameIsType, boolean encoder) { Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); @@ -524,15 +545,26 @@ final public class MediaCodec { */ public static final int CONFIGURE_FLAG_ENCODE = 1; + /** @hide */ + @IntDef(flag = true, value = { CONFIGURE_FLAG_ENCODE }) + @Retention(RetentionPolicy.SOURCE) + public @interface ConfigureFlag {} + /** * Configures a component. * * @param format The format of the input data (decoder) or the desired - * format of the output data (encoder). + * format of the output data (encoder). Passing {@code null} + * as {@code format} is equivalent to passing an + * {@link MediaFormat#MediaFormat an empty mediaformat}. * @param surface Specify a surface on which to render the output of this - * decoder. + * decoder. Pass {@code null} as {@code surface} if the + * codec does not generate raw video output (e.g. not a video + * decoder) and/or if you want to configure the codec for + * {@link ByteBuffer} output. * @param crypto Specify a crypto object to facilitate secure decryption - * of the media data. + * of the media data. Pass {@code null} as {@code crypto} for + * non-secure codecs. * @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the * component as an encoder. * @throws IllegalArgumentException if the surface has been released (or is invalid), @@ -544,14 +576,14 @@ final public class MediaCodec { * @throws CodecException upon codec error. */ public void configure( - MediaFormat format, - Surface surface, MediaCrypto crypto, int flags) { - Map<String, Object> formatMap = format.getMap(); - + @Nullable MediaFormat format, + @Nullable Surface surface, @Nullable MediaCrypto crypto, + @ConfigureFlag int flags) { String[] keys = null; Object[] values = null; if (format != null) { + Map<String, Object> formatMap = format.getMap(); keys = new String[formatMap.size()]; values = new Object[formatMap.size()]; @@ -578,11 +610,11 @@ final public class MediaCodec { native_configure(keys, values, surface, crypto, flags); } - private native final void native_setCallback(Callback cb); + private native final void native_setCallback(@Nullable Callback cb); private native final void native_configure( - String[] keys, Object[] values, - Surface surface, MediaCrypto crypto, int flags); + @Nullable String[] keys, @Nullable Object[] values, + @Nullable Surface surface, @Nullable MediaCrypto crypto, @ConfigureFlag int flags); /** * Requests a Surface to use as the input to an encoder, in place of input buffers. This @@ -596,6 +628,7 @@ final public class MediaCodec { * unexpected results. * @throws IllegalStateException if not in the Configured state. */ + @NonNull public native final Surface createInputSurface(); /** @@ -669,7 +702,7 @@ final public class MediaCodec { * Thrown when an internal codec error occurs. */ public final static class CodecException extends IllegalStateException { - CodecException(int errorCode, int actionCode, String detailMessage, int reason) { + CodecException(int errorCode, int actionCode, @Nullable String detailMessage, int reason) { super(detailMessage); mErrorCode = errorCode; mReason = reason; @@ -704,6 +737,7 @@ final public class MediaCodec { * The reason could be one of {@link #REASON_HARDWARE} or {@link #REASON_RECLAIMED}. * */ + @ReasonCode public int getReason() { return mReason; } @@ -725,7 +759,7 @@ final public class MediaCodec { * since this string will not be localized or generally * comprehensible to end-users. */ - public String getDiagnosticInfo() { + public @NonNull String getDiagnosticInfo() { return mDiagnosticInfo; } @@ -742,6 +776,14 @@ final public class MediaCodec { */ public static final int REASON_RECLAIMED = 1; + /** @hide */ + @IntDef({ + REASON_HARDWARE, + REASON_RECLAIMED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ReasonCode {} + /* Must be in sync with android_media_MediaCodec.cpp */ private final static int ACTION_TRANSIENT = 1; private final static int ACTION_RECOVERABLE = 2; @@ -756,7 +798,7 @@ final public class MediaCodec { * Thrown when a crypto error occurs while queueing a secure input buffer. */ public final static class CryptoException extends RuntimeException { - public CryptoException(int errorCode, String detailMessage) { + public CryptoException(int errorCode, @Nullable String detailMessage) { super(detailMessage); mErrorCode = errorCode; } @@ -789,9 +831,20 @@ final public class MediaCodec { */ public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; + /** @hide */ + @IntDef({ + ERROR_NO_KEY, + ERROR_KEY_EXPIRED, + ERROR_RESOURCE_BUSY, + ERROR_INSUFFICIENT_OUTPUT_PROTECTION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CryptoErrorCode {} + /** * Retrieve the error code associated with a CryptoException */ + @CryptoErrorCode public int getErrorCode() { return mErrorCode; } @@ -885,10 +938,10 @@ final public class MediaCodec { public final static class CryptoInfo { public void set( int newNumSubSamples, - int[] newNumBytesOfClearData, - int[] newNumBytesOfEncryptedData, - byte[] newKey, - byte[] newIV, + @NonNull int[] newNumBytesOfClearData, + @NonNull int[] newNumBytesOfEncryptedData, + @NonNull byte[] newKey, + @NonNull byte[] newIV, int newMode) { numSubSamples = newNumSubSamples; numBytesOfClearData = newNumBytesOfClearData; @@ -970,7 +1023,7 @@ final public class MediaCodec { public final void queueSecureInputBuffer( int index, int offset, - CryptoInfo info, + @NonNull CryptoInfo info, long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { @@ -989,7 +1042,7 @@ final public class MediaCodec { private native final void native_queueSecureInputBuffer( int index, int offset, - CryptoInfo info, + @NonNull CryptoInfo info, long presentationTimeUs, int flags) throws CryptoException; @@ -1043,6 +1096,15 @@ final public class MediaCodec { */ public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3; + /** @hide */ + @IntDef({ + INFO_TRY_AGAIN_LATER, + INFO_OUTPUT_FORMAT_CHANGED, + INFO_OUTPUT_BUFFERS_CHANGED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OutputBufferInfo {} + /** * Dequeue an output buffer, block at most "timeoutUs" microseconds. * Returns the index of an output buffer that has been successfully @@ -1053,8 +1115,9 @@ final public class MediaCodec { * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. */ + @OutputBufferInfo public final int dequeueOutputBuffer( - BufferInfo info, long timeoutUs) { + @NonNull BufferInfo info, long timeoutUs) { int res = native_dequeueOutputBuffer(info, timeoutUs); synchronized(mBufferLock) { if (res == INFO_OUTPUT_BUFFERS_CHANGED) { @@ -1067,7 +1130,7 @@ final public class MediaCodec { } private native final int native_dequeueOutputBuffer( - BufferInfo info, long timeoutUs); + @NonNull BufferInfo info, long timeoutUs); /** * If you are done with a buffer, use this call to return the buffer to @@ -1176,6 +1239,7 @@ final public class MediaCodec { * Configured state. * @throws MediaCodec.CodecException upon codec error. */ + @NonNull public final MediaFormat getOutputFormat() { return new MediaFormat(getFormatNative(false /* input */)); } @@ -1190,6 +1254,7 @@ final public class MediaCodec { * Configured state. * @throws MediaCodec.CodecException upon codec error. */ + @NonNull public final MediaFormat getInputFormat() { return new MediaFormat(getFormatNative(true /* input */)); } @@ -1203,12 +1268,15 @@ final public class MediaCodec { * @return the format for the output buffer, or null if the index * is not a dequeued output buffer. */ + @NonNull public final MediaFormat getOutputFormat(int index) { return new MediaFormat(getOutputFormatNative(index)); } + @NonNull private native final Map<String, Object> getFormatNative(boolean input); + @NonNull private native final Map<String, Object> getOutputFormatNative(int index); // used to track dequeued buffers @@ -1230,12 +1298,12 @@ final public class MediaCodec { } } - public void setImage(Image image) { + public void setImage(@Nullable Image image) { free(); mImage = image; } - public void setByteBuffer(ByteBuffer buffer) { + public void setByteBuffer(@Nullable ByteBuffer buffer) { free(); mByteBuffer = buffer; } @@ -1252,7 +1320,7 @@ final public class MediaCodec { } } - public void put(int index, ByteBuffer newBuffer) { + public void put(int index, @Nullable ByteBuffer newBuffer) { CodecBuffer buffer = mMap.get(index); if (buffer == null) { // likely buffer = new CodecBuffer(); @@ -1261,7 +1329,7 @@ final public class MediaCodec { buffer.setByteBuffer(newBuffer); } - public void put(int index, Image newImage) { + public void put(int index, @Nullable Image newImage) { CodecBuffer buffer = mMap.get(index); if (buffer == null) { // likely buffer = new CodecBuffer(); @@ -1285,7 +1353,7 @@ final public class MediaCodec { final private Object mBufferLock; private final void invalidateByteBuffer( - ByteBuffer[] buffers, int index) { + @Nullable ByteBuffer[] buffers, int index) { if (buffers != null && index >= 0 && index < buffers.length) { ByteBuffer buffer = buffers[index]; if (buffer != null) { @@ -1295,7 +1363,7 @@ final public class MediaCodec { } private final void validateInputByteBuffer( - ByteBuffer[] buffers, int index) { + @Nullable ByteBuffer[] buffers, int index) { if (buffers != null && index >= 0 && index < buffers.length) { ByteBuffer buffer = buffers[index]; if (buffer != null) { @@ -1306,7 +1374,7 @@ final public class MediaCodec { } private final void revalidateByteBuffer( - ByteBuffer[] buffers, int index) { + @Nullable ByteBuffer[] buffers, int index) { synchronized(mBufferLock) { if (buffers != null && index >= 0 && index < buffers.length) { ByteBuffer buffer = buffers[index]; @@ -1318,7 +1386,7 @@ final public class MediaCodec { } private final void validateOutputByteBuffer( - ByteBuffer[] buffers, int index, BufferInfo info) { + @Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) { if (buffers != null && index >= 0 && index < buffers.length) { ByteBuffer buffer = buffers[index]; if (buffer != null) { @@ -1328,7 +1396,7 @@ final public class MediaCodec { } } - private final void invalidateByteBuffers(ByteBuffer[] buffers) { + private final void invalidateByteBuffers(@Nullable ByteBuffer[] buffers) { if (buffers != null) { for (ByteBuffer buffer: buffers) { if (buffer != null) { @@ -1338,14 +1406,14 @@ final public class MediaCodec { } } - private final void freeByteBuffer(ByteBuffer buffer) { + private final void freeByteBuffer(@Nullable ByteBuffer buffer) { if (buffer != null /* && buffer.isDirect() */) { // all of our ByteBuffers are direct java.nio.NioUtils.freeDirectBuffer(buffer); } } - private final void freeByteBuffers(ByteBuffer[] buffers) { + private final void freeByteBuffers(@Nullable ByteBuffer[] buffers) { if (buffers != null) { for (ByteBuffer buffer: buffers) { freeByteBuffer(buffer); @@ -1388,13 +1456,14 @@ final public class MediaCodec { * @deprecated Use the new {@link #getInputBuffer} method instead * each time an input buffer is dequeued. * - * <b>Note:</b>As of API 21, dequeued input buffers are + * <b>Note:</b> As of API 21, dequeued input buffers are * automatically {@link java.nio.Buffer#clear cleared}. * * @throws IllegalStateException if not in the Executing state, * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. */ + @NonNull public ByteBuffer[] getInputBuffers() { if (mCachedInputBuffers == null) { throw new IllegalStateException(); @@ -1415,7 +1484,7 @@ final public class MediaCodec { * each time an output buffer is dequeued. This method is not * supported if codec is configured in asynchronous mode. * - * <b>Note:</b>As of API 21, the position and limit of output + * <b>Note:</b> As of API 21, the position and limit of output * buffers that are dequeued will be set to the valid data * range. * @@ -1423,6 +1492,7 @@ final public class MediaCodec { * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. */ + @NonNull public ByteBuffer[] getOutputBuffers() { if (mCachedOutputBuffers == null) { throw new IllegalStateException(); @@ -1449,6 +1519,7 @@ final public class MediaCodec { * @throws IllegalStateException if not in the Executing state. * @throws MediaCodec.CodecException upon codec error. */ + @Nullable public ByteBuffer getInputBuffer(int index) { ByteBuffer newBuffer = getBuffer(true /* input */, index); synchronized(mBufferLock) { @@ -1477,6 +1548,7 @@ final public class MediaCodec { * @throws IllegalStateException if not in the Executing state. * @throws MediaCodec.CodecException upon codec error. */ + @Nullable public Image getInputImage(int index) { Image newImage = getImage(true /* input */, index); synchronized(mBufferLock) { @@ -1505,6 +1577,7 @@ final public class MediaCodec { * @throws IllegalStateException if not in the Executing state. * @throws MediaCodec.CodecException upon codec error. */ + @Nullable public ByteBuffer getOutputBuffer(int index) { ByteBuffer newBuffer = getBuffer(false /* input */, index); synchronized(mBufferLock) { @@ -1532,6 +1605,7 @@ final public class MediaCodec { * @throws IllegalStateException if not in the Executing state. * @throws MediaCodec.CodecException upon codec error. */ + @Nullable public Image getOutputImage(int index) { Image newImage = getImage(false /* input */, index); synchronized(mBufferLock) { @@ -1552,19 +1626,28 @@ final public class MediaCodec { */ public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; + /** @hide */ + @IntDef({ + VIDEO_SCALING_MODE_SCALE_TO_FIT, + VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VideoScalingMode {} + /** * If a surface has been specified in a previous call to {@link #configure} * specifies the scaling mode to use. The default is "scale to fit". * @throws IllegalArgumentException if mode is not recognized. * @throws IllegalStateException if in the Uninitialized state. */ - public native final void setVideoScalingMode(int mode); + public native final void setVideoScalingMode(@VideoScalingMode int mode); /** * Get the component name. If the codec was created by createDecoderByType * or createEncoderByType, what component is chosen is not known beforehand. * @throws IllegalStateException if in the Uninitialized state. */ + @NonNull public native final String getName(); /** @@ -1592,9 +1675,12 @@ final public class MediaCodec { /** * Communicate additional parameter changes to the component instance. + * <b>Note:</b> Some of these parameter changes may silently fail to apply. + * + * @param params The bundle of parameters to set. * @throws IllegalStateException if in the Uninitialized state. */ - public final void setParameters(Bundle params) { + public final void setParameters(@Nullable Bundle params) { if (params == null) { return; } @@ -1626,9 +1712,11 @@ final public class MediaCodec { * {@code flush}, you must call {@link #start} to "resume" receiving input buffers, * even if an input surface was created. * - * @param cb The callback that will run. + * @param cb The callback that will run. Use {@code null} to clear a previously + * set callback (before {@link #configure configure} is called and run + * in synchronous mode). */ - public void setCallback(/* MediaCodec. */ Callback cb) { + public void setCallback(@Nullable /* MediaCodec. */ Callback cb) { if (mEventHandler != null) { // set java callback on handler Message msg = mEventHandler.obtainMessage(EVENT_SET_CALLBACK, 0, 0, cb); @@ -1652,7 +1740,7 @@ final public class MediaCodec { * @param codec The MediaCodec object. * @param index The index of the available input buffer. */ - public abstract void onInputBufferAvailable(MediaCodec codec, int index); + public abstract void onInputBufferAvailable(@NonNull MediaCodec codec, int index); /** * Called when an output buffer becomes available. @@ -1661,7 +1749,8 @@ final public class MediaCodec { * @param index The index of the available output buffer. * @param info Info regarding the available output buffer {@link MediaCodec.BufferInfo}. */ - public abstract void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info); + public abstract void onOutputBufferAvailable( + @NonNull MediaCodec codec, int index, @NonNull BufferInfo info); /** * Called when the MediaCodec encountered an error @@ -1669,7 +1758,7 @@ final public class MediaCodec { * @param codec The MediaCodec object. * @param e The {@link MediaCodec.CodecException} object describing the error. */ - public abstract void onError(MediaCodec codec, CodecException e); + public abstract void onError(@NonNull MediaCodec codec, @NonNull CodecException e); /** * Called when the output format has changed @@ -1677,18 +1766,19 @@ final public class MediaCodec { * @param codec The MediaCodec object. * @param format The new output format. */ - public abstract void onOutputFormatChanged(MediaCodec codec, MediaFormat format); + public abstract void onOutputFormatChanged( + @NonNull MediaCodec codec, @NonNull MediaFormat format); } private void postEventFromNative( - int what, int arg1, int arg2, Object obj) { + int what, int arg1, int arg2, @Nullable Object obj) { if (mEventHandler != null) { Message msg = mEventHandler.obtainMessage(what, arg1, arg2, obj); mEventHandler.sendMessage(msg); } } - private native final void setParameters(String[] keys, Object[] values); + private native final void setParameters(@NonNull String[] keys, @NonNull Object[] values); /** * Get the codec info. If the codec was created by createDecoderByType @@ -1696,20 +1786,24 @@ final public class MediaCodec { * and thus the caller does not have the MediaCodecInfo. * @throws IllegalStateException if in the Uninitialized state. */ + @NonNull public MediaCodecInfo getCodecInfo() { return MediaCodecList.getInfoFor(getName()); } + @NonNull private native final ByteBuffer[] getBuffers(boolean input); + @Nullable private native final ByteBuffer getBuffer(boolean input, int index); + @Nullable private native final Image getImage(boolean input, int index); private static native final void native_init(); private native final void native_setup( - String name, boolean nameIsType, boolean encoder); + @NonNull String name, boolean nameIsType, boolean encoder); private native final void native_finalize(); @@ -1756,6 +1850,7 @@ final public class MediaCodec { return mTimestamp; } + @NonNull public Plane[] getPlanes() { checkValid(); return Arrays.copyOf(mPlanes, mPlanes.length); @@ -1774,7 +1869,7 @@ final public class MediaCodec { * The crop rectangle specifies the region of valid pixels in the image, * using coordinates in the largest-resolution plane. */ - public void setCropRect(Rect cropRect) { + public void setCropRect(@Nullable Rect cropRect) { if (mIsReadOnly) { throw new ReadOnlyBufferException(); } @@ -1787,7 +1882,7 @@ final public class MediaCodec { } } - private int readInt(ByteBuffer buffer, boolean asLong) { + private int readInt(@NonNull ByteBuffer buffer, boolean asLong) { if (asLong) { return (int)buffer.getLong(); } else { @@ -1796,8 +1891,8 @@ final public class MediaCodec { } public MediaImage( - ByteBuffer buffer, ByteBuffer info, boolean readOnly, - long timestamp, int xOffset, int yOffset, Rect cropRect) { + @NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly, + long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) { mFormat = ImageFormat.YUV_420_888; mTimestamp = timestamp; mIsValid = true; @@ -1863,7 +1958,7 @@ final public class MediaCodec { } private class MediaPlane extends Plane { - public MediaPlane(ByteBuffer buffer, int rowInc, int colInc) { + public MediaPlane(@NonNull ByteBuffer buffer, int rowInc, int colInc) { mData = buffer; mRowInc = rowInc; mColInc = colInc; @@ -1882,6 +1977,7 @@ final public class MediaCodec { } @Override + @NonNull public ByteBuffer getBuffer() { checkValid(); return mData; diff --git a/media/java/android/media/MediaCrypto.java b/media/java/android/media/MediaCrypto.java index da81b37..474d8b9 100644 --- a/media/java/android/media/MediaCrypto.java +++ b/media/java/android/media/MediaCrypto.java @@ -16,6 +16,7 @@ package android.media; +import android.annotation.NonNull; import android.media.MediaCryptoException; import java.util.UUID; @@ -34,11 +35,12 @@ public final class MediaCrypto { * this device. * @param uuid The UUID of the crypto scheme. */ - public static final boolean isCryptoSchemeSupported(UUID uuid) { + public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) { return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid)); } - private static final byte[] getByteArrayFromUUID(UUID uuid) { + @NonNull + private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) { long msb = uuid.getMostSignificantBits(); long lsb = uuid.getLeastSignificantBits(); @@ -51,7 +53,7 @@ public final class MediaCrypto { return uuidBytes; } - private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid); + private static final native boolean isCryptoSchemeSupportedNative(@NonNull byte[] uuid); /** * Instantiate a MediaCrypto object using opaque, crypto scheme specific @@ -59,7 +61,7 @@ public final class MediaCrypto { * @param uuid The UUID of the crypto scheme. * @param initData Opaque initialization data specific to the crypto scheme. */ - public MediaCrypto(UUID uuid, byte[] initData) throws MediaCryptoException { + public MediaCrypto(@NonNull UUID uuid, @NonNull byte[] initData) throws MediaCryptoException { native_setup(getByteArrayFromUUID(uuid), initData); } @@ -68,7 +70,7 @@ public final class MediaCrypto { * to decode data of the given mime type. * @param mime The mime type of the media data */ - public final native boolean requiresSecureDecoderComponent(String mime); + public final native boolean requiresSecureDecoderComponent(@NonNull String mime); /** * Associate a MediaDrm session with this MediaCrypto instance. The @@ -81,7 +83,7 @@ public final class MediaCrypto { * MediaCrypto instance * @throws MediaCryptoException on failure to set the sessionId */ - public final native void setMediaDrmSession(byte[] sessionId) + public final native void setMediaDrmSession(@NonNull byte[] sessionId) throws MediaCryptoException; @Override @@ -92,7 +94,7 @@ public final class MediaCrypto { public native final void release(); private static native final void native_init(); - private native final void native_setup(byte[] uuid, byte[] initData) + private native final void native_setup(@NonNull byte[] uuid, @NonNull byte[] initData) throws MediaCryptoException; private native final void native_finalize(); diff --git a/media/java/android/media/MediaCryptoException.java b/media/java/android/media/MediaCryptoException.java index 703e96f..32ddf47 100644 --- a/media/java/android/media/MediaCryptoException.java +++ b/media/java/android/media/MediaCryptoException.java @@ -16,12 +16,14 @@ package android.media; +import android.annotation.Nullable; + /** * Exception thrown if MediaCrypto object could not be instantiated or * if unable to perform an operation on the MediaCrypto object. */ public final class MediaCryptoException extends Exception { - public MediaCryptoException(String detailMessage) { + public MediaCryptoException(@Nullable String detailMessage) { super(detailMessage); } } diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index fc5fc43..acff301 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -16,11 +16,17 @@ package android.media; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; import android.annotation.SystemApi; import android.os.Handler; import android.os.Looper; @@ -104,6 +110,8 @@ public final class MediaDrm { private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES; private EventHandler mEventHandler; + private EventHandler mOnKeysChangeEventHandler; + private EventHandler mOnExpirationUpdateEventHandler; private OnEventListener mOnEventListener; private OnKeysChangeListener mOnKeysChangeListener; private OnExpirationUpdateListener mOnExpirationUpdateListener; @@ -124,12 +132,20 @@ public final class MediaDrm { */ public static final int CERTIFICATE_TYPE_X509 = 1; + /** @hide */ + @IntDef({ + CERTIFICATE_TYPE_NONE, + CERTIFICATE_TYPE_X509, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CertificateType {} + /** * Query if the given scheme identified by its UUID is supported on * this device. * @param uuid The UUID of the crypto scheme. */ - public static final boolean isCryptoSchemeSupported(UUID uuid) { + public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) { return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null); } @@ -141,11 +157,12 @@ public final class MediaDrm { * @param mimeType The MIME type of the media container, e.g. "video/mp4" * or "video/webm" */ - public static final boolean isCryptoSchemeSupported(UUID uuid, String mimeType) { + public static final boolean isCryptoSchemeSupported( + @NonNull UUID uuid, @NonNull String mimeType) { return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType); } - private static final byte[] getByteArrayFromUUID(UUID uuid) { + private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) { long msb = uuid.getMostSignificantBits(); long lsb = uuid.getLeastSignificantBits(); @@ -158,8 +175,8 @@ public final class MediaDrm { return uuidBytes; } - private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid, - String mimeType); + private static final native boolean isCryptoSchemeSupportedNative( + @NonNull byte[] uuid, @Nullable String mimeType); /** * Instantiate a MediaDrm object @@ -169,7 +186,7 @@ public final class MediaDrm { * @throws UnsupportedSchemeException if the device does not support the * specified scheme UUID */ - public MediaDrm(UUID uuid) throws UnsupportedSchemeException { + public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException { Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); @@ -198,7 +215,7 @@ public final class MediaDrm { /** * @hide */ - public MediaDrmStateException(int errorCode, String detailMessage) { + public MediaDrmStateException(int errorCode, @Nullable String detailMessage) { super(detailMessage); mErrorCode = errorCode; @@ -224,6 +241,7 @@ public final class MediaDrm { * since this string will not be localized or generally comprehensible * to end-users. */ + @NonNull public String getDiagnosticInfo() { return mDiagnosticInfo; } @@ -233,13 +251,13 @@ public final class MediaDrm { * Register a callback to be invoked when a session expiration update * occurs. The app's OnExpirationUpdateListener will be notified * when the expiration time of the keys in the session have changed. - * @param listener the callback that will be run + * @param listener the callback that will be run, or {@code null} to unregister the + * previously registered callback. * @param handler the handler on which the listener should be invoked, or - * null if the listener should be invoked on the calling thread's looper. + * {@code null} if the listener should be invoked on the calling thread's looper. */ - public void setOnExpirationUpdateListener(OnExpirationUpdateListener listener, - Handler handler) - { + public void setOnExpirationUpdateListener( + @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) { if (listener != null) { Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); if (looper != null) { @@ -266,20 +284,21 @@ public final class MediaDrm { * @param expirationTime the new expiration time for the keys in the session. * The time is in milliseconds, relative to the Unix epoch. */ - void onExpirationUpdate(MediaDrm md, byte[] sessionId, long expirationTime); + void onExpirationUpdate( + @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime); } /** * Register a callback to be invoked when the state of keys in a session * change, e.g. when a license update occurs or when a license expires. * - * @param listener the callback that will be run when key status changes + * @param listener the callback that will be run when key status changes, or + * {@code null} to unregister the previously registered callback. * @param handler the handler on which the listener should be invoked, or * null if the listener should be invoked on the calling thread's looper. */ - public void setOnKeysChangeListener(OnKeysChangeListener listener, - Handler handler) - { + public void setOnKeysChangeListener( + @Nullable OnKeysChangeListener listener, @Nullable Handler handler) { if (listener != null) { Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); if (looper != null) { @@ -309,7 +328,9 @@ public final class MediaDrm { * which may trigger an attempt to resume playback on the media stream * if it is currently blocked waiting for a key. */ - void onKeysChange(MediaDrm md, byte[] sessionId, List<KeyStatus> keyInformation, + void onKeysChange( + @NonNull MediaDrm md, @NonNull byte[] sessionId, + @NonNull List<KeyStatus> keyInformation, boolean hasNewUsableKey); } @@ -344,6 +365,16 @@ public final class MediaDrm { */ public static final int KEY_STATUS_INTERNAL_ERROR = 4; + /** @hide */ + @IntDef({ + KEY_STATUS_USABLE, + KEY_STATUS_EXPIRED, + KEY_STATUS_OUTPUT_NOT_ALLOWED, + KEY_STATUS_PENDING, + KEY_STATUS_INTERNAL_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface KeyStatusCode {} /** * Defines the status of a key. @@ -355,7 +386,7 @@ public final class MediaDrm { private final byte[] mKeyId; private final int mStatusCode; - KeyStatus(byte[] keyId, int statusCode) { + KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) { mKeyId = keyId; mStatusCode = statusCode; } @@ -363,20 +394,23 @@ public final class MediaDrm { /** * Returns the status code for the key */ + @KeyStatusCode public int getStatusCode() { return mStatusCode; } /** * Returns the id for the key */ + @NonNull public byte[] getKeyId() { return mKeyId; } } /** * Register a callback to be invoked when an event occurs * - * @param listener the callback that will be run + * @param listener the callback that will be run. Use {@code null} to + * stop receiving event callbacks. */ - public void setOnEventListener(OnEventListener listener) + public void setOnEventListener(@Nullable OnEventListener listener) { mOnEventListener = listener; } @@ -391,12 +425,16 @@ public final class MediaDrm { * Called when an event occurs that requires the app to be notified * * @param md the MediaDrm object on which the event occurred - * @param sessionId the DRM session ID on which the event occurred + * @param sessionId the DRM session ID on which the event occurred, + * or {@code null} if there is no session ID associated with the event. * @param event indicates the event type * @param extra an secondary error code * @param data optional byte array of data that may be associated with the event */ - void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data); + void onEvent( + @NonNull MediaDrm md, @Nullable byte[] sessionId, + @DrmEvent int event, int extra, + @Nullable byte[] data); } /** @@ -433,6 +471,17 @@ public final class MediaDrm { */ public static final int EVENT_SESSION_RECLAIMED = 5; + /** @hide */ + @IntDef({ + EVENT_PROVISION_REQUIRED, + EVENT_KEY_REQUIRED, + EVENT_KEY_EXPIRED, + EVENT_VENDOR_DEFINED, + EVENT_SESSION_RECLAIMED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DrmEvent {} + private static final int DRM_EVENT = 200; private static final int EXPIRATION_UPDATE = 201; private static final int KEYS_CHANGE = 202; @@ -441,13 +490,13 @@ public final class MediaDrm { { private MediaDrm mMediaDrm; - public EventHandler(MediaDrm md, Looper looper) { + public EventHandler(@NonNull MediaDrm md, @NonNull Looper looper) { super(looper); mMediaDrm = md; } @Override - public void handleMessage(Message msg) { + public void handleMessage(@NonNull Message msg) { if (mMediaDrm.mNativeContext == 0) { Log.w(TAG, "MediaDrm went away with unhandled events"); return; @@ -516,7 +565,8 @@ public final class MediaDrm { /** * Parse a list of KeyStatus objects from an event parcel */ - private List<KeyStatus> keyStatusListFromParcel(Parcel parcel) { + @NonNull + private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) { int nelems = parcel.readInt(); List<KeyStatus> keyStatusList = new ArrayList(nelems); while (nelems-- > 0) { @@ -534,8 +584,8 @@ public final class MediaDrm { * code is safe from the object disappearing from underneath it. (This is * the cookie passed to native_setup().) */ - private static void postEventFromNative(Object mediadrm_ref, - int what, int eventType, int extra, Object obj) + private static void postEventFromNative(@NonNull Object mediadrm_ref, + int what, int eventType, int extra, @Nullable Object obj) { MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get(); if (md == null) { @@ -553,6 +603,7 @@ public final class MediaDrm { * @throws NotProvisionedException if provisioning is needed * @throws ResourceBusyException if required resources are in use */ + @NonNull public native byte[] openSession() throws NotProvisionedException, ResourceBusyException; @@ -560,7 +611,7 @@ public final class MediaDrm { * Close a session on the MediaDrm object that was previously opened * with {@link #openSession}. */ - public native void closeSession(byte[] sessionId); + public native void closeSession(@NonNull byte[] sessionId); /** * This key request type species that the keys will be for online use, they will @@ -580,6 +631,15 @@ public final class MediaDrm { */ public static final int KEY_TYPE_RELEASE = 3; + /** @hide */ + @IntDef({ + KEY_TYPE_STREAMING, + KEY_TYPE_OFFLINE, + KEY_TYPE_RELEASE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface KeyType {} + /** * Key request type is initial license request */ @@ -595,6 +655,15 @@ public final class MediaDrm { */ public static final int REQUEST_TYPE_RELEASE = 2; + /** @hide */ + @IntDef({ + REQUEST_TYPE_INITIAL, + REQUEST_TYPE_RENEWAL, + REQUEST_TYPE_RELEASE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RequestType {} + /** * Contains the opaque data an app uses to request keys from a license server */ @@ -608,18 +677,38 @@ public final class MediaDrm { /** * Get the opaque message data */ - public byte[] getData() { return mData; } + @NonNull + public byte[] getData() { + if (mData == null) { + // this should never happen as mData is initialized in + // JNI after construction of the KeyRequest object. The check + // is needed here to guarantee @NonNull annotation. + throw new RuntimeException("KeyRequest is not initialized"); + } + return mData; + } /** * Get the default URL to use when sending the key request message to a * server, if known. The app may prefer to use a different license * server URL from other sources. + * This method returns an empty string if the default URL is not known. */ - public String getDefaultUrl() { return mDefaultUrl; } + @NonNull + public String getDefaultUrl() { + if (mDefaultUrl == null) { + // this should never happen as mDefaultUrl is initialized in + // JNI after construction of the KeyRequest object. The check + // is needed here to guarantee @NonNull annotation. + throw new RuntimeException("KeyRequest is not initialized"); + } + return mDefaultUrl; + } /** * Get the type of the request */ + @RequestType public int getRequestType() { return mRequestType; } }; @@ -652,12 +741,15 @@ public final class MediaDrm { * keys, which are identified by a keySetId. * @param optionalParameters are included in the key request message to * allow a client application to provide additional message parameters to the server. - * + * This may be {@code null} if no additional parameters are to be sent. * @throws NotProvisionedException if reprovisioning is needed, due to a * problem with the certifcate */ - public native KeyRequest getKeyRequest(byte[] scope, byte[] init, - String mimeType, int keyType, HashMap<String, String> optionalParameters) + @NonNull + public native KeyRequest getKeyRequest( + @NonNull byte[] scope, @Nullable byte[] init, + @Nullable String mimeType, @KeyType int keyType, + @Nullable HashMap<String, String> optionalParameters) throws NotProvisionedException; @@ -680,7 +772,9 @@ public final class MediaDrm { * @throws DeniedByServerException if the response indicates that the * server rejected the request */ - public native byte[] provideKeyResponse(byte[] scope, byte[] response) + @Nullable + public native byte[] provideKeyResponse( + @NonNull byte[] scope, @NonNull byte[] response) throws NotProvisionedException, DeniedByServerException; @@ -691,14 +785,14 @@ public final class MediaDrm { * @param sessionId the session ID for the DRM session * @param keySetId identifies the saved key set to restore */ - public native void restoreKeys(byte[] sessionId, byte[] keySetId); + public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId); /** * Remove the current keys from a session. * * @param sessionId the session ID for the DRM session */ - public native void removeKeys(byte[] sessionId); + public native void removeKeys(@NonNull byte[] sessionId); /** * Request an informative description of the key status for the session. The status is @@ -709,7 +803,8 @@ public final class MediaDrm { * * @param sessionId the session ID for the DRM session */ - public native HashMap<String, String> queryKeyStatus(byte[] sessionId); + @NonNull + public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId); /** * Contains the opaque data an app uses to request a certificate from a provisioning @@ -721,14 +816,33 @@ public final class MediaDrm { /** * Get the opaque message data */ - public byte[] getData() { return mData; } + @NonNull + public byte[] getData() { + if (mData == null) { + // this should never happen as mData is initialized in + // JNI after construction of the KeyRequest object. The check + // is needed here to guarantee @NonNull annotation. + throw new RuntimeException("ProvisionRequest is not initialized"); + } + return mData; + } /** * Get the default URL to use when sending the provision request * message to a server, if known. The app may prefer to use a different * provisioning server URL obtained from other sources. + * This method returns an empty string if the default URL is not known. */ - public String getDefaultUrl() { return mDefaultUrl; } + @NonNull + public String getDefaultUrl() { + if (mDefaultUrl == null) { + // this should never happen as mDefaultUrl is initialized in + // JNI after construction of the ProvisionRequest object. The check + // is needed here to guarantee @NonNull annotation. + throw new RuntimeException("ProvisionRequest is not initialized"); + } + return mDefaultUrl; + } private byte[] mData; private String mDefaultUrl; @@ -743,12 +857,14 @@ public final class MediaDrm { * is returned in ProvisionRequest.data. The recommended URL to deliver the provision * request to is returned in ProvisionRequest.defaultUrl. */ + @NonNull public ProvisionRequest getProvisionRequest() { return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, ""); } + @NonNull private native ProvisionRequest getProvisionRequestNative(int certType, - String certAuthority); + @NonNull String certAuthority); /** * After a provision response is received by the app, it is provided to the DRM @@ -760,12 +876,14 @@ public final class MediaDrm { * @throws DeniedByServerException if the response indicates that the * server rejected the request */ - public void provideProvisionResponse(byte[] response) + public void provideProvisionResponse(@NonNull byte[] response) throws DeniedByServerException { provideProvisionResponseNative(response); } - private native Certificate provideProvisionResponseNative(byte[] response) + @NonNull + /* could there be a valid response with 0-sized certificate or key? */ + private native Certificate provideProvisionResponseNative(@NonNull byte[] response) throws DeniedByServerException; /** @@ -795,6 +913,7 @@ public final class MediaDrm { * record on the client is only removed after positive confirmation that the server * received the message using releaseSecureStops(). */ + @NonNull public native List<byte[]> getSecureStops(); /** @@ -802,7 +921,8 @@ public final class MediaDrm { * * @param ssid - The secure stop ID provided by the license server. */ - public native byte[] getSecureStop(byte[] ssid); + @NonNull + public native byte[] getSecureStop(@NonNull byte[] ssid); /** * Process the SecureStop server response message ssRelease. After authenticating @@ -810,7 +930,7 @@ public final class MediaDrm { * * @param ssRelease the server response indicating which secure stops to release */ - public native void releaseSecureStops(byte[] ssRelease); + public native void releaseSecureStops(@NonNull byte[] ssRelease); /** * Remove all secure stops without requiring interaction with the server. @@ -839,6 +959,16 @@ public final class MediaDrm { */ public static final String PROPERTY_ALGORITHMS = "algorithms"; + /** @hide */ + @StringDef({ + PROPERTY_VENDOR, + PROPERTY_VERSION, + PROPERTY_DESCRIPTION, + PROPERTY_ALGORITHMS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StringProperty {} + /** * Read a DRM engine plugin String property value, given the property name string. * <p> @@ -846,51 +976,68 @@ public final class MediaDrm { * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION}, * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS} */ - public native String getPropertyString(String propertyName); - + /* FIXME this throws IllegalStateException for invalid property names */ + @NonNull + public native String getPropertyString(@NonNull @StringProperty String propertyName); /** * Byte array property name: the device unique identifier is established during * device provisioning and provides a means of uniquely identifying each device. */ + /* FIXME this throws IllegalStateException for invalid property names */ public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; + /** @hide */ + @StringDef({ + PROPERTY_DEVICE_UNIQUE_ID, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ArrayProperty {} + /** * Read a DRM engine plugin byte array property value, given the property name string. * <p> * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID} */ - public native byte[] getPropertyByteArray(String propertyName); - + @NonNull + public native byte[] getPropertyByteArray(@ArrayProperty String propertyName); /** * Set a DRM engine plugin String property value. */ - public native void setPropertyString(String propertyName, String value); + public native void setPropertyString( + @StringProperty String propertyName, @NonNull String value); /** * Set a DRM engine plugin byte array property value. */ - public native void setPropertyByteArray(String propertyName, byte[] value); + public native void setPropertyByteArray( + @ArrayProperty String propertyName, @NonNull byte[] value); + private static final native void setCipherAlgorithmNative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); - private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId, - String algorithm); + private static final native void setMacAlgorithmNative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm); - private static final native void setMacAlgorithmNative(MediaDrm drm, byte[] sessionId, - String algorithm); + @NonNull + private static final native byte[] encryptNative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, + @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv); - private static final native byte[] encryptNative(MediaDrm drm, byte[] sessionId, - byte[] keyId, byte[] input, byte[] iv); + @NonNull + private static final native byte[] decryptNative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, + @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv); - private static final native byte[] decryptNative(MediaDrm drm, byte[] sessionId, - byte[] keyId, byte[] input, byte[] iv); + @NonNull + private static final native byte[] signNative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, + @NonNull byte[] keyId, @NonNull byte[] message); - private static final native byte[] signNative(MediaDrm drm, byte[] sessionId, - byte[] keyId, byte[] message); - - private static final native boolean verifyNative(MediaDrm drm, byte[] sessionId, - byte[] keyId, byte[] message, byte[] signature); + private static final native boolean verifyNative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, + @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature); /** * In addition to supporting decryption of DASH Common Encrypted Media, the @@ -919,8 +1066,8 @@ public final class MediaDrm { private MediaDrm mDrm; private byte[] mSessionId; - CryptoSession(MediaDrm drm, byte[] sessionId, - String cipherAlgorithm, String macAlgorithm) + CryptoSession(@NonNull MediaDrm drm, @NonNull byte[] sessionId, + @NonNull String cipherAlgorithm, @NonNull String macAlgorithm) { mSessionId = sessionId; mDrm = drm; @@ -935,7 +1082,9 @@ public final class MediaDrm { * @param input the data to encrypt * @param iv the initialization vector to use for the cipher */ - public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) { + @NonNull + public byte[] encrypt( + @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) { return encryptNative(mDrm, mSessionId, keyid, input, iv); } @@ -946,7 +1095,9 @@ public final class MediaDrm { * @param input the data to encrypt * @param iv the initialization vector to use for the cipher */ - public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) { + @NonNull + public byte[] decrypt( + @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) { return decryptNative(mDrm, mSessionId, keyid, input, iv); } @@ -956,7 +1107,8 @@ public final class MediaDrm { * @param keyid specifies which key to use * @param message the data for which a signature is to be computed */ - public byte[] sign(byte[] keyid, byte[] message) { + @NonNull + public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) { return signNative(mDrm, mSessionId, keyid, message); } @@ -969,7 +1121,8 @@ public final class MediaDrm { * @param signature the reference signature which will be compared with the * computed signature */ - public boolean verify(byte[] keyid, byte[] message, byte[] signature) { + public boolean verify( + @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) { return verifyNative(mDrm, mSessionId, keyid, message, signature); } }; @@ -994,8 +1147,9 @@ public final class MediaDrm { * using the method {@link #getPropertyString} with the property name * "algorithms". */ - public CryptoSession getCryptoSession(byte[] sessionId, - String cipherAlgorithm, String macAlgorithm) + public CryptoSession getCryptoSession( + @NonNull byte[] sessionId, + @NonNull String cipherAlgorithm, @NonNull String macAlgorithm) { return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm); } @@ -1010,7 +1164,7 @@ public final class MediaDrm { private byte[] mData; private String mDefaultUrl; - CertificateRequest(byte[] data, String defaultUrl) { + CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) { mData = data; mDefaultUrl = defaultUrl; } @@ -1018,6 +1172,7 @@ public final class MediaDrm { /** * Get the opaque message data */ + @NonNull public byte[] getData() { return mData; } /** @@ -1025,6 +1180,7 @@ public final class MediaDrm { * message to a server, if known. The app may prefer to use a different * certificate server URL obtained from other sources. */ + @NonNull public String getDefaultUrl() { return mDefaultUrl; } } @@ -1040,8 +1196,9 @@ public final class MediaDrm { * * @hide - not part of the public API at this time */ - public CertificateRequest getCertificateRequest(int certType, - String certAuthority) + @NonNull + public CertificateRequest getCertificateRequest( + @CertificateType int certType, @NonNull String certAuthority) { ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority); return new CertificateRequest(provisionRequest.getData(), @@ -1060,12 +1217,30 @@ public final class MediaDrm { /** * Get the wrapped private key data */ - public byte[] getWrappedPrivateKey() { return mWrappedKey; } + @NonNull + public byte[] getWrappedPrivateKey() { + if (mWrappedKey == null) { + // this should never happen as mWrappedKey is initialized in + // JNI after construction of the KeyRequest object. The check + // is needed here to guarantee @NonNull annotation. + throw new RuntimeException("Cerfificate is not initialized"); + } + return mWrappedKey; + } /** * Get the PEM-encoded certificate chain */ - public byte[] getContent() { return mCertificateData; } + @NonNull + public byte[] getContent() { + if (mCertificateData == null) { + // this should never happen as mCertificateData is initialized in + // JNI after construction of the KeyRequest object. The check + // is needed here to guarantee @NonNull annotation. + throw new RuntimeException("Cerfificate is not initialized"); + } + return mCertificateData; + } private byte[] mWrappedKey; private byte[] mCertificateData; @@ -1089,13 +1264,16 @@ public final class MediaDrm { * * @hide - not part of the public API at this time */ - public Certificate provideCertificateResponse(byte[] response) + @NonNull + public Certificate provideCertificateResponse(@NonNull byte[] response) throws DeniedByServerException { return provideProvisionResponseNative(response); } - private static final native byte[] signRSANative(MediaDrm drm, byte[] sessionId, - String algorithm, byte[] wrappedKey, byte[] message); + @NonNull + private static final native byte[] signRSANative( + @NonNull MediaDrm drm, @NonNull byte[] sessionId, + @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message); /** * Sign data using an RSA key @@ -1108,8 +1286,10 @@ public final class MediaDrm { * * @hide - not part of the public API at this time */ - public byte[] signRSA(byte[] sessionId, String algorithm, - byte[] wrappedKey, byte[] message) { + @NonNull + public byte[] signRSA( + @NonNull byte[] sessionId, @NonNull String algorithm, + @NonNull byte[] wrappedKey, @NonNull byte[] message) { return signRSANative(this, sessionId, algorithm, wrappedKey, message); } diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index b4acbc0..0bf995f 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -16,6 +16,9 @@ package android.media; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; @@ -27,6 +30,8 @@ import android.os.IBinder; import java.io.FileDescriptor; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; @@ -69,21 +74,26 @@ final public class MediaExtractor { * Sets the data source (MediaDataSource) to use. * * @param dataSource the MediaDataSource for the media you want to extract from + * + * @throws IllegalArgumentException if dataSource is invalid. */ - public native final void setDataSource(MediaDataSource dataSource) throws IllegalArgumentException, IOException; + public native final void setDataSource(@NonNull MediaDataSource dataSource) + throws IOException; /** * Sets the data source as a content Uri. * * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to extract from. - * @param headers the headers to be sent together with the request for the data + * @param headers the headers to be sent together with the request for the data. + * This can be {@code null} if no specific headers are to be sent with the + * request. */ public final void setDataSource( - Context context, Uri uri, Map<String, String> headers) + @NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers) throws IOException { String scheme = uri.getScheme(); - if(scheme == null || scheme.equals("file")) { + if (scheme == null || scheme.equals("file")) { setDataSource(uri.getPath()); return; } @@ -122,9 +132,11 @@ final public class MediaExtractor { * Sets the data source (file-path or http URL) to use. * * @param path the path of the file, or the http URL - * @param headers the headers associated with the http request for the stream you want to play + * @param headers the headers associated with the http request for the stream you want to play. + * This can be {@code null} if no specific headers are to be sent with the + * request. */ - public final void setDataSource(String path, Map<String, String> headers) + public final void setDataSource(@NonNull String path, @Nullable Map<String, String> headers) throws IOException { String[] keys = null; String[] values = null; @@ -149,10 +161,10 @@ final public class MediaExtractor { } private native final void nativeSetDataSource( - IBinder httpServiceBinder, - String path, - String[] keys, - String[] values) throws IOException; + @NonNull IBinder httpServiceBinder, + @NonNull String path, + @Nullable String[] keys, + @Nullable String[] values) throws IOException; /** * Sets the data source (file-path or http URL) to use. @@ -166,7 +178,7 @@ final public class MediaExtractor { * As an alternative, the application could first open the file for reading, * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. */ - public final void setDataSource(String path) throws IOException { + public final void setDataSource(@NonNull String path) throws IOException { nativeSetDataSource( MediaHTTPService.createHttpServiceBinderIfNecessary(path), path, @@ -180,7 +192,7 @@ final public class MediaExtractor { * * @param fd the FileDescriptor for the file you want to extract from. */ - public final void setDataSource(FileDescriptor fd) throws IOException { + public final void setDataSource(@NonNull FileDescriptor fd) throws IOException { setDataSource(fd, 0, 0x7ffffffffffffffL); } @@ -194,7 +206,7 @@ final public class MediaExtractor { * @param length the length in bytes of the data to be extracted */ public native final void setDataSource( - FileDescriptor fd, long offset, long length) throws IOException; + @NonNull FileDescriptor fd, long offset, long length) throws IOException; @Override protected void finalize() { @@ -217,7 +229,9 @@ final public class MediaExtractor { * Get the PSSH info if present. * @return a map of uuid-to-bytes, with the uuid specifying * the crypto scheme, and the bytes being the data specific to that scheme. + * This can be {@code null} if the source does not contain PSSH info. */ + @Nullable public Map<UUID, byte[]> getPsshInfo() { Map<UUID, byte[]> psshMap = null; Map<String, Object> formatMap = getFileFormatNative(); @@ -243,16 +257,19 @@ final public class MediaExtractor { return psshMap; } + @NonNull private native Map<String, Object> getFileFormatNative(); /** * Get the track format at the specified index. * More detail on the representation can be found at {@link android.media.MediaCodec} */ + @NonNull public MediaFormat getTrackFormat(int index) { return new MediaFormat(getTrackFormatNative(index)); } + @NonNull private native Map<String, Object> getTrackFormatNative(int index); /** @@ -284,11 +301,20 @@ final public class MediaExtractor { */ public static final int SEEK_TO_CLOSEST_SYNC = 2; + /** @hide */ + @IntDef({ + SEEK_TO_PREVIOUS_SYNC, + SEEK_TO_NEXT_SYNC, + SEEK_TO_CLOSEST_SYNC, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SeekMode {} + /** * All selected tracks seek near the requested time according to the * specified mode. */ - public native void seekTo(long timeUs, int mode); + public native void seekTo(long timeUs, @SeekMode int mode); /** * Advance to the next sample. Returns false if no more sample data @@ -305,7 +331,7 @@ final public class MediaExtractor { * @param byteBuf the destination byte buffer * @return the sample size (or -1 if no more samples are available). */ - public native int readSampleData(ByteBuffer byteBuf, int offset); + public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset); /** * Returns the track index the current sample originates from (or -1 @@ -334,9 +360,20 @@ final public class MediaExtractor { */ public static final int SAMPLE_FLAG_ENCRYPTED = 2; + /** @hide */ + @IntDef( + flag = true, + value = { + SAMPLE_FLAG_SYNC, + SAMPLE_FLAG_ENCRYPTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SampleFlag {} + /** * Returns the current sample's flags. */ + @SampleFlag public native int getSampleFlags(); /** @@ -347,7 +384,7 @@ final public class MediaExtractor { * to be filled in. * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED} */ - public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info); + public native boolean getSampleCryptoInfo(@NonNull MediaCodec.CryptoInfo info); /** * Returns an estimate of how much data is presently cached in memory diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index f518ab2..4b6b4fa 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -16,12 +16,18 @@ package android.media; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; import dalvik.system.CloseGuard; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.Map; @@ -80,19 +86,27 @@ final public class MediaMuxer { public static final int MUXER_OUTPUT_WEBM = 1; }; + /** @hide */ + @IntDef({ + OutputFormat.MUXER_OUTPUT_MPEG_4, + OutputFormat.MUXER_OUTPUT_WEBM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Format {} + // All the native functions are listed here. - private static native long nativeSetup(FileDescriptor fd, int format); + private static native long nativeSetup(@NonNull FileDescriptor fd, int format); private static native void nativeRelease(long nativeObject); private static native void nativeStart(long nativeObject); private static native void nativeStop(long nativeObject); - private static native int nativeAddTrack(long nativeObject, String[] keys, - Object[] values); - private static native void nativeSetOrientationHint(long nativeObject, - int degrees); + private static native int nativeAddTrack( + long nativeObject, @NonNull String[] keys, @NonNull Object[] values); + private static native void nativeSetOrientationHint( + long nativeObject, int degrees); private static native void nativeSetLocation(long nativeObject, int latitude, int longitude); - private static native void nativeWriteSampleData(long nativeObject, - int trackIndex, ByteBuffer byteBuf, - int offset, int size, long presentationTimeUs, int flags); + private static native void nativeWriteSampleData( + long nativeObject, int trackIndex, @NonNull ByteBuffer byteBuf, + int offset, int size, long presentationTimeUs, @MediaCodec.BufferFlag int flags); // Muxer internal states. private static final int MUXER_STATE_UNINITIALIZED = -1; @@ -115,7 +129,7 @@ final public class MediaMuxer { * @see android.media.MediaMuxer.OutputFormat * @throws IOException if failed to open the file for write */ - public MediaMuxer(String path, int format) throws IOException { + public MediaMuxer(@NonNull String path, @Format int format) throws IOException { if (path == null) { throw new IllegalArgumentException("path must not be null"); } @@ -246,11 +260,12 @@ final public class MediaMuxer { /** * Adds a track with the specified format. - * @param format The media format for the track. + * @param format The media format for the track. This must not be an empty + * MediaFormat. * @return The track index for this newly added track, and it should be used * in the {@link #writeSampleData}. */ - public int addTrack(MediaFormat format) { + public int addTrack(@NonNull MediaFormat format) { if (format == null) { throw new IllegalArgumentException("format must not be null."); } @@ -302,8 +317,8 @@ final public class MediaMuxer { * MediaMuxer uses the flags provided in {@link MediaCodec.BufferInfo}, * to signal sync frames. */ - public void writeSampleData(int trackIndex, ByteBuffer byteBuf, - BufferInfo bufferInfo) { + public void writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf, + @NonNull BufferInfo bufferInfo) { if (trackIndex < 0 || trackIndex > mLastTrackIndex) { throw new IllegalArgumentException("trackIndex is invalid"); } diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java index e6bc10d..74a2fb2 100644 --- a/media/java/android/media/MediaSync.java +++ b/media/java/android/media/MediaSync.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.media.AudioTrack; import android.os.Handler; import android.os.Looper; @@ -120,9 +121,10 @@ final public class MediaSync { * * @param sync The MediaSync object. * @param audioBuffer The returned audio buffer. + * @param bufferIndex The index associated with the audio buffer */ public abstract void onReturnAudioBuffer( - MediaSync sync, ByteBuffer audioBuffer, int bufferIndex); + @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex); } private static final String TAG = "MediaSync"; @@ -138,7 +140,7 @@ final public class MediaSync { public int mSizeInBytes; long mPresentationTimeUs; - public AudioBuffer(ByteBuffer byteBuffer, int bufferIndex, + public AudioBuffer(@NonNull ByteBuffer byteBuffer, int bufferIndex, int sizeInBytes, long presentationTimeUs) { mByteBuffer = byteBuffer; mBufferIndex = bufferIndex; @@ -200,13 +202,17 @@ final public class MediaSync { /** * Sets an asynchronous callback for actionable MediaSync events. - * It shouldn't be called inside callback. + * <p> + * This method can be called multiple times to update a previously set callback. If the + * handler is changed, undelivered notifications scheduled for the old handler may be dropped. + * <p> + * <b>Do not call this inside callback.</b> * - * @param cb The callback that will run. - * @param handler The Handler that will run the callback. Using null means to use MediaSync's + * @param cb The callback that will run. Use {@code null} to stop receiving callbacks. + * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's * internal handler if it exists. */ - public void setCallback(/* MediaSync. */ Callback cb, Handler handler) { + public void setCallback(@Nullable /* MediaSync. */ Callback cb, @Nullable Handler handler) { synchronized(mCallbackLock) { if (handler != null) { mCallbackHandler = handler; @@ -235,11 +241,11 @@ final public class MediaSync { * @throws IllegalStateException if not in the Initialized state, or another surface * has already been configured. */ - public void configureSurface(Surface surface) { + public void configureSurface(@Nullable Surface surface) { native_configureSurface(surface); } - private native final void native_configureSurface(Surface surface); + private native final void native_configureSurface(@Nullable Surface surface); /** * Configures the audio track for MediaSync. @@ -249,7 +255,7 @@ final public class MediaSync { * @throws IllegalStateException if not in the Initialized state, or another audio track * has already been configured. */ - public void configureAudioTrack(AudioTrack audioTrack) { + public void configureAudioTrack(@Nullable AudioTrack audioTrack) { // AudioTrack has sanity check for configured sample rate. int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate()); @@ -261,7 +267,7 @@ final public class MediaSync { } private native final void native_configureAudioTrack( - AudioTrack audioTrack, int nativeSampleRateInHz); + @Nullable AudioTrack audioTrack, int nativeSampleRateInHz); /** * Requests a Surface to use as the input. This may only be called after @@ -272,6 +278,7 @@ final public class MediaSync { * @throws IllegalStateException if not configured, or another input surface has * already been created. */ + @NonNull public native final Surface createInputSurface(); /** @@ -412,7 +419,7 @@ final public class MediaSync { return native_getTimestamp(timestamp); } - private native final boolean native_getTimestamp(MediaTimestamp timestamp); + private native final boolean native_getTimestamp(@NonNull MediaTimestamp timestamp); /** * Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode). @@ -427,7 +434,8 @@ final public class MediaSync { * has not been done correctly. */ public void queueAudio( - ByteBuffer audioData, int bufferIndex, int sizeInBytes, long presentationTimeUs) { + @NonNull ByteBuffer audioData, int bufferIndex, int sizeInBytes, + long presentationTimeUs) { if (mAudioTrack == null || mAudioThread == null) { throw new IllegalStateException( "AudioTrack is NOT configured or audio thread is not created"); @@ -489,7 +497,7 @@ final public class MediaSync { private native final void native_updateQueuedAudioData( int sizeInBytes, long presentationTimeUs); - private final void postReturnByteBuffer(final AudioBuffer audioBuffer) { + private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) { synchronized(mCallbackLock) { if (mCallbackHandler != null) { final MediaSync sync = this; |