From 2659c2f16cf55d58f5b3817347340ae84f9eaabd Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 27 Apr 2017 13:18:20 -0700 Subject: [PATCH] cas: convert MediaCas to HIDL - Replace AIDL interfaces with corresponding HIDL ones. bug: 22804304 Change-Id: I8c552ce6fc3766715cd1bc0ea05c0988d56f590e --- Android.mk | 6 +- media/java/android/media/MediaCas.java | 208 +++++++---------- .../java/android/media/MediaCasException.java | 59 ++--- .../android/media/MediaCasStateException.java | 55 +++-- media/java/android/media/MediaCodec.java | 5 +- .../java/android/media/MediaDescrambler.java | 55 +---- media/java/android/media/MediaExtractor.java | 17 +- media/jni/Android.bp | 6 + media/jni/android_media_MediaCodec.cpp | 8 +- media/jni/android_media_MediaCodec.h | 11 +- media/jni/android_media_MediaDescrambler.cpp | 215 +++++++++--------- media/jni/android_media_MediaDescrambler.h | 31 +-- media/jni/android_media_MediaExtractor.cpp | 56 +++-- media/jni/android_media_MediaExtractor.h | 6 +- 14 files changed, 337 insertions(+), 401 deletions(-) diff --git a/Android.mk b/Android.mk index 4cedadcb77646..1e61e44d9c439 100644 --- a/Android.mk +++ b/Android.mk @@ -437,10 +437,6 @@ LOCAL_SRC_FILES += \ location/java/android/location/INetInitiatedListener.aidl \ location/java/com/android/internal/location/ILocationProvider.aidl \ media/java/android/media/IAudioService.aidl \ - ../av/drm/libmediadrm/aidl/android/media/ICas.aidl \ - ../av/drm/libmediadrm/aidl/android/media/ICasListener.aidl \ - ../av/drm/libmediadrm/aidl/android/media/IDescrambler.aidl \ - ../av/drm/libmediadrm/aidl/android/media/IMediaCasService.aidl \ media/java/android/media/IAudioFocusDispatcher.aidl \ media/java/android/media/IAudioRoutesObserver.aidl \ media/java/android/media/IMediaHTTPConnection.aidl \ @@ -613,6 +609,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android.hardware.vibrator-V1.1-java-constants \ android.hardware.wifi-V1.0-java-constants \ +include hardware/interfaces/cas/1.0/CasHal.mk + # Loaded with System.loadLibrary by android.view.textclassifier LOCAL_REQUIRED_MODULES += libtextclassifier diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index ce50cc801dafb..12352e7f8b242 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -18,21 +18,20 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.cas.V1_0.*; import android.media.MediaCasException.*; import android.os.Handler; import android.os.HandlerThread; -import android.os.IBinder; +import android.os.IHwBinder; import android.os.Looper; import android.os.Message; -import android.os.Parcel; -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; +import java.util.ArrayList; + /** * MediaCas can be used to obtain keys for descrambling protected media streams, in * conjunction with {@link android.media.MediaDescrambler}. The MediaCas APIs are @@ -95,7 +94,6 @@ import android.util.Singleton; */ public final class MediaCas implements AutoCloseable { private static final String TAG = "MediaCas"; - private final ParcelableCasData mCasData = new ParcelableCasData(); private ICas mICas; private EventListener mListener; private HandlerThread mHandlerThread; @@ -105,8 +103,10 @@ public final class MediaCas implements AutoCloseable { new Singleton() { @Override protected IMediaCasService create() { - return IMediaCasService.Stub.asInterface( - ServiceManager.getService("media.cas")); + try { + return IMediaCasService.getService(); + } catch (RemoteException e) {} + return null; } }; @@ -136,65 +136,21 @@ public final class MediaCas implements AutoCloseable { @Override public void handleMessage(Message msg) { if (msg.what == MSG_CAS_EVENT) { - mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2, (byte[]) msg.obj); + mListener.onEvent(MediaCas.this, msg.arg1, msg.arg2, + toBytes((ArrayList) msg.obj)); } } } private final ICasListener.Stub mBinder = new ICasListener.Stub() { @Override - public void onEvent(int event, int arg, @Nullable byte[] data) + public void onEvent(int event, int arg, @Nullable ArrayList data) throws RemoteException { mEventHandler.sendMessage(mEventHandler.obtainMessage( EventHandler.MSG_CAS_EVENT, event, arg, data)); } }; - /** - * Class for parceling byte array data over ICas binder. - */ - static class ParcelableCasData implements Parcelable { - private byte[] mData; - private int mOffset; - private int mLength; - - ParcelableCasData() { - mData = null; - mOffset = mLength = 0; - } - - private ParcelableCasData(Parcel in) { - this(); - } - - void set(@NonNull byte[] data, int offset, int length) { - mData = data; - mOffset = offset; - mLength = length; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeByteArray(mData, mOffset, mLength); - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public ParcelableCasData createFromParcel(Parcel in) { - return new ParcelableCasData(in); - } - - public ParcelableCasData[] newArray(int size) { - return new ParcelableCasData[size]; - } - }; - } - /** * Describe a CAS plugin with its CA_system_ID and string name. * @@ -210,9 +166,9 @@ public final class MediaCas implements AutoCloseable { mName = null; } - PluginDescriptor(int CA_system_id, String name) { - mCASystemId = CA_system_id; - mName = name; + PluginDescriptor(@NonNull HidlCasPluginDescriptor descriptor) { + mCASystemId = descriptor.caSystemId; + mName = descriptor.name; } public int getSystemId() { @@ -230,13 +186,38 @@ public final class MediaCas implements AutoCloseable { } } + private ArrayList toByteArray(@NonNull byte[] data, int offset, int length) { + ArrayList byteArray = new ArrayList(length); + for (int i = 0; i < length; i++) { + byteArray.add(Byte.valueOf(data[offset + i])); + } + return byteArray; + } + + private ArrayList toByteArray(@Nullable byte[] data) { + if (data == null) { + return new ArrayList(); + } + return toByteArray(data, 0, data.length); + } + + private byte[] toBytes(@NonNull ArrayList byteArray) { + byte[] data = null; + if (byteArray != null) { + data = new byte[byteArray.size()]; + for (int i = 0; i < data.length; i++) { + data[i] = byteArray.get(i); + } + } + return data; + } /** * Class for an open session with the CA system. */ public final class Session implements AutoCloseable { - final byte[] mSessionId; + final ArrayList mSessionId; - Session(@NonNull byte[] sessionId) { + Session(@NonNull ArrayList sessionId) { mSessionId = sessionId; } @@ -254,9 +235,8 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mICas.setSessionPrivateData(mSessionId, data); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.setSessionPrivateData(mSessionId, toByteArray(data, 0, data.length))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -279,10 +259,8 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mCasData.set(data, offset, length); - mICas.processEcm(mSessionId, mCasData); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.processEcm(mSessionId, toByteArray(data, offset, length))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -314,56 +292,21 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mICas.closeSession(mSessionId); - } catch (ServiceSpecificException e) { - MediaCasStateException.throwExceptions(e); + MediaCasStateException.throwExceptionIfNeeded( + mICas.closeSession(mSessionId)); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } } } - Session createFromSessionId(byte[] sessionId) { - if (sessionId == null || sessionId.length == 0) { + Session createFromSessionId(@NonNull ArrayList sessionId) { + if (sessionId == null || sessionId.size() == 0) { return null; } return new Session(sessionId); } - /** - * Class for parceling CAS plugin descriptors over IMediaCasService binder. - */ - static class ParcelableCasPluginDescriptor - extends PluginDescriptor implements Parcelable { - - private ParcelableCasPluginDescriptor(int CA_system_id, String name) { - super(CA_system_id, name); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - Log.w(TAG, "ParcelableCasPluginDescriptor.writeToParcel shouldn't be called!"); - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public ParcelableCasPluginDescriptor createFromParcel(Parcel in) { - int CA_system_id = in.readInt(); - String name = in.readString(); - return new ParcelableCasPluginDescriptor(CA_system_id, name); - } - - public ParcelableCasPluginDescriptor[] newArray(int size) { - return new ParcelableCasPluginDescriptor[size]; - } - }; - } - /** * Query if a certain CA system is supported on this device. * @@ -393,13 +336,14 @@ public final class MediaCas implements AutoCloseable { if (service != null) { try { - ParcelableCasPluginDescriptor[] descriptors = service.enumeratePlugins(); - if (descriptors.length == 0) { + ArrayList descriptors = + service.enumeratePlugins(); + if (descriptors.size() == 0) { return null; } - PluginDescriptor[] results = new PluginDescriptor[descriptors.length]; + PluginDescriptor[] results = new PluginDescriptor[descriptors.size()]; for (int i = 0; i < results.length; i++) { - results[i] = descriptors[i]; + results[i] = new PluginDescriptor(descriptors.get(i)); } return results; } catch (RemoteException e) { @@ -430,7 +374,7 @@ public final class MediaCas implements AutoCloseable { } } - IBinder getBinder() { + IHwBinder getBinder() { validateInternalStates(); return mICas.asBinder(); @@ -497,14 +441,22 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mICas.setPrivateData(data); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.setPrivateData(toByteArray(data, 0, data.length))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } } + private class OpenSessionCallback implements ICas.openSessionCallback { + public Session mSession; + public int mStatus; + @Override + public void onValues(int status, ArrayList sessionId) { + mStatus = status; + mSession = createFromSessionId(sessionId); + } + } /** * Open a session to descramble one or more streams scrambled by the * conditional access system. @@ -519,9 +471,10 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - return createFromSessionId(mICas.openSession()); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + OpenSessionCallback cb = new OpenSessionCallback(); + mICas.openSession(cb); + MediaCasException.throwExceptionIfNeeded(cb.mStatus); + return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -544,10 +497,8 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mCasData.set(data, offset, length); - mICas.processEmm(mCasData); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.processEmm(toByteArray(data, offset, length))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -585,9 +536,8 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mICas.sendEvent(event, arg, data); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.sendEvent(event, arg, toByteArray(data))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -608,9 +558,8 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mICas.provision(provisionString); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.provision(provisionString)); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -631,9 +580,8 @@ public final class MediaCas implements AutoCloseable { validateInternalStates(); try { - mICas.refreshEntitlements(refreshType, refreshData); - } catch (ServiceSpecificException e) { - MediaCasException.throwExceptions(e); + MediaCasException.throwExceptionIfNeeded( + mICas.refreshEntitlements(refreshType, toByteArray(refreshData))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } diff --git a/media/java/android/media/MediaCasException.java b/media/java/android/media/MediaCasException.java index 485f6eebf88e0..35fb104b7f0b1 100644 --- a/media/java/android/media/MediaCasException.java +++ b/media/java/android/media/MediaCasException.java @@ -16,60 +16,29 @@ package android.media; -import android.os.ServiceSpecificException; +import android.hardware.cas.V1_0.Status; /** * 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) { + private 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()); + static void throwExceptionIfNeeded(int error) throws MediaCasException { + if (error == Status.OK) { + return; + } + + if (error == Status.ERROR_CAS_NOT_PROVISIONED) { + throw new NotProvisionedException(null); + } else if (error == Status.ERROR_CAS_RESOURCE_BUSY) { + throw new ResourceBusyException(null); + } else if (error == Status.ERROR_CAS_DEVICE_REVOKED) { + throw new DeniedByServerException(null); } else { - MediaCasStateException.throwExceptions(e); + MediaCasStateException.throwExceptionIfNeeded(error); } } diff --git a/media/java/android/media/MediaCasStateException.java b/media/java/android/media/MediaCasStateException.java index cf05c29752722..26c57923912d1 100644 --- a/media/java/android/media/MediaCasStateException.java +++ b/media/java/android/media/MediaCasStateException.java @@ -18,9 +18,8 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.ServiceSpecificException; -import static android.media.MediaCasException.*; +import android.hardware.cas.V1_0.Status; /** * Base class for MediaCas runtime exceptions @@ -29,46 +28,62 @@ public class MediaCasStateException extends IllegalStateException { private final int mErrorCode; private final String mDiagnosticInfo; - /** @hide */ - public MediaCasStateException(int err, @Nullable String msg, @Nullable String diagnosticInfo) { + private MediaCasStateException(int err, @Nullable String msg, @Nullable String diagnosticInfo) { super(msg); mErrorCode = err; mDiagnosticInfo = diagnosticInfo; } - static void throwExceptions(ServiceSpecificException e) { + static void throwExceptionIfNeeded(int err) { + throwExceptionIfNeeded(err, null /* msg */); + } + + static void throwExceptionIfNeeded(int err, @Nullable String msg) { + if (err == Status.OK) { + return; + } + if (err == Status.BAD_VALUE) { + throw new IllegalArgumentException(); + } + String diagnosticInfo = ""; - switch (e.errorCode) { - case ERROR_DRM_UNKNOWN: + switch (err) { + case Status.ERROR_CAS_UNKNOWN: diagnosticInfo = "General CAS error"; break; - case ERROR_DRM_NO_LICENSE: + case Status.ERROR_CAS_NO_LICENSE: diagnosticInfo = "No license"; break; - case ERROR_DRM_LICENSE_EXPIRED: + case Status.ERROR_CAS_LICENSE_EXPIRED: diagnosticInfo = "License expired"; break; - case ERROR_DRM_SESSION_NOT_OPENED: + case Status.ERROR_CAS_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: + case Status.ERROR_CAS_CANNOT_HANDLE: diagnosticInfo = "Unsupported scheme or data format"; break; - case ERROR_DRM_TAMPER_DETECTED: + case Status.ERROR_CAS_INVALID_STATE: + diagnosticInfo = "Invalid CAS state"; + break; + case Status.ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION: + diagnosticInfo = "Insufficient output protection"; + break; + case Status.ERROR_CAS_TAMPER_DETECTED: diagnosticInfo = "Tamper detected"; break; + case Status.ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED: + diagnosticInfo = "Not initialized"; + break; + case Status.ERROR_CAS_DECRYPT: + diagnosticInfo = "Decrypt error"; + break; default: diagnosticInfo = "Unknown CAS state exception"; break; } - throw new MediaCasStateException(e.errorCode, e.getMessage(), - String.format("%s (err=%d)", diagnosticInfo, e.errorCode)); + throw new MediaCasStateException(err, msg, + String.format("%s (err=%d)", diagnosticInfo, err)); } /** diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index e667554dab64d..3d5f6bc9ac713 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -26,6 +26,7 @@ import android.media.MediaCodecInfo.CodecCapabilities; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IHwBinder; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; @@ -1903,7 +1904,7 @@ final public class MediaCodec { private void configure( @Nullable MediaFormat format, @Nullable Surface surface, - @Nullable MediaCrypto crypto, @Nullable IBinder descramblerBinder, + @Nullable MediaCrypto crypto, @Nullable IHwBinder descramblerBinder, @ConfigureFlag int flags) { if (crypto != null && descramblerBinder != null) { throw new IllegalArgumentException("Can't use crypto and descrambler together!"); @@ -2018,7 +2019,7 @@ final public class MediaCodec { private native final void native_configure( @Nullable String[] keys, @Nullable Object[] values, @Nullable Surface surface, @Nullable MediaCrypto crypto, - @Nullable IBinder descramblerBinder, @ConfigureFlag int flags); + @Nullable IHwBinder descramblerBinder, @ConfigureFlag int flags); /** * Requests a Surface to use as the input to an encoder, in place of input buffers. This diff --git a/media/java/android/media/MediaDescrambler.java b/media/java/android/media/MediaDescrambler.java index b75b7dd8b7424..40c837b13cc8f 100644 --- a/media/java/android/media/MediaDescrambler.java +++ b/media/java/android/media/MediaDescrambler.java @@ -17,10 +17,9 @@ package android.media; import android.annotation.NonNull; +import android.hardware.cas.V1_0.*; import android.media.MediaCasException.UnsupportedCasException; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; +import android.os.IHwBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.Log; @@ -40,7 +39,7 @@ import java.nio.ByteBuffer; */ public final class MediaDescrambler implements AutoCloseable { private static final String TAG = "MediaDescrambler"; - private IDescrambler mIDescrambler; + private IDescramblerBase mIDescrambler; private final void validateInternalStates() { if (mIDescrambler == null) { @@ -53,39 +52,6 @@ public final class MediaDescrambler implements AutoCloseable { throw new IllegalStateException(); } - /** - * 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() { - } - - private DescrambleInfo(Parcel in) { - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public DescrambleInfo createFromParcel(Parcel in) { - return new DescrambleInfo(in); - } - - public DescrambleInfo[] newArray(int size) { - return new DescrambleInfo[size]; - } - }; - } - /** * Instantiate a MediaDescrambler. * @@ -107,7 +73,7 @@ public final class MediaDescrambler implements AutoCloseable { native_setup(mIDescrambler.asBinder()); } - IBinder getBinder() { + IHwBinder getBinder() { validateInternalStates(); return mIDescrambler.asBinder(); @@ -151,9 +117,8 @@ public final class MediaDescrambler implements AutoCloseable { validateInternalStates(); try { - mIDescrambler.setMediaCasSession(session.mSessionId); - } catch (ServiceSpecificException e) { - MediaCasStateException.throwExceptions(e); + MediaCasStateException.throwExceptionIfNeeded( + mIDescrambler.setMediaCasSession(session.mSessionId)); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -210,7 +175,9 @@ public final class MediaDescrambler implements AutoCloseable { srcBuf, srcBuf.position(), srcBuf.limit(), dstBuf, dstBuf.position(), dstBuf.limit()); } catch (ServiceSpecificException e) { - MediaCasStateException.throwExceptions(e); + MediaCasStateException.throwExceptionIfNeeded(e.errorCode, e.getMessage()); + } catch (RemoteException e) { + cleanupAndRethrowIllegalState(); } return -1; } @@ -234,12 +201,12 @@ public final class MediaDescrambler implements AutoCloseable { } private static native final void native_init(); - private native final void native_setup(@NonNull IBinder decramblerBinder); + private native final void native_setup(@NonNull IHwBinder decramblerBinder); 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, int srcLimit, - ByteBuffer dstBuf, int dstOffset, int dstLimit); + ByteBuffer dstBuf, int dstOffset, int dstLimit) throws RemoteException; static { System.loadLibrary("media_jni"); diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index fe461be6dea55..2c1b4b3526e0d 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -26,8 +26,8 @@ import android.media.MediaCodec; import android.media.MediaFormat; import android.media.MediaHTTPService; import android.net.Uri; -import android.os.Bundle; import android.os.IBinder; +import android.os.IHwBinder; import android.os.PersistableBundle; import com.android.internal.util.Preconditions; @@ -38,9 +38,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Collections; +import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; @@ -263,7 +262,7 @@ final public class MediaExtractor { nativeSetMediaCas(mediaCas.getBinder()); } - private native final void nativeSetMediaCas(@NonNull IBinder casBinder); + private native final void nativeSetMediaCas(@NonNull IHwBinder casBinder); /** * Describes the conditional access system used to scramble a track. @@ -300,6 +299,14 @@ final public class MediaExtractor { } } + private ArrayList toByteArray(@NonNull byte[] data) { + ArrayList byteArray = new ArrayList(data.length); + for (int i = 0; i < data.length; i++) { + byteArray.add(i, Byte.valueOf(data[i])); + } + return byteArray; + } + /** * Retrieves the information about the conditional access system used to scramble * a track. @@ -317,7 +324,7 @@ final public class MediaExtractor { buf.rewind(); final byte[] sessionId = new byte[buf.remaining()]; buf.get(sessionId); - session = mMediaCas.createFromSessionId(sessionId); + session = mMediaCas.createFromSessionId(toByteArray(sessionId)); } return new CasInfo(systemId, session); } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 9686ab5cafb14..02667ca070a43 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -50,6 +50,12 @@ cc_library_shared { "libexif", "libpiex", "libandroidfw", + "libhidlbase", + "libhidltransport", + "android.hardware.cas@1.0", + "android.hardware.cas.native@1.0", + "android.hidl.memory@1.0", + "android.hidl.token@1.0-utils", ], header_libs: ["libhardware_headers"], diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 04230a0ab6bdb..a27d157829051 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -21,6 +21,7 @@ #include "android_media_MediaCodec.h" #include "android_media_MediaCrypto.h" +#include "android_media_MediaDescrambler.h" #include "android_media_MediaMetricsJNI.h" #include "android_media_Utils.h" #include "android_runtime/AndroidRuntime.h" @@ -29,7 +30,7 @@ #include "jni.h" #include "JNIHelp.h" -#include +#include #include @@ -1010,8 +1011,7 @@ static void android_media_MediaCodec_native_configure( sp descrambler; if (descramblerBinderObj != NULL) { - sp binder = ibinderForJavaObject(env, descramblerBinderObj); - descrambler = interface_cast(binder); + descrambler = JDescrambler::GetDescrambler(env, descramblerBinderObj); } err = codec->configure(format, bufferProducer, crypto, descrambler, flags); @@ -1952,7 +1952,7 @@ static const JNINativeMethod gMethods[] = { { "native_configure", "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;" - "Landroid/media/MediaCrypto;Landroid/os/IBinder;I)V", + "Landroid/media/MediaCrypto;Landroid/os/IHwBinder;I)V", (void *)android_media_MediaCodec_native_configure }, { "native_setSurface", diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index c9a1700215a47..2ec8703adc831 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -36,10 +36,13 @@ class IGraphicBufferProducer; struct MediaCodec; struct PersistentSurface; class Surface; -namespace media { -class IDescrambler; -}; -using namespace media; +namespace hardware { +namespace cas { +namespace native { +namespace V1_0 { +struct IDescrambler; +}}}} +using hardware::cas::native::V1_0::IDescrambler; struct JMediaCodec : public AHandler { JMediaCodec( diff --git a/media/jni/android_media_MediaDescrambler.cpp b/media/jni/android_media_MediaDescrambler.cpp index 85d33b7730079..bee52188e798e 100644 --- a/media/jni/android_media_MediaDescrambler.cpp +++ b/media/jni/android_media_MediaDescrambler.cpp @@ -20,16 +20,19 @@ #include "android_media_MediaDescrambler.h" #include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" +#include "android_os_HwRemoteBinder.h" #include "JNIHelp.h" -#include +#include +#include #include +#include #include #include namespace android { -using media::MediaDescrambler::DescrambleInfo; + +using hardware::hidl_handle; struct fields_t { jfieldID context; @@ -94,10 +97,9 @@ static status_t getBufferAndSize( } JDescrambler::JDescrambler(JNIEnv *env, jobject descramblerBinderObj) { - sp cas; - if (descramblerBinderObj != NULL) { - sp binder = ibinderForJavaObject(env, descramblerBinderObj); - mDescrambler = interface_cast(binder); + mDescrambler = GetDescrambler(env, descramblerBinderObj); + if (mDescrambler == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); } } @@ -108,9 +110,23 @@ JDescrambler::~JDescrambler() { mDealer.clear(); } -void JDescrambler::ensureBufferCapacity(size_t neededSize) { +// static +sp JDescrambler::GetDescrambler(JNIEnv *env, jobject obj) { + if (obj != NULL) { + sp hwBinder = + JHwRemoteBinder::GetNativeContext(env, obj)->getBinder(); + + if (hwBinder != NULL) { + return hardware::fromBinder< + IDescrambler, BpHwDescrambler, BnHwDescrambler>(hwBinder); + } + } + return NULL; +} + +bool JDescrambler::ensureBufferCapacity(size_t neededSize) { if (mMem != NULL && mMem->size() >= neededSize) { - return; + return true; } ALOGV("ensureBufferCapacity: current size %zu, new size %zu", @@ -122,49 +138,84 @@ void JDescrambler::ensureBufferCapacity(size_t neededSize) { neededSize = (neededSize + 65535) & ~65535; mDealer = new MemoryDealer(neededSize, "JDescrambler"); mMem = mDealer->allocate(neededSize); + + ssize_t offset; + size_t size; + sp heap = mMem->getMemory(&offset, &size); + if (heap == NULL) { + return false; + } + + native_handle_t* nativeHandle = native_handle_create(1, 0); + if (!nativeHandle) { + ALOGE("ensureBufferCapacity: failed to create native handle"); + return false; + } + nativeHandle->data[0] = heap->getHeapID(); + mDescramblerSrcBuffer.heapBase = hidl_memory("ashmem", + hidl_handle(nativeHandle), heap->getSize()); + mDescramblerSrcBuffer.offset = (uint64_t) offset; + mDescramblerSrcBuffer.size = (uint64_t) size; + return true; } -Status JDescrambler::descramble( +status_t JDescrambler::descramble( jbyte key, - size_t numSubSamples, ssize_t totalLength, - DescramblerPlugin::SubSample *subSamples, + const hidl_vec& subSamples, const void *srcPtr, jint srcOffset, void *dstPtr, jint dstOffset, - ssize_t *result) { + Status *status, + uint32_t *bytesWritten, + hidl_string *detailedError) { // 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 // to 2 shared mem buffers. Mutex::Autolock autolock(mSharedMemLock); - ensureBufferCapacity(totalLength); + if (!ensureBufferCapacity(totalLength)) { + return NO_MEMORY; + } memcpy(mMem->pointer(), (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength); - DescrambleInfo info; - info.dstType = DescrambleInfo::kDestinationTypeVmPointer; - info.numSubSamples = numSubSamples; - info.scramblingControl = (DescramblerPlugin::ScramblingControl) key; - info.subSamples = subSamples; - info.srcMem = mMem; - info.srcOffset = 0; - info.dstPtr = NULL; - info.dstOffset = 0; + DestinationBuffer dstBuffer; + dstBuffer.type = BufferType::SHARED_MEMORY; + dstBuffer.nonsecureMemory = mDescramblerSrcBuffer; - int32_t descrambleResult; - Status status = mDescrambler->descramble(info, &descrambleResult); + auto err = mDescrambler->descramble( + (ScramblingControl) key, + subSamples, + mDescramblerSrcBuffer, + 0, + dstBuffer, + 0, + [&status, &bytesWritten, &detailedError] ( + Status _status, uint32_t _bytesWritten, + const hidl_string& _detailedError) { + *status = _status; + *bytesWritten = _bytesWritten; + *detailedError = _detailedError; + }); - if (status.isOk()) { - *result = (descrambleResult <= totalLength) ? descrambleResult : -1; - if (*result > 0) { - memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *result); + if (!err.isOk()) { + return FAILED_TRANSACTION; + } + + if (*status == Status::OK) { + if (*bytesWritten > 0 && (ssize_t) *bytesWritten <= totalLength) { + memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *bytesWritten); + } else { + // status seems OK but bytesWritten is invalid, we really + // have no idea what is wrong. + *status = Status::ERROR_CAS_UNKNOWN; } } - return status; + return OK; } } // namespace android @@ -191,10 +242,10 @@ static void android_media_MediaDescrambler_native_setup( static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples, jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj, - DescramblerPlugin::SubSample **outSubSamples) { + hidl_vec *outSubSamples) { - if (numSubSamples <= 0 || numSubSamples >= - (signed)(INT32_MAX / sizeof(DescramblerPlugin::SubSample)) ) { + if (numSubSamples <= 0 || + numSubSamples >= (signed)(INT32_MAX / sizeof(SubSample))) { // subSamples array may silently overflow if number of samples are // too large. Use INT32_MAX as maximum allocation size may be less // than SIZE_MAX on some platforms. @@ -215,24 +266,23 @@ static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples, ? NULL : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); - DescramblerPlugin::SubSample *subSamples = - new(std::nothrow) DescramblerPlugin::SubSample[numSubSamples]; - + outSubSamples->resize(numSubSamples); + SubSample *subSamples = outSubSamples->data(); if (subSamples == NULL) { ALOGE("Failed to allocate SubSample array!"); return -1; } for (jint i = 0; i < numSubSamples; ++i) { - subSamples[i].mNumBytesOfClearData = + subSamples[i].numBytesOfClearData = (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i]; - subSamples[i].mNumBytesOfEncryptedData = + subSamples[i].numBytesOfEncryptedData = (numBytesOfEncryptedData == NULL) ? 0 : numBytesOfEncryptedData[i]; - totalSize += subSamples[i].mNumBytesOfClearData + - subSamples[i].mNumBytesOfEncryptedData; + totalSize += subSamples[i].numBytesOfClearData + + subSamples[i].numBytesOfEncryptedData; } if (numBytesOfEncryptedData != NULL) { @@ -248,12 +298,9 @@ static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples, } if (totalSize < 0) { - delete[] subSamples; return -1; } - *outSubSamples = subSamples; - return totalSize; } @@ -280,12 +327,6 @@ static jthrowable createServiceSpecificException( 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, @@ -293,11 +334,12 @@ static jint android_media_MediaDescrambler_native_descramble( jobject dstBuf, jint dstOffset, jint dstLimit) { sp descrambler = getDescrambler(env, thiz); if (descrambler == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + jniThrowException(env, "java/lang/IllegalStateException", + "Invalid descrambler object!"); return -1; } - DescramblerPlugin::SubSample *subSamples = NULL; + hidl_vec subSamples; ssize_t totalLength = getSubSampleInfo( env, numSubSamples, numBytesOfClearDataObj, numBytesOfEncryptedDataObj, &subSamples); @@ -307,7 +349,6 @@ static jint android_media_MediaDescrambler_native_descramble( return -1; } - ssize_t result = -1; void *srcPtr = NULL, *dstPtr = NULL; jbyteArray srcArray = NULL, dstArray = NULL; status_t err = getBufferAndSize( @@ -329,13 +370,15 @@ static jint android_media_MediaDescrambler_native_descramble( } Status status; - if (err == OK) { - status = descrambler->descramble( - key, numSubSamples, totalLength, subSamples, - srcPtr, srcOffset, dstPtr, dstOffset, &result); - } + uint32_t bytesWritten; + hidl_string detailedError; - delete[] subSamples; + err = descrambler->descramble( + key, totalLength, subSamples, + srcPtr, srcOffset, dstPtr, dstOffset, + &status, &bytesWritten, &detailedError); + + // Release byte array before throwing if (srcArray != NULL) { env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0); } @@ -343,51 +386,17 @@ static jint android_media_MediaDescrambler_native_descramble( 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; - } - } + if (err == NO_MEMORY) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + } else if (err == FAILED_TRANSACTION) { + jniThrowException(env, "android/os/RemoteException", NULL); + } else if (status != Status::OK) { + // Throw ServiceSpecific with cas error code and detailed msg, + // which will be re-thrown as MediaCasStateException. + env->Throw(createServiceSpecificException( + env, (int) status, detailedError.c_str())); } - return result; + return bytesWritten; } static const JNINativeMethod gMethods[] = { @@ -395,7 +404,7 @@ static const JNINativeMethod gMethods[] = { (void *)android_media_MediaDescrambler_native_release }, { "native_init", "()V", (void *)android_media_MediaDescrambler_native_init }, - { "native_setup", "(Landroid/os/IBinder;)V", + { "native_setup", "(Landroid/os/IHwBinder;)V", (void *)android_media_MediaDescrambler_native_setup }, { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I", (void *)android_media_MediaDescrambler_native_descramble }, diff --git a/media/jni/android_media_MediaDescrambler.h b/media/jni/android_media_MediaDescrambler.h index aeef05e7968d9..015fad22e13b9 100644 --- a/media/jni/android_media_MediaDescrambler.h +++ b/media/jni/android_media_MediaDescrambler.h @@ -19,34 +19,37 @@ #include "jni.h" -#include -#include +#include + #include #include -#include namespace android { class IMemory; class MemoryDealer; -namespace media { -class IDescrambler; -}; -using namespace media; -using binder::Status; + +using hardware::hidl_memory; +using hardware::hidl_string; +using hardware::hidl_vec; +using namespace hardware::cas::V1_0; +using namespace hardware::cas::native::V1_0; struct JDescrambler : public RefBase { JDescrambler(JNIEnv *env, jobject descramberBinderObj); - Status descramble( + status_t descramble( jbyte key, - size_t numSubSamples, ssize_t totalLength, - DescramblerPlugin::SubSample *subSamples, + const hidl_vec& subSamples, const void *srcPtr, jint srcOffset, void *dstPtr, jint dstOffset, - ssize_t *result); + Status *status, + uint32_t *bytesWritten, + hidl_string *detailedError); + + static sp GetDescrambler(JNIEnv *env, jobject obj); protected: virtual ~JDescrambler(); @@ -55,9 +58,11 @@ private: sp mDescrambler; sp mMem; sp mDealer; + SharedBuffer mDescramblerSrcBuffer; + Mutex mSharedMemLock; - void ensureBufferCapacity(size_t neededSize); + bool ensureBufferCapacity(size_t neededSize); DISALLOW_EVIL_CONSTRUCTORS(JDescrambler); }; diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index 9e5d3d12f0bd6..c9657b119bdb3 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -18,16 +18,20 @@ #define LOG_TAG "MediaExtractor-JNI" #include +#include "android_media_MediaDataSource.h" #include "android_media_MediaExtractor.h" #include "android_media_MediaMetricsJNI.h" - #include "android_media_Utils.h" +#include "android_os_HwRemoteBinder.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" +#include "android_util_Binder.h" #include "jni.h" #include "JNIHelp.h" -#include "android_media_MediaDataSource.h" +#include +#include +#include #include #include #include @@ -37,14 +41,12 @@ #include #include #include -#include - #include -#include "android_util_Binder.h" - namespace android { +using namespace hardware::cas::V1_0; + struct fields_t { jfieldID context; @@ -89,8 +91,28 @@ status_t JMediaExtractor::setDataSource(const sp &datasource) { return mImpl->setDataSource(datasource); } -status_t JMediaExtractor::setMediaCas(const sp &cas) { - return mImpl->setMediaCas(cas); +status_t JMediaExtractor::setMediaCas(JNIEnv *env, jobject casBinderObj) { + if (casBinderObj == NULL) { + return BAD_VALUE; + } + + sp hwBinder = + JHwRemoteBinder::GetNativeContext(env, casBinderObj)->getBinder(); + if (hwBinder == NULL) { + return BAD_VALUE; + } + + sp cas = hardware::fromBinder(hwBinder); + if (cas == NULL) { + return BAD_VALUE; + } + + HalToken halToken; + if (!createHalToken(cas, &halToken)) { + return BAD_VALUE; + } + + return mImpl->setMediaCas(halToken); } size_t JMediaExtractor::countTracks() const { @@ -748,23 +770,13 @@ static void android_media_MediaExtractor_setMediaCas( return; } - if (casBinderObj == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } - - sp cas; - if (casBinderObj != NULL) { - sp binder = ibinderForJavaObject(env, casBinderObj); - cas = interface_cast(binder); - } - status_t err = extractor->setMediaCas(cas); + status_t err = extractor->setMediaCas(env, casBinderObj); if (err != OK) { - cas.clear(); + extractor.clear(); jniThrowException( env, - "java/io/IllegalArgumentException", + "java/lang/IllegalArgumentException", "Failed to set MediaCas on extractor."); } } @@ -896,7 +908,7 @@ static const JNINativeMethod gMethods[] = { { "setDataSource", "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaExtractor_setDataSourceCallback }, - { "nativeSetMediaCas", "(Landroid/os/IBinder;)V", + { "nativeSetMediaCas", "(Landroid/os/IHwBinder;)V", (void *)android_media_MediaExtractor_setMediaCas }, { "getCachedDuration", "()J", diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h index 3d8c50b809ac9..94d36f25ae1c8 100644 --- a/media/jni/android_media_MediaExtractor.h +++ b/media/jni/android_media_MediaExtractor.h @@ -28,10 +28,6 @@ #include "jni.h" namespace android { -namespace media { -class ICas; -}; -using namespace media; struct IMediaHTTPService; class MetaData; @@ -48,7 +44,7 @@ struct JMediaExtractor : public RefBase { status_t setDataSource(int fd, off64_t offset, off64_t size); status_t setDataSource(const sp &source); - status_t setMediaCas(const sp &cas); + status_t setMediaCas(JNIEnv *env, jobject casBinderObj); size_t countTracks() const; status_t getTrackFormat(size_t index, jobject *format) const;