MediaCas: address comments for API reviews
- Wrap session id byte array in Session object - Move session operations from MediaCas to Session - Remove position prarameters on descramble() method - Retrieve cas info for a track by getCasInfo() instead of getDrmInitData(). bug: 22804304 bug: 36791613 bug: 36783335 Change-Id: Ib3ad8d6a2f679c0e60d2bb025ac5999339722306
This commit is contained in:
@@ -21818,24 +21818,20 @@ package android.media {
|
||||
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
|
||||
}
|
||||
|
||||
public final class MediaCas {
|
||||
public final class MediaCas implements java.lang.AutoCloseable {
|
||||
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
|
||||
method public void closeSession(byte[]);
|
||||
method public void close();
|
||||
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
|
||||
method public static boolean isSystemIdSupported(int);
|
||||
method public byte[] openSession(int) throws android.media.MediaCasException;
|
||||
method public byte[] openSession(int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[], byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[], byte[]) throws android.media.MediaCasException;
|
||||
method public android.media.MediaCas.Session openSession(int) throws android.media.MediaCasException;
|
||||
method public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException;
|
||||
method public void processEmm(byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEmm(byte[]) throws android.media.MediaCasException;
|
||||
method public void provision(java.lang.String) throws android.media.MediaCasException;
|
||||
method public void refreshEntitlements(int, byte[]) throws android.media.MediaCasException;
|
||||
method public void release();
|
||||
method public void sendEvent(int, int, byte[]) throws android.media.MediaCasException;
|
||||
method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
|
||||
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
|
||||
method public void setSessionPrivateData(byte[], byte[]) throws android.media.MediaCasException;
|
||||
}
|
||||
|
||||
public static abstract interface MediaCas.EventListener {
|
||||
@@ -21847,6 +21843,13 @@ package android.media {
|
||||
method public int getSystemId();
|
||||
}
|
||||
|
||||
public final class MediaCas.Session implements java.lang.AutoCloseable {
|
||||
method public void close();
|
||||
method public void processEcm(byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[]) throws android.media.MediaCasException;
|
||||
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
|
||||
}
|
||||
|
||||
public class MediaCasException extends java.lang.Exception {
|
||||
}
|
||||
|
||||
@@ -22290,12 +22293,12 @@ package android.media {
|
||||
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
|
||||
}
|
||||
|
||||
public final class MediaDescrambler {
|
||||
public final class MediaDescrambler implements java.lang.AutoCloseable {
|
||||
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
|
||||
method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
|
||||
method public final void release();
|
||||
method public void close();
|
||||
method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
|
||||
method public final boolean requiresSecureDecoderComponent(java.lang.String);
|
||||
method public final void setMediaCasSession(byte[]);
|
||||
method public final void setMediaCasSession(android.media.MediaCas.Session);
|
||||
}
|
||||
|
||||
public class MediaDescription implements android.os.Parcelable {
|
||||
@@ -22433,6 +22436,7 @@ package android.media {
|
||||
ctor public MediaExtractor();
|
||||
method public boolean advance();
|
||||
method public long getCachedDuration();
|
||||
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
|
||||
method public android.media.DrmInitData getDrmInitData();
|
||||
method public android.media.MediaMetricsSet getMetrics();
|
||||
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
|
||||
@@ -22464,6 +22468,11 @@ package android.media {
|
||||
field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0
|
||||
}
|
||||
|
||||
public static final class MediaExtractor.CasInfo {
|
||||
method public android.media.MediaCas.Session getSession();
|
||||
method public int getSystemId();
|
||||
}
|
||||
|
||||
public final class MediaFormat {
|
||||
ctor public MediaFormat();
|
||||
method public final boolean containsKey(java.lang.String);
|
||||
|
||||
@@ -23643,24 +23643,20 @@ package android.media {
|
||||
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
|
||||
}
|
||||
|
||||
public final class MediaCas {
|
||||
public final class MediaCas implements java.lang.AutoCloseable {
|
||||
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
|
||||
method public void closeSession(byte[]);
|
||||
method public void close();
|
||||
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
|
||||
method public static boolean isSystemIdSupported(int);
|
||||
method public byte[] openSession(int) throws android.media.MediaCasException;
|
||||
method public byte[] openSession(int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[], byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[], byte[]) throws android.media.MediaCasException;
|
||||
method public android.media.MediaCas.Session openSession(int) throws android.media.MediaCasException;
|
||||
method public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException;
|
||||
method public void processEmm(byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEmm(byte[]) throws android.media.MediaCasException;
|
||||
method public void provision(java.lang.String) throws android.media.MediaCasException;
|
||||
method public void refreshEntitlements(int, byte[]) throws android.media.MediaCasException;
|
||||
method public void release();
|
||||
method public void sendEvent(int, int, byte[]) throws android.media.MediaCasException;
|
||||
method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
|
||||
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
|
||||
method public void setSessionPrivateData(byte[], byte[]) throws android.media.MediaCasException;
|
||||
}
|
||||
|
||||
public static abstract interface MediaCas.EventListener {
|
||||
@@ -23672,6 +23668,13 @@ package android.media {
|
||||
method public int getSystemId();
|
||||
}
|
||||
|
||||
public final class MediaCas.Session implements java.lang.AutoCloseable {
|
||||
method public void close();
|
||||
method public void processEcm(byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[]) throws android.media.MediaCasException;
|
||||
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
|
||||
}
|
||||
|
||||
public class MediaCasException extends java.lang.Exception {
|
||||
}
|
||||
|
||||
@@ -24115,12 +24118,12 @@ package android.media {
|
||||
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
|
||||
}
|
||||
|
||||
public final class MediaDescrambler {
|
||||
public final class MediaDescrambler implements java.lang.AutoCloseable {
|
||||
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
|
||||
method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
|
||||
method public final void release();
|
||||
method public void close();
|
||||
method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
|
||||
method public final boolean requiresSecureDecoderComponent(java.lang.String);
|
||||
method public final void setMediaCasSession(byte[]);
|
||||
method public final void setMediaCasSession(android.media.MediaCas.Session);
|
||||
}
|
||||
|
||||
public class MediaDescription implements android.os.Parcelable {
|
||||
@@ -24258,6 +24261,7 @@ package android.media {
|
||||
ctor public MediaExtractor();
|
||||
method public boolean advance();
|
||||
method public long getCachedDuration();
|
||||
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
|
||||
method public android.media.DrmInitData getDrmInitData();
|
||||
method public android.media.MediaMetricsSet getMetrics();
|
||||
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
|
||||
@@ -24289,6 +24293,11 @@ package android.media {
|
||||
field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0
|
||||
}
|
||||
|
||||
public static final class MediaExtractor.CasInfo {
|
||||
method public android.media.MediaCas.Session getSession();
|
||||
method public int getSystemId();
|
||||
}
|
||||
|
||||
public final class MediaFormat {
|
||||
ctor public MediaFormat();
|
||||
method public final boolean containsKey(java.lang.String);
|
||||
|
||||
@@ -21931,24 +21931,20 @@ package android.media {
|
||||
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
|
||||
}
|
||||
|
||||
public final class MediaCas {
|
||||
public final class MediaCas implements java.lang.AutoCloseable {
|
||||
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
|
||||
method public void closeSession(byte[]);
|
||||
method public void close();
|
||||
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
|
||||
method public static boolean isSystemIdSupported(int);
|
||||
method public byte[] openSession(int) throws android.media.MediaCasException;
|
||||
method public byte[] openSession(int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[], byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[], byte[]) throws android.media.MediaCasException;
|
||||
method public android.media.MediaCas.Session openSession(int) throws android.media.MediaCasException;
|
||||
method public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException;
|
||||
method public void processEmm(byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEmm(byte[]) throws android.media.MediaCasException;
|
||||
method public void provision(java.lang.String) throws android.media.MediaCasException;
|
||||
method public void refreshEntitlements(int, byte[]) throws android.media.MediaCasException;
|
||||
method public void release();
|
||||
method public void sendEvent(int, int, byte[]) throws android.media.MediaCasException;
|
||||
method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
|
||||
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
|
||||
method public void setSessionPrivateData(byte[], byte[]) throws android.media.MediaCasException;
|
||||
}
|
||||
|
||||
public static abstract interface MediaCas.EventListener {
|
||||
@@ -21960,6 +21956,13 @@ package android.media {
|
||||
method public int getSystemId();
|
||||
}
|
||||
|
||||
public final class MediaCas.Session implements java.lang.AutoCloseable {
|
||||
method public void close();
|
||||
method public void processEcm(byte[], int, int) throws android.media.MediaCasException;
|
||||
method public void processEcm(byte[]) throws android.media.MediaCasException;
|
||||
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
|
||||
}
|
||||
|
||||
public class MediaCasException extends java.lang.Exception {
|
||||
}
|
||||
|
||||
@@ -22403,12 +22406,12 @@ package android.media {
|
||||
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
|
||||
}
|
||||
|
||||
public final class MediaDescrambler {
|
||||
public final class MediaDescrambler implements java.lang.AutoCloseable {
|
||||
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
|
||||
method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
|
||||
method public final void release();
|
||||
method public void close();
|
||||
method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
|
||||
method public final boolean requiresSecureDecoderComponent(java.lang.String);
|
||||
method public final void setMediaCasSession(byte[]);
|
||||
method public final void setMediaCasSession(android.media.MediaCas.Session);
|
||||
}
|
||||
|
||||
public class MediaDescription implements android.os.Parcelable {
|
||||
@@ -22546,6 +22549,7 @@ package android.media {
|
||||
ctor public MediaExtractor();
|
||||
method public boolean advance();
|
||||
method public long getCachedDuration();
|
||||
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
|
||||
method public android.media.DrmInitData getDrmInitData();
|
||||
method public android.media.MediaMetricsSet getMetrics();
|
||||
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
|
||||
@@ -22577,6 +22581,11 @@ package android.media {
|
||||
field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0
|
||||
}
|
||||
|
||||
public static final class MediaExtractor.CasInfo {
|
||||
method public android.media.MediaCas.Session getSession();
|
||||
method public int getSystemId();
|
||||
}
|
||||
|
||||
public final class MediaFormat {
|
||||
ctor public MediaFormat();
|
||||
method public final boolean containsKey(java.lang.String);
|
||||
|
||||
@@ -51,12 +51,13 @@ import android.util.Singleton;
|
||||
* management messages) can be distributed out-of-band, or in-band with the stream.
|
||||
* <p>
|
||||
* To descramble elementary streams, the app first calls {@link #openSession} to
|
||||
* generate a sessionId that will uniquely identify a session. A session provides
|
||||
* a context for subsequent key updates and descrambling activities. The ECMs
|
||||
* (Entitlement control messages) are sent to the session via method {@link #processEcm}.
|
||||
* generate a {@link Session} object that will uniquely identify a session. A session
|
||||
* provides a context for subsequent key updates and descrambling activities. The ECMs
|
||||
* (Entitlement control messages) are sent to the session via method
|
||||
* {@link Session#processEcm}.
|
||||
* <p>
|
||||
* The app next constructs a MediaDescrambler object, and initializes it with the
|
||||
* sessionId using {@link MediaDescrambler#setMediaCasSession}. This ties the
|
||||
* session using {@link MediaDescrambler#setMediaCasSession}. This ties the
|
||||
* descrambler to the session, and the descrambler can then be used to descramble
|
||||
* content secured with the session's key, either during extraction, or during decoding
|
||||
* with {@link android.media.MediaCodec}.
|
||||
@@ -79,19 +80,20 @@ import android.util.Singleton;
|
||||
* If the app uses {@link MediaExtractor}, it can delegate the CAS session
|
||||
* management to MediaExtractor by calling {@link MediaExtractor#setMediaCas}.
|
||||
* MediaExtractor will take over and call {@link #openSession}, {@link #processEmm}
|
||||
* and/or {@link #processEcm}, etc.. if necessary.
|
||||
* and/or {@link Session#processEcm}, etc.. if necessary.
|
||||
* <p>
|
||||
* When using {@link MediaExtractor}, the app would still need a MediaDescrambler
|
||||
* to use with {@link MediaCodec} if the licensing requires a secure decoder. The
|
||||
* sessionId of the descrambler can be retrieved by {@link MediaExtractor#getDrmInitData}
|
||||
* and used to initialize a MediaDescrambler object for MediaCodec.
|
||||
* session associated with the descrambler of a track can be retrieved by calling
|
||||
* {@link MediaExtractor#getCasInfo}, and used to initialize a MediaDescrambler
|
||||
* object for MediaCodec.
|
||||
* <p>
|
||||
* <h3>Listeners</h3>
|
||||
* <p>The app may register a listener to receive events from the CA system using
|
||||
* method {@link #setEventListener}. The exact format of the event is scheme-specific
|
||||
* and is not specified by this API.
|
||||
*/
|
||||
public final class MediaCas {
|
||||
public final class MediaCas implements AutoCloseable {
|
||||
private static final String TAG = "MediaCas";
|
||||
private final ParcelableCasData mCasData = new ParcelableCasData();
|
||||
private ICas mICas;
|
||||
@@ -228,6 +230,106 @@ public final class MediaCas {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for an open session with the CA system.
|
||||
*/
|
||||
public final class Session implements AutoCloseable {
|
||||
final byte[] mSessionId;
|
||||
|
||||
Session(@NonNull byte[] sessionId) {
|
||||
mSessionId = sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the private data for a session.
|
||||
*
|
||||
* @param data byte array of the private data.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void setPrivateData(@NonNull byte[] data)
|
||||
throws MediaCasException {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mICas.setSessionPrivateData(mSessionId, data);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
cleanupAndRethrowIllegalState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a received ECM packet to the specified session of the CA system.
|
||||
*
|
||||
* @param data byte array of the ECM data.
|
||||
* @param offset position within data where the ECM data begins.
|
||||
* @param length length of the data (starting from offset).
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void processEcm(@NonNull byte[] data, int offset, int length)
|
||||
throws MediaCasException {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mCasData.set(data, offset, length);
|
||||
mICas.processEcm(mSessionId, mCasData);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
cleanupAndRethrowIllegalState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a received ECM packet to the specified session of the CA system.
|
||||
* This is similar to {@link Session#processEcm(byte[], int, int)}
|
||||
* except that the entire byte array is sent.
|
||||
*
|
||||
* @param data byte array of the ECM data.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void processEcm(@NonNull byte[] data) throws MediaCasException {
|
||||
processEcm(data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mICas.closeSession(mSessionId);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasStateException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
cleanupAndRethrowIllegalState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Session createFromSessionId(byte[] sessionId) {
|
||||
if (sessionId == null || sessionId.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return new Session(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for parceling CAS plugin descriptors over IMediaCasService binder.
|
||||
*/
|
||||
@@ -408,17 +510,17 @@ public final class MediaCas {
|
||||
*
|
||||
* @param programNumber program_number of the program (as in ISO/IEC13818-1).
|
||||
*
|
||||
* @return session id of the newly opened session.
|
||||
* @return session the newly opened session.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public byte[] openSession(int programNumber) throws MediaCasException {
|
||||
public Session openSession(int programNumber) throws MediaCasException {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
return mICas.openSession(programNumber);
|
||||
return createFromSessionId(mICas.openSession(programNumber));
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
@@ -433,18 +535,18 @@ public final class MediaCas {
|
||||
* @param programNumber program_number of the stream (as in ISO/IEC13818-1).
|
||||
* @param elementaryPID elementary_PID of the stream (as in ISO/IEC13818-1).
|
||||
*
|
||||
* @return session id of the newly opened session.
|
||||
* @return session the newly opened session.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public byte[] openSession(int programNumber, int elementaryPID)
|
||||
public Session openSession(int programNumber, int elementaryPID)
|
||||
throws MediaCasException {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
return mICas.openSessionForStream(programNumber, elementaryPID);
|
||||
return createFromSessionId(mICas.openSessionForStream(programNumber, elementaryPID));
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
@@ -453,92 +555,6 @@ public final class MediaCas {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the specified session.
|
||||
*
|
||||
* @param sessionId the session to be closed.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void closeSession(@NonNull byte[] sessionId) {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mICas.closeSession(sessionId);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasStateException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
cleanupAndRethrowIllegalState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the private data for a session.
|
||||
*
|
||||
* @param sessionId the session for which the private data is intended.
|
||||
* @param data byte array of the private data.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void setSessionPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data)
|
||||
throws MediaCasException {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mICas.setSessionPrivateData(sessionId, data);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
cleanupAndRethrowIllegalState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a received ECM packet to the specified session of the CA system.
|
||||
*
|
||||
* @param sessionId the session for which the ECM is intended.
|
||||
* @param data byte array of the ECM data.
|
||||
* @param offset position within data where the ECM data begins.
|
||||
* @param length length of the data (starting from offset).
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data,
|
||||
int offset, int length) throws MediaCasException {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mCasData.set(data, offset, length);
|
||||
mICas.processEcm(sessionId, mCasData);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
cleanupAndRethrowIllegalState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a received ECM packet to the specified session of the CA system.
|
||||
* This is similar to {@link #processEcm(byte[], byte[], int, int)}
|
||||
* except that the entire byte array is sent.
|
||||
*
|
||||
* @param sessionId the session for which the ECM is intended.
|
||||
* @param data byte array of the ECM data.
|
||||
*
|
||||
* @throws IllegalStateException if the MediaCas instance is not valid.
|
||||
* @throws MediaCasException for CAS-specific errors.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data)
|
||||
throws MediaCasException {
|
||||
processEcm(sessionId, data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a received EMM packet to the CA system.
|
||||
*
|
||||
@@ -650,10 +666,8 @@ public final class MediaCas {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the MediaCas instance.
|
||||
*/
|
||||
public void release() {
|
||||
@Override
|
||||
public void close() {
|
||||
if (mICas != null) {
|
||||
try {
|
||||
mICas.release();
|
||||
@@ -666,6 +680,6 @@ public final class MediaCas {
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
release();
|
||||
close();
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ import java.nio.ByteBuffer;
|
||||
* Scrambling schemes are identified by 16-bit unsigned integer as in CA_system_id.
|
||||
*
|
||||
*/
|
||||
public final class MediaDescrambler {
|
||||
public final class MediaDescrambler implements AutoCloseable {
|
||||
private static final String TAG = "MediaDescrambler";
|
||||
private IDescrambler mIDescrambler;
|
||||
|
||||
@@ -141,17 +141,17 @@ public final class MediaDescrambler {
|
||||
* android.media.MediaCodec#queueSecureInputBuffer} by specifying even
|
||||
* or odd key in the {@link android.media.MediaCodec.CryptoInfo#key} field.
|
||||
*
|
||||
* @param sessionId the MediaCas sessionId to associate with this
|
||||
* @param session the MediaCas session to associate with this
|
||||
* MediaDescrambler instance.
|
||||
*
|
||||
* @throws IllegalStateException if the descrambler instance is not valid.
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public final void setMediaCasSession(@NonNull byte[] sessionId) {
|
||||
public final void setMediaCasSession(@NonNull MediaCas.Session session) {
|
||||
validateInternalStates();
|
||||
|
||||
try {
|
||||
mIDescrambler.setMediaCasSession(sessionId);
|
||||
mIDescrambler.setMediaCasSession(session.mSessionId);
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasStateException.throwExceptions(e);
|
||||
} catch (RemoteException e) {
|
||||
@@ -163,11 +163,10 @@ public final class MediaDescrambler {
|
||||
* Descramble a ByteBuffer of data described by a
|
||||
* {@link android.media.MediaCodec.CryptoInfo} structure.
|
||||
*
|
||||
* @param srcBuf ByteBuffer containing the scrambled data.
|
||||
* @param srcPos position within src where the scrambled data starts.
|
||||
* @param dstBuf ByteBuffer to descramble into. If null, descrambling will happen
|
||||
* in-place and src will be used as dst.
|
||||
* @param dstPos position within dst to put the descrambled data.
|
||||
* @param srcBuf ByteBuffer containing the scrambled data, which starts at
|
||||
* srcBuf.position().
|
||||
* @param dstBuf ByteBuffer to hold the descrambled data, which starts at
|
||||
* dstBuf.position().
|
||||
* @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure
|
||||
* describing the subsamples contained in src.
|
||||
*
|
||||
@@ -178,7 +177,7 @@ public final class MediaDescrambler {
|
||||
* @throws MediaCasStateException for CAS-specific state exceptions.
|
||||
*/
|
||||
public final int descramble(
|
||||
@NonNull ByteBuffer srcBuf, int srcPos, ByteBuffer dstBuf, int dstPos,
|
||||
@NonNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf,
|
||||
@NonNull MediaCodec.CryptoInfo cryptoInfo) {
|
||||
validateInternalStates();
|
||||
|
||||
@@ -208,14 +207,16 @@ public final class MediaDescrambler {
|
||||
cryptoInfo.numSubSamples,
|
||||
cryptoInfo.numBytesOfClearData,
|
||||
cryptoInfo.numBytesOfEncryptedData,
|
||||
srcBuf, srcPos, dstBuf, dstPos);
|
||||
srcBuf, srcBuf.position(), srcBuf.limit(),
|
||||
dstBuf, dstBuf.position(), dstBuf.limit());
|
||||
} catch (ServiceSpecificException e) {
|
||||
MediaCasStateException.throwExceptions(e);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public final void release() {
|
||||
@Override
|
||||
public void close() {
|
||||
if (mIDescrambler != null) {
|
||||
try {
|
||||
mIDescrambler.release();
|
||||
@@ -229,7 +230,7 @@ public final class MediaDescrambler {
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
release();
|
||||
close();
|
||||
}
|
||||
|
||||
private static native final void native_init();
|
||||
@@ -237,7 +238,8 @@ public final class MediaDescrambler {
|
||||
private native final void native_release();
|
||||
private native final int native_descramble(
|
||||
byte key, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
|
||||
@NonNull ByteBuffer srcBuf, int srcOffset, ByteBuffer dstBuf, int dstOffset);
|
||||
@NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit,
|
||||
ByteBuffer dstBuf, int dstOffset, int dstLimit);
|
||||
|
||||
static {
|
||||
System.loadLibrary("media_jni");
|
||||
|
||||
@@ -259,11 +259,71 @@ final public class MediaExtractor {
|
||||
* @param mediaCas the MediaCas object to use.
|
||||
*/
|
||||
public final void setMediaCas(@NonNull MediaCas mediaCas) {
|
||||
mMediaCas = mediaCas;
|
||||
nativeSetMediaCas(mediaCas.getBinder());
|
||||
}
|
||||
|
||||
private native final void nativeSetMediaCas(@NonNull IBinder casBinder);
|
||||
|
||||
/**
|
||||
* Describes the conditional access system used to scramble a track.
|
||||
*/
|
||||
public static final class CasInfo {
|
||||
private final int mSystemId;
|
||||
private final MediaCas.Session mSession;
|
||||
|
||||
CasInfo(int systemId, @Nullable MediaCas.Session session) {
|
||||
mSystemId = systemId;
|
||||
mSession = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the system id of the conditional access system.
|
||||
*
|
||||
* @return CA system id of the CAS used to scramble the track.
|
||||
*/
|
||||
public int getSystemId() {
|
||||
return mSystemId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link MediaCas.Session} associated with a track. The
|
||||
* session is needed to initialize a descrambler in order to decode the
|
||||
* scrambled track.
|
||||
* <p>
|
||||
* @see MediaDescrambler#setMediaCasSession
|
||||
* <p>
|
||||
* @return a {@link MediaCas.Session} object associated with a track.
|
||||
*/
|
||||
public MediaCas.Session getSession() {
|
||||
return mSession;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the information about the conditional access system used to scramble
|
||||
* a track.
|
||||
*
|
||||
* @param index of the track.
|
||||
* @return an {@link CasInfo} object describing the conditional access system.
|
||||
*/
|
||||
public CasInfo getCasInfo(int index) {
|
||||
Map<String, Object> formatMap = getTrackFormatNative(index);
|
||||
if (formatMap.containsKey(MediaFormat.KEY_CA_SYSTEM_ID)) {
|
||||
int systemId = ((Integer)formatMap.get(MediaFormat.KEY_CA_SYSTEM_ID)).intValue();
|
||||
MediaCas.Session session = null;
|
||||
if (mMediaCas != null && formatMap.containsKey(MediaFormat.KEY_CA_SESSION_ID)) {
|
||||
ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_SESSION_ID);
|
||||
buf.rewind();
|
||||
final byte[] sessionId = new byte[buf.remaining()];
|
||||
buf.get(sessionId);
|
||||
session = mMediaCas.createFromSessionId(sessionId);
|
||||
}
|
||||
return new CasInfo(systemId, session);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
native_finalize();
|
||||
@@ -307,31 +367,6 @@ final public class MediaExtractor {
|
||||
return initDataMap.get(schemeUuid);
|
||||
}
|
||||
};
|
||||
} else if (formatMap.containsKey("mime")
|
||||
&& "video/mp2ts".equals(formatMap.get("mime"))) {
|
||||
final Map<UUID, DrmInitData.SchemeInitData> initDataMap =
|
||||
new HashMap<UUID, DrmInitData.SchemeInitData>();
|
||||
|
||||
int numTracks = getTrackCount();
|
||||
for (int i = 0; i < numTracks; ++i) {
|
||||
Map<String, Object> trackFormatMap = getTrackFormatNative(i);
|
||||
if (!trackFormatMap.containsKey("cas")) {
|
||||
continue;
|
||||
}
|
||||
ByteBuffer buf = (ByteBuffer) trackFormatMap.get("cas");
|
||||
buf.rewind();
|
||||
final byte[] data = new byte[buf.remaining()];
|
||||
buf.get(data);
|
||||
initDataMap.put(new UUID(0, i), new DrmInitData.SchemeInitData("cas", data));
|
||||
}
|
||||
if (initDataMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new DrmInitData() {
|
||||
public SchemeInitData get(UUID schemeUuid) {
|
||||
return initDataMap.get(schemeUuid);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
int numTracks = getTrackCount();
|
||||
for (int i = 0; i < numTracks; ++i) {
|
||||
@@ -349,8 +384,8 @@ final public class MediaExtractor {
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -680,5 +715,7 @@ final public class MediaExtractor {
|
||||
native_init();
|
||||
}
|
||||
|
||||
private MediaCas mMediaCas;
|
||||
|
||||
private long mNativeContext;
|
||||
}
|
||||
|
||||
@@ -767,6 +767,29 @@ public final class MediaFormat {
|
||||
*/
|
||||
public static final String KEY_TRACK_ID = "track-id";
|
||||
|
||||
/**
|
||||
* A key describing the system id of the conditional access system used to scramble
|
||||
* a media track.
|
||||
* <p>
|
||||
* This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
|
||||
* access system.
|
||||
* <p>
|
||||
* The associated value is an integer.
|
||||
* @hide
|
||||
*/
|
||||
public static final String KEY_CA_SYSTEM_ID = "ca-system-id";
|
||||
|
||||
/**
|
||||
* A key describing the {@link MediaCas.Session} object associated with a media track.
|
||||
* <p>
|
||||
* This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
|
||||
* access system.
|
||||
* <p>
|
||||
* The associated value is a ByteBuffer.
|
||||
* @hide
|
||||
*/
|
||||
public static final String KEY_CA_SESSION_ID = "ca-session-id";
|
||||
|
||||
/* package private */ MediaFormat(Map<String, Object> map) {
|
||||
mMap = map;
|
||||
}
|
||||
|
||||
@@ -54,11 +54,10 @@ static void setDescrambler(
|
||||
}
|
||||
|
||||
static status_t getBufferAndSize(
|
||||
JNIEnv *env, jobject byteBuf, jint offset, size_t length,
|
||||
JNIEnv *env, jobject byteBuf, jint offset, jint limit, size_t length,
|
||||
void **outPtr, jbyteArray *outByteArray) {
|
||||
void *ptr = env->GetDirectBufferAddress(byteBuf);
|
||||
|
||||
size_t bufSize;
|
||||
jbyteArray byteArray = NULL;
|
||||
|
||||
ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
|
||||
@@ -78,13 +77,9 @@ static status_t getBufferAndSize(
|
||||
|
||||
jboolean isCopy;
|
||||
ptr = env->GetByteArrayElements(byteArray, &isCopy);
|
||||
|
||||
bufSize = (size_t) env->GetArrayLength(byteArray);
|
||||
} else {
|
||||
bufSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
|
||||
}
|
||||
|
||||
if (length + offset > bufSize) {
|
||||
if ((jint)length + offset > limit) {
|
||||
if (byteArray != NULL) {
|
||||
env->ReleaseByteArrayElements(byteArray, (jbyte *)ptr, 0);
|
||||
}
|
||||
@@ -294,7 +289,8 @@ static void throwServiceSpecificException(
|
||||
static jint android_media_MediaDescrambler_native_descramble(
|
||||
JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
|
||||
jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
|
||||
jobject srcBuf, jint srcOffset, jobject dstBuf, jint dstOffset) {
|
||||
jobject srcBuf, jint srcOffset, jint srcLimit,
|
||||
jobject dstBuf, jint dstOffset, jint dstLimit) {
|
||||
sp<JDescrambler> descrambler = getDescrambler(env, thiz);
|
||||
if (descrambler == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
@@ -307,7 +303,7 @@ static jint android_media_MediaDescrambler_native_descramble(
|
||||
numBytesOfEncryptedDataObj, &subSamples);
|
||||
if (totalLength < 0) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"Invalid sub sample info!");
|
||||
"Invalid subsample info!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -315,16 +311,23 @@ static jint android_media_MediaDescrambler_native_descramble(
|
||||
void *srcPtr = NULL, *dstPtr = NULL;
|
||||
jbyteArray srcArray = NULL, dstArray = NULL;
|
||||
status_t err = getBufferAndSize(
|
||||
env, srcBuf, srcOffset, totalLength, &srcPtr, &srcArray);
|
||||
env, srcBuf, srcOffset, srcLimit, totalLength, &srcPtr, &srcArray);
|
||||
|
||||
if (err == OK) {
|
||||
if (dstBuf == NULL) {
|
||||
dstPtr = srcPtr;
|
||||
} else {
|
||||
err = getBufferAndSize(
|
||||
env, dstBuf, dstOffset, totalLength, &dstPtr, &dstArray);
|
||||
env, dstBuf, dstOffset, dstLimit, totalLength, &dstPtr, &dstArray);
|
||||
}
|
||||
}
|
||||
|
||||
if (err != OK) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"Invalid buffer offset and/or size for subsamples!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Status status;
|
||||
if (err == OK) {
|
||||
status = descrambler->descramble(
|
||||
@@ -394,7 +397,7 @@ static const JNINativeMethod gMethods[] = {
|
||||
(void *)android_media_MediaDescrambler_native_init },
|
||||
{ "native_setup", "(Landroid/os/IBinder;)V",
|
||||
(void *)android_media_MediaDescrambler_native_setup },
|
||||
{ "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;ILjava/nio/ByteBuffer;I)I",
|
||||
{ "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
|
||||
(void *)android_media_MediaDescrambler_native_descramble },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user