Merge "MediaCas: rethrow MediaCas specific exception"

This commit is contained in:
TreeHugger Robot
2017-03-27 20:17:10 +00:00
committed by Android (Google) Code Review
11 changed files with 464 additions and 176 deletions

View File

@ -21818,23 +21818,23 @@ package android.media {
}
public final class MediaCas {
ctor public MediaCas(int) throws android.media.UnsupportedCasException;
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void closeSession(byte[]);
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method public static boolean isSystemIdSupported(int);
method public byte[] openSession(int);
method public byte[] openSession(int, int);
method public void processEcm(byte[], byte[], int, int);
method public void processEcm(byte[], byte[]);
method public void processEmm(byte[], int, int);
method public void processEmm(byte[]);
method public void provision(java.lang.String);
method public void refreshEntitlements(int, byte[]);
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 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[]);
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[]);
method public void setSessionPrivateData(byte[], byte[]);
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,7 +21847,22 @@ package android.media {
}
public class MediaCasException extends java.lang.Exception {
ctor public MediaCasException(java.lang.String);
}
public static final class MediaCasException.DeniedByServerException extends android.media.MediaCasException {
}
public static final class MediaCasException.NotProvisionedException extends android.media.MediaCasException {
}
public static final class MediaCasException.ResourceBusyException extends android.media.MediaCasException {
}
public static final class MediaCasException.UnsupportedCasException extends android.media.MediaCasException {
}
public class MediaCasStateException extends java.lang.IllegalStateException {
method public java.lang.String getDiagnosticInfo();
}
public final class MediaCodec {
@ -22275,7 +22290,7 @@ package android.media {
}
public final class MediaDescrambler {
ctor public MediaDescrambler(int) throws android.media.UnsupportedCasException;
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 final boolean requiresSecureDecoderComponent(java.lang.String);
@ -23586,10 +23601,6 @@ package android.media {
field public static final int TONE_SUP_RINGTONE = 23; // 0x17
}
public final class UnsupportedCasException extends android.media.MediaCasException {
ctor public UnsupportedCasException(java.lang.String);
}
public final class UnsupportedSchemeException extends android.media.MediaDrmException {
ctor public UnsupportedSchemeException(java.lang.String);
}

View File

@ -23605,23 +23605,23 @@ package android.media {
}
public final class MediaCas {
ctor public MediaCas(int) throws android.media.UnsupportedCasException;
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void closeSession(byte[]);
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method public static boolean isSystemIdSupported(int);
method public byte[] openSession(int);
method public byte[] openSession(int, int);
method public void processEcm(byte[], byte[], int, int);
method public void processEcm(byte[], byte[]);
method public void processEmm(byte[], int, int);
method public void processEmm(byte[]);
method public void provision(java.lang.String);
method public void refreshEntitlements(int, byte[]);
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 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[]);
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[]);
method public void setSessionPrivateData(byte[], byte[]);
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 {
@ -23634,7 +23634,22 @@ package android.media {
}
public class MediaCasException extends java.lang.Exception {
ctor public MediaCasException(java.lang.String);
}
public static final class MediaCasException.DeniedByServerException extends android.media.MediaCasException {
}
public static final class MediaCasException.NotProvisionedException extends android.media.MediaCasException {
}
public static final class MediaCasException.ResourceBusyException extends android.media.MediaCasException {
}
public static final class MediaCasException.UnsupportedCasException extends android.media.MediaCasException {
}
public class MediaCasStateException extends java.lang.IllegalStateException {
method public java.lang.String getDiagnosticInfo();
}
public final class MediaCodec {
@ -24062,7 +24077,7 @@ package android.media {
}
public final class MediaDescrambler {
ctor public MediaDescrambler(int) throws android.media.UnsupportedCasException;
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 final boolean requiresSecureDecoderComponent(java.lang.String);
@ -25384,10 +25399,6 @@ package android.media {
field public static final int TONE_SUP_RINGTONE = 23; // 0x17
}
public final class UnsupportedCasException extends android.media.MediaCasException {
ctor public UnsupportedCasException(java.lang.String);
}
public final class UnsupportedSchemeException extends android.media.MediaDrmException {
ctor public UnsupportedSchemeException(java.lang.String);
}

View File

@ -21929,23 +21929,23 @@ package android.media {
}
public final class MediaCas {
ctor public MediaCas(int) throws android.media.UnsupportedCasException;
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void closeSession(byte[]);
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method public static boolean isSystemIdSupported(int);
method public byte[] openSession(int);
method public byte[] openSession(int, int);
method public void processEcm(byte[], byte[], int, int);
method public void processEcm(byte[], byte[]);
method public void processEmm(byte[], int, int);
method public void processEmm(byte[]);
method public void provision(java.lang.String);
method public void refreshEntitlements(int, byte[]);
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 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[]);
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[]);
method public void setSessionPrivateData(byte[], byte[]);
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 {
@ -21958,7 +21958,22 @@ package android.media {
}
public class MediaCasException extends java.lang.Exception {
ctor public MediaCasException(java.lang.String);
}
public static final class MediaCasException.DeniedByServerException extends android.media.MediaCasException {
}
public static final class MediaCasException.NotProvisionedException extends android.media.MediaCasException {
}
public static final class MediaCasException.ResourceBusyException extends android.media.MediaCasException {
}
public static final class MediaCasException.UnsupportedCasException extends android.media.MediaCasException {
}
public class MediaCasStateException extends java.lang.IllegalStateException {
method public java.lang.String getDiagnosticInfo();
}
public final class MediaCodec {
@ -22386,7 +22401,7 @@ package android.media {
}
public final class MediaDescrambler {
ctor public MediaDescrambler(int) throws android.media.UnsupportedCasException;
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 final boolean requiresSecureDecoderComponent(java.lang.String);
@ -23697,10 +23712,6 @@ package android.media {
field public static final int TONE_SUP_RINGTONE = 23; // 0x17
}
public final class UnsupportedCasException extends android.media.MediaCasException {
ctor public UnsupportedCasException(java.lang.String);
}
public final class UnsupportedSchemeException extends android.media.MediaDrmException {
ctor public UnsupportedSchemeException(java.lang.String);
}

View File

@ -18,6 +18,7 @@ package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.MediaCasException.*;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@ -28,6 +29,7 @@ import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.util.Log;
import android.util.Singleton;
@ -84,8 +86,6 @@ import android.util.Singleton;
* sessionId of the descrambler can be retrieved by {@link MediaExtractor#getDrmInitData}
* and used to initialize a MediaDescrambler object for MediaCodec.
* <p>
* TODO: determine exception handling schemes.
* <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
@ -382,28 +382,22 @@ public final class MediaCas {
mEventHandler = new EventHandler(looper);
}
/*
* TODO: handle ServiceSpecificException from the IMediaCas
* All Drm-specific failures will be thrown by mICas as
* ServiceSpecificException exception with Drm error code.
* These need to be re-thrown as crypto exceptions.
*/
/**
* Send the private data for the CA system.
*
* @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.
*/
/*
* TODO: need to re-throw DRM-specific exceptions
*/
public void setPrivateData(@NonNull byte[] data) {
public void setPrivateData(@NonNull byte[] data) throws MediaCasException {
validateInternalStates();
try {
mICas.setPrivateData(data);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -416,14 +410,17 @@ public final class MediaCas {
*
* @return session id of the newly opened session.
*
* @throws IllegalStateException if the MediaCas instance is not valid,
* or IllegalArgumentException if a session for the program already exists.
* @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) {
public byte[] openSession(int programNumber) throws MediaCasException {
validateInternalStates();
try {
return mICas.openSession(programNumber);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -438,14 +435,18 @@ public final class MediaCas {
*
* @return session id of the newly opened session.
*
* @throws IllegalStateException if the MediaCas instance is not valid,
* or IllegalArgumentException if a session for the stream already exists.
* @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 byte[] openSession(int programNumber, int elementaryPID)
throws MediaCasException {
validateInternalStates();
try {
return mICas.openSessionForStream(programNumber, elementaryPID);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -457,14 +458,16 @@ public final class MediaCas {
*
* @param sessionId the session to be closed.
*
* @throws IllegalStateException if the MediaCas instance is not valid,
* or IllegalArgumentException if the session is not valid.
* @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();
}
@ -476,17 +479,18 @@ public final class MediaCas {
* @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,
* or IllegalArgumentException if the session is not valid.
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
/*
* TODO: need to re-throw DRM-specific exceptions
*/
public void setSessionPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data) {
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();
}
@ -500,19 +504,19 @@ public final class MediaCas {
* @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,
* or IllegalArgumentException if the session is not valid.
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
/*
* TODO: need to re-throw DRM-specific exceptions
*/
public void processEcm(
@NonNull byte[] sessionId, @NonNull byte[] data, int offset, int length) {
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();
}
@ -526,13 +530,12 @@ public final class MediaCas {
* @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,
* or IllegalArgumentException if the session is not valid.
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
/*
* TODO: need to re-throw DRM-specific exceptions
*/
public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data) {
public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data)
throws MediaCasException {
processEcm(sessionId, data, 0, data.length);
}
@ -544,16 +547,18 @@ public final class MediaCas {
* @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.
*/
/*
* TODO: need to re-throw DRM-specific exceptions
*/
public void processEmm(@NonNull byte[] data, int offset, int length) {
public void processEmm(@NonNull byte[] data, int offset, int length)
throws MediaCasException {
validateInternalStates();
try {
mCasData.set(data, offset, length);
mICas.processEmm(mCasData);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -567,11 +572,10 @@ public final class MediaCas {
* @param data byte array of the EMM data.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
/*
* TODO: need to re-throw DRM-specific exceptions
*/
public void processEmm(@NonNull byte[] data) {
public void processEmm(@NonNull byte[] data) throws MediaCasException {
processEmm(data, 0, data.length);
}
@ -584,12 +588,17 @@ public final class MediaCas {
* @param data a byte array containing scheme-specific data for the event.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public void sendEvent(int event, int arg, @Nullable byte[] data) {
public void sendEvent(int event, int arg, @Nullable byte[] data)
throws MediaCasException {
validateInternalStates();
try {
mICas.sendEvent(event, arg, data);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -603,12 +612,16 @@ public final class MediaCas {
* specific.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public void provision(@NonNull String provisionString) {
public void provision(@NonNull String provisionString) throws MediaCasException {
validateInternalStates();
try {
mICas.provision(provisionString);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -621,15 +634,17 @@ public final class MediaCas {
* @param refreshData private data associated with the refreshment.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
/*
* TODO: define enums for refreshType
*/
public void refreshEntitlements(int refreshType, @Nullable byte[] refreshData) {
public void refreshEntitlements(int refreshType, @Nullable byte[] refreshData)
throws MediaCasException {
validateInternalStates();
try {
mICas.refreshEntitlements(refreshType, refreshData);
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}

View File

@ -16,11 +16,104 @@
package android.media;
import android.os.ServiceSpecificException;
/**
* Base class for MediaCas exceptions
*/
public class MediaCasException extends Exception {
/** @hide */
public static final int DRM_ERROR_BASE = -2000;
/** @hide */
public static final int ERROR_DRM_UNKNOWN = DRM_ERROR_BASE;
/** @hide */
public static final int ERROR_DRM_NO_LICENSE = DRM_ERROR_BASE - 1;
/** @hide */
public static final int ERROR_DRM_LICENSE_EXPIRED = DRM_ERROR_BASE - 2;
/** @hide */
public static final int ERROR_DRM_SESSION_NOT_OPENED = DRM_ERROR_BASE - 3;
/** @hide */
public static final int ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 4;
/** @hide */
public static final int ERROR_DRM_DECRYPT = DRM_ERROR_BASE - 5;
/** @hide */
public static final int ERROR_DRM_CANNOT_HANDLE = DRM_ERROR_BASE - 6;
/** @hide */
public static final int ERROR_DRM_TAMPER_DETECTED = DRM_ERROR_BASE - 7;
/** @hide */
public static final int ERROR_DRM_NOT_PROVISIONED = DRM_ERROR_BASE - 8;
/** @hide */
public static final int ERROR_DRM_DEVICE_REVOKED = DRM_ERROR_BASE - 9;
/** @hide */
public static final int ERROR_DRM_RESOURCE_BUSY = DRM_ERROR_BASE - 10;
/** @hide */
public static final int ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION = DRM_ERROR_BASE - 11;
/** @hide */
public static final int ERROR_DRM_LAST_USED_ERRORCODE = DRM_ERROR_BASE - 11;
/** @hide */
public static final int ERROR_DRM_VENDOR_MAX = DRM_ERROR_BASE - 500;
/** @hide */
public static final int ERROR_DRM_VENDOR_MIN = DRM_ERROR_BASE - 999;
/** @hide */
public MediaCasException(String detailMessage) {
super(detailMessage);
}
static void throwExceptions(ServiceSpecificException e) throws MediaCasException {
if (e.errorCode == ERROR_DRM_NOT_PROVISIONED) {
throw new NotProvisionedException(e.getMessage());
} else if (e.errorCode == ERROR_DRM_RESOURCE_BUSY) {
throw new ResourceBusyException(e.getMessage());
} else if (e.errorCode == ERROR_DRM_DEVICE_REVOKED) {
throw new DeniedByServerException(e.getMessage());
} else {
MediaCasStateException.throwExceptions(e);
}
}
/**
* Exception thrown when an attempt is made to construct a MediaCas object
* using a CA_system_id that is not supported by the device
*/
public static final class UnsupportedCasException extends MediaCasException {
/** @hide */
public UnsupportedCasException(String detailMessage) {
super(detailMessage);
}
}
/**
* Exception thrown when an operation on a MediaCas object is attempted
* before it's provisioned successfully.
*/
public static final class NotProvisionedException extends MediaCasException {
/** @hide */
public NotProvisionedException(String detailMessage) {
super(detailMessage);
}
}
/**
* Exception thrown when the provisioning server or key server denies a
* license for a device.
*/
public static final class DeniedByServerException extends MediaCasException {
/** @hide */
public DeniedByServerException(String detailMessage) {
super(detailMessage);
}
}
/**
* Exception thrown when an operation on a MediaCas object is attempted
* and hardware resources are not available, due to being in use.
*/
public static final class ResourceBusyException extends MediaCasException {
/** @hide */
public ResourceBusyException(String detailMessage) {
super(detailMessage);
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ServiceSpecificException;
import static android.media.MediaCasException.*;
/**
* Base class for MediaCas runtime exceptions
*/
public class MediaCasStateException extends IllegalStateException {
private final int mErrorCode;
private final String mDiagnosticInfo;
/** @hide */
public MediaCasStateException(int err, @Nullable String msg, @Nullable String diagnosticInfo) {
super(msg);
mErrorCode = err;
mDiagnosticInfo = diagnosticInfo;
}
static void throwExceptions(ServiceSpecificException e) {
String diagnosticInfo = "";
switch (e.errorCode) {
case ERROR_DRM_UNKNOWN:
diagnosticInfo = "General CAS error";
break;
case ERROR_DRM_NO_LICENSE:
diagnosticInfo = "No license";
break;
case ERROR_DRM_LICENSE_EXPIRED:
diagnosticInfo = "License expired";
break;
case ERROR_DRM_SESSION_NOT_OPENED:
diagnosticInfo = "Session not opened";
break;
case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
diagnosticInfo = "Not initialized";
break;
case ERROR_DRM_DECRYPT:
diagnosticInfo = "Decrypt error";
break;
case ERROR_DRM_CANNOT_HANDLE:
diagnosticInfo = "Unsupported scheme or data format";
break;
case ERROR_DRM_TAMPER_DETECTED:
diagnosticInfo = "Tamper detected";
break;
default:
diagnosticInfo = "Unknown CAS state exception";
break;
}
throw new MediaCasStateException(e.errorCode, e.getMessage(),
String.format("%s (err=%d)", diagnosticInfo, e.errorCode));
}
/**
* Retrieve the associated error code
*
* @hide
*/
public int getErrorCode() {
return mErrorCode;
}
/**
* Retrieve a developer-readable diagnostic information string
* associated with the exception. Do not show this to end-users,
* since this string will not be localized or generally comprehensible
* to end-users.
*/
@NonNull
public String getDiagnosticInfo() {
return mDiagnosticInfo;
}
}

View File

@ -1878,9 +1878,7 @@ final public class MediaCodec {
* @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
* component as an encoder.
* @param descrambler Specify a descrambler object to facilitate secure
* descrambling of the media data. descrambler must not be
* null if this method is used. For non-secure codecs, use
* {@link #configure} and with null crypto parameter.
* descrambling of the media data, or null for non-secure codecs.
* @throws IllegalArgumentException if the surface has been released (or is invalid),
* or the format is unacceptable (e.g. missing a mandatory key),
* or the flags are not set properly
@ -1891,8 +1889,9 @@ final public class MediaCodec {
*/
public void configure(
@Nullable MediaFormat format, @Nullable Surface surface,
@ConfigureFlag int flags, @NonNull MediaDescrambler descrambler) {
configure(format, surface, null, descrambler.getBinder(), flags);
@ConfigureFlag int flags, @Nullable MediaDescrambler descrambler) {
configure(format, surface, null,
descrambler != null ? descrambler.getBinder() : null, flags);
}
private void configure(

View File

@ -17,10 +17,12 @@
package android.media;
import android.annotation.NonNull;
import android.media.MediaCasException.UnsupportedCasException;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
import java.nio.ByteBuffer;
@ -54,13 +56,13 @@ public final class MediaDescrambler {
/**
* Class for parceling descrambling parameters over IDescrambler binder.
*/
// This class currently is not used by Java binder. descramble() goes through
// jni to use shared memory. However, the parcelable is still required for AIDL.
static class DescrambleInfo implements Parcelable {
private DescrambleInfo() {
// TODO: implement
}
private DescrambleInfo(Parcel in) {
// TODO: disable
}
@Override
@ -70,7 +72,6 @@ public final class MediaDescrambler {
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO: implement
}
public static final Parcelable.Creator<DescrambleInfo> CREATOR
@ -112,13 +113,6 @@ public final class MediaDescrambler {
return mIDescrambler.asBinder();
}
/*
* TODO: handle ServiceSpecificException from the mIDescrambler
* All Drm-specific failures will be thrown by mIDescrambler as
* ServiceSpecificException exception with Drm error code.
* These need to be re-thrown as crypto exceptions.
*/
/**
* Query if the scrambling scheme requires the use of a secure decoder
* to decode data of the given mime type.
@ -150,14 +144,16 @@ public final class MediaDescrambler {
* @param sessionId the MediaCas sessionId to associate with this
* MediaDescrambler instance.
*
* @throws IllegalStateException if the descrambler instance is not valid,
* or IllegalArgumentException if the sessionId is not valid.
* @throws IllegalStateException if the descrambler instance is not valid.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public final void setMediaCasSession(@NonNull byte[] sessionId) {
validateInternalStates();
try {
mIDescrambler.setMediaCasSession(sessionId);
} catch (ServiceSpecificException e) {
MediaCasStateException.throwExceptions(e);
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
@ -179,9 +175,7 @@ public final class MediaDescrambler {
* values indicating errors.
*
* @throws IllegalStateException if the descrambler instance is not valid.
*/
/*
* TODO: throw DRM-specific exception if decrambling is failing.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public final int descramble(
@NonNull ByteBuffer srcBuf, int srcPos, ByteBuffer dstBuf, int dstPos,
@ -208,12 +202,17 @@ public final class MediaDescrambler {
"Invalid CryptoInfo: key array is invalid!");
}
return native_descramble(
cryptoInfo.key[0],
cryptoInfo.numSubSamples,
cryptoInfo.numBytesOfClearData,
cryptoInfo.numBytesOfEncryptedData,
srcBuf, srcPos, dstBuf, dstPos);
try {
return native_descramble(
cryptoInfo.key[0],
cryptoInfo.numSubSamples,
cryptoInfo.numBytesOfClearData,
cryptoInfo.numBytesOfEncryptedData,
srcBuf, srcPos, dstBuf, dstPos);
} catch (ServiceSpecificException e) {
MediaCasStateException.throwExceptions(e);
}
return -1;
}
public final void release() {

View File

@ -1,27 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
/**
* Exception thrown when an attempt is made to construct a MediaCas object
* using a CA_system_id that is not supported by the device
*/
public final class UnsupportedCasException extends MediaCasException {
public UnsupportedCasException(String detailMessage) {
super(detailMessage);
}
}

View File

@ -129,7 +129,7 @@ void JDescrambler::ensureBufferCapacity(size_t neededSize) {
mMem = mDealer->allocate(neededSize);
}
ssize_t JDescrambler::descramble(
Status JDescrambler::descramble(
jbyte key,
size_t numSubSamples,
ssize_t totalLength,
@ -137,7 +137,8 @@ ssize_t JDescrambler::descramble(
const void *srcPtr,
jint srcOffset,
void *dstPtr,
jint dstOffset) {
jint dstOffset,
ssize_t *result) {
// TODO: IDescrambler::descramble() is re-entrant, however because we
// only have 1 shared mem buffer, we can only do 1 descramble at a time.
// Concurrency might be improved by allowing on-demand allocation of up
@ -159,16 +160,16 @@ ssize_t JDescrambler::descramble(
info.dstPtr = NULL;
info.dstOffset = 0;
int32_t result;
binder::Status status = mDescrambler->descramble(info, &result);
int32_t descrambleResult;
Status status = mDescrambler->descramble(info, &descrambleResult);
if (!status.isOk() || result > totalLength) {
return -1;
if (status.isOk()) {
*result = (descrambleResult <= totalLength) ? descrambleResult : -1;
if (*result > 0) {
memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *result);
}
}
if (result > 0) {
memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), result);
}
return result;
return status;
}
} // namespace android
@ -251,11 +252,45 @@ static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
numBytesOfClearData = NULL;
}
if (totalSize < 0) {
delete[] subSamples;
return -1;
}
*outSubSamples = subSamples;
return totalSize;
}
static jthrowable createServiceSpecificException(
JNIEnv *env, int serviceSpecificError, const char *msg) {
if (env->ExceptionCheck()) {
ALOGW("Discarding pending exception");
env->ExceptionDescribe();
env->ExceptionClear();
}
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/os/ServiceSpecificException"));
CHECK(clazz.get() != NULL);
const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
CHECK(ctor != NULL);
ScopedLocalRef<jstring> msgObj(
env, env->NewStringUTF(msg != NULL ?
msg : String8::format("Error %#x", serviceSpecificError)));
return (jthrowable)env->NewObject(
clazz.get(), ctor, serviceSpecificError, msgObj.get());
}
static void throwServiceSpecificException(
JNIEnv *env, int serviceSpecificError, const char *msg) {
jthrowable exception = createServiceSpecificException(env, serviceSpecificError, msg);
env->Throw(exception);
}
static jint android_media_MediaDescrambler_native_descramble(
JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
@ -290,11 +325,11 @@ static jint android_media_MediaDescrambler_native_descramble(
env, dstBuf, dstOffset, totalLength, &dstPtr, &dstArray);
}
}
Status status;
if (err == OK) {
result = descrambler->descramble(
status = descrambler->descramble(
key, numSubSamples, totalLength, subSamples,
srcPtr, srcOffset, dstPtr, dstOffset);
srcPtr, srcOffset, dstPtr, dstOffset, &result);
}
delete[] subSamples;
@ -304,6 +339,51 @@ static jint android_media_MediaDescrambler_native_descramble(
if (dstArray != NULL) {
env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
}
if (!status.isOk()) {
switch (status.exceptionCode()) {
case Status::EX_SECURITY:
jniThrowException(env, "java/lang/SecurityException",
status.exceptionMessage());
break;
case Status::EX_BAD_PARCELABLE:
jniThrowException(env, "java/lang/BadParcelableException",
status.exceptionMessage());
break;
case Status::EX_ILLEGAL_ARGUMENT:
jniThrowException(env, "java/lang/IllegalArgumentException",
status.exceptionMessage());
break;
case Status::EX_NULL_POINTER:
jniThrowException(env, "java/lang/NullPointerException",
status.exceptionMessage());
break;
case Status::EX_ILLEGAL_STATE:
jniThrowException(env, "java/lang/IllegalStateException",
status.exceptionMessage());
break;
case Status::EX_NETWORK_MAIN_THREAD:
jniThrowException(env, "java/lang/NetworkOnMainThreadException",
status.exceptionMessage());
break;
case Status::EX_UNSUPPORTED_OPERATION:
jniThrowException(env, "java/lang/UnsupportedOperationException",
status.exceptionMessage());
break;
case Status::EX_SERVICE_SPECIFIC:
throwServiceSpecificException(env, status.serviceSpecificErrorCode(),
status.exceptionMessage());
break;
default:
{
String8 msg;
msg.appendFormat("Unknown exception code: %d, msg: %s",
status.exceptionCode(), status.exceptionMessage().string());
jniThrowException(env, "java/lang/RuntimeException", msg.string());
break;
}
}
}
return result;
}

View File

@ -19,6 +19,7 @@
#include "jni.h"
#include <binder/Status.h>
#include <media/cas/DescramblerAPI.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/Mutex.h>
@ -31,11 +32,12 @@ namespace media {
class IDescrambler;
};
using namespace media;
using binder::Status;
struct JDescrambler : public RefBase {
JDescrambler(JNIEnv *env, jobject descramberBinderObj);
ssize_t descramble(
Status descramble(
jbyte key,
size_t numSubSamples,
ssize_t totalLength,
@ -43,7 +45,8 @@ struct JDescrambler : public RefBase {
const void *srcPtr,
jint srcOffset,
void *dstPtr,
jint dstOffset);
jint dstOffset,
ssize_t *result);
protected:
virtual ~JDescrambler();