Add onExpirationUpdate and onKeyStatusChange listeners.
We must only create one DrmListener instance.
We then process different listeners in DrmListener::notify.
To facilitate testing, we call the listeners from clearkey plugin's
provideKeyResponse function. We have previously tested
EventType::VENDOR_DEFINED in the same manner.
bug: 77712870
Test: native CTS test testClearKeyPlaybackCenc
Test: CTS MediaDrmMockTest
Change-Id: Ie15e3012a4068824f72371a66e9fca2ee27180f8
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 5597488..2552073 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -17,6 +17,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaDrm"
+#include <inttypes.h>
+
#include <media/NdkMediaDrm.h>
#include <cutils/properties.h>
@@ -40,10 +42,32 @@
{
private:
AMediaDrm *mObj;
- AMediaDrmEventListener mListener;
+ AMediaDrmEventListener mEventListener;
+ AMediaDrmExpirationUpdateListener mExpirationUpdateListener;
+ AMediaDrmKeysChangeListener mKeysChangeListener;
public:
- DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), mListener(listener) {}
+ DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj),
+ mEventListener(listener), mExpirationUpdateListener(NULL), mKeysChangeListener(NULL) {}
+
+ DrmListener(AMediaDrm *obj, AMediaDrmExpirationUpdateListener listener) : mObj(obj),
+ mEventListener(NULL), mExpirationUpdateListener(listener), mKeysChangeListener(NULL) {}
+
+ DrmListener(AMediaDrm *obj, AMediaDrmKeysChangeListener listener) : mObj(obj),
+ mEventListener(NULL), mExpirationUpdateListener(NULL), mKeysChangeListener(listener) {}
+
+ void setEventListener(AMediaDrmEventListener listener) {
+ mEventListener = listener;
+ }
+
+ void setExpirationUpdateListener(AMediaDrmExpirationUpdateListener listener) {
+ mExpirationUpdateListener = listener;
+ }
+
+ void setKeysChangeListener(AMediaDrmKeysChangeListener listener) {
+ mKeysChangeListener = listener;
+ }
+
void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj);
};
@@ -62,27 +86,75 @@
};
void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
- if (!mListener) {
+ if (!mEventListener && !mExpirationUpdateListener && !mKeysChangeListener) {
+ ALOGE("No listeners are specified");
return;
}
+ obj->setDataPosition(0);
+
AMediaDrmSessionId sessionId = {NULL, 0};
int32_t sessionIdSize = obj->readInt32();
- if (sessionIdSize) {
- uint8_t *sessionIdData = new uint8_t[sessionIdSize];
- sessionId.ptr = sessionIdData;
- sessionId.length = sessionIdSize;
- obj->read(sessionIdData, sessionId.length);
+ if (sessionIdSize <= 0) {
+ ALOGE("Invalid session id size");
+ return;
}
- int32_t dataSize = obj->readInt32();
- uint8_t *data = NULL;
- if (dataSize) {
- data = new uint8_t[dataSize];
- obj->read(data, dataSize);
+ std::unique_ptr<uint8_t[]> sessionIdData(new uint8_t[sessionIdSize]);
+ sessionId.ptr = sessionIdData.get();
+ sessionId.length = sessionIdSize;
+ status_t err = obj->read(sessionIdData.get(), sessionId.length);
+ if (err != OK) {
+ ALOGE("Failed to read session id, error=%d", err);
+ return;
}
- // translate DrmPlugin event types into their NDK equivalents
+ if (DrmPlugin::kDrmPluginEventExpirationUpdate == eventType) {
+ int64_t expiryTimeInMS = obj->readInt64();
+ if (expiryTimeInMS >= 0) {
+ (*mExpirationUpdateListener)(mObj, &sessionId, expiryTimeInMS);
+ } else {
+ ALOGE("Failed to read expiry time, status=%" PRId64 "", expiryTimeInMS);
+ }
+ return;
+ } else if (DrmPlugin::kDrmPluginEventKeysChange == eventType) {
+ int32_t numKeys = 0;
+ err = obj->readInt32(&numKeys);
+ if (err != OK) {
+ ALOGE("Failed to read number of keys status, error=%d", err);
+ return;
+ }
+
+ Vector<AMediaDrmKeyStatus> keysStatus;
+ std::vector<std::unique_ptr<uint8_t[]> > dataPointers;
+ AMediaDrmKeyStatus keyStatus;
+
+ for (size_t i = 0; i < numKeys; ++i) {
+ keyStatus.keyId.ptr = nullptr;
+ keyStatus.keyId.length = 0;
+ int32_t idSize = obj->readInt32();
+ if (idSize > 0) {
+ std::unique_ptr<uint8_t[]> data(new uint8_t[idSize]);
+ err = obj->read(data.get(), idSize);
+ if (err != OK) {
+ ALOGE("Failed to read key data, error=%d", err);
+ return;
+ }
+ keyStatus.keyId.ptr = data.get();
+ keyStatus.keyId.length = idSize;
+ dataPointers.push_back(std::move(data));
+ }
+ keyStatus.keyType = static_cast<AMediaDrmKeyStatusType>(obj->readInt32());
+ keysStatus.push(keyStatus);
+ }
+
+ bool hasNewUsableKey = obj->readInt32();
+ (*mKeysChangeListener)(mObj, &sessionId, keysStatus.array(), numKeys, hasNewUsableKey);
+ return;
+ }
+
+ // Handles AMediaDrmEventListener below:
+ // translates DrmPlugin event types into their NDK equivalents
AMediaDrmEventType ndkEventType;
switch(eventType) {
case DrmPlugin::kDrmPluginEventProvisionRequired:
@@ -97,19 +169,30 @@
case DrmPlugin::kDrmPluginEventVendorDefined:
ndkEventType = EVENT_VENDOR_DEFINED;
break;
+ case DrmPlugin::kDrmPluginEventSessionReclaimed:
+ ndkEventType = EVENT_SESSION_RECLAIMED;
+ break;
default:
ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
- goto cleanup;
+ return;
}
- (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
-
- cleanup:
- delete [] sessionId.ptr;
- delete [] data;
+ int32_t dataSize = obj->readInt32();
+ uint8_t *data = NULL;
+ if (dataSize > 0) {
+ data = new uint8_t[dataSize];
+ err = obj->read(data, dataSize);
+ if (err == OK) {
+ (*mEventListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
+ } else {
+ ALOGE("Failed to read event data, error=%d", err);
+ }
+ delete [] data;
+ } else {
+ ALOGE("Error reading parcel: invalid event data size=%d", dataSize);
+ }
}
-
extern "C" {
static media_status_t translateStatus(status_t status) {
@@ -198,6 +281,8 @@
AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
AMediaDrm *mObj = new AMediaDrm();
mObj->mDrm = CreateDrmFromUUID(uuid);
+
+ mObj->mListener.clear();
return mObj;
}
@@ -216,11 +301,47 @@
if (!mObj || mObj->mDrm == NULL) {
return AMEDIA_ERROR_INVALID_OBJECT;
}
- mObj->mListener = new DrmListener(mObj, listener);
+
+ if (mObj->mListener.get()) {
+ mObj->mListener->setEventListener(listener);
+ } else {
+ mObj->mListener = new DrmListener(mObj, listener);
+ }
mObj->mDrm->setListener(mObj->mListener);
return AMEDIA_OK;
}
+EXPORT
+media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *mObj,
+ AMediaDrmExpirationUpdateListener listener) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ if (mObj->mListener.get()) {
+ mObj->mListener->setExpirationUpdateListener(listener);
+ } else {
+ mObj->mListener = new DrmListener(mObj, listener);
+ }
+ mObj->mDrm->setListener(mObj->mListener);
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *mObj,
+ AMediaDrmKeysChangeListener listener) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ if (mObj->mListener.get()) {
+ mObj->mListener->setKeysChangeListener(listener);
+ } else {
+ mObj->mListener = new DrmListener(mObj, listener);
+ }
+ mObj->mDrm->setListener(mObj->mListener);
+ return AMEDIA_OK;
+}
static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) {
diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h
index 0209681..2e438d9 100644
--- a/media/ndk/include/media/NdkMediaDrm.h
+++ b/media/ndk/include/media/NdkMediaDrm.h
@@ -56,6 +56,7 @@
typedef AMediaDrmByteArray AMediaDrmScope;
typedef AMediaDrmByteArray AMediaDrmKeySetId;
typedef AMediaDrmByteArray AMediaDrmSecureStop;
+typedef AMediaDrmByteArray AMediaDrmKeyId;
typedef enum AMediaDrmEventType {
/**
@@ -81,12 +82,89 @@
* This event may indicate some specific vendor-defined condition, see your
* DRM provider documentation for details
*/
- EVENT_VENDOR_DEFINED = 4
+ EVENT_VENDOR_DEFINED = 4,
+
+ /**
+ * This event indicates that a session opened by the app has been reclaimed
+ * by the resource manager.
+ */
+ EVENT_SESSION_RECLAIMED = 5,
} AMediaDrmEventType;
+typedef enum AMediaDrmKeyType {
+ /**
+ * This key request type specifies that the keys will be for online use, they will
+ * not be saved to the device for subsequent use when the device is not connected
+ * to a network.
+ */
+ KEY_TYPE_STREAMING = 1,
+
+ /**
+ * This key request type specifies that the keys will be for offline use, they
+ * will be saved to the device for use when the device is not connected to a network.
+ */
+ KEY_TYPE_OFFLINE = 2,
+
+ /**
+ * This key request type specifies that previously saved offline keys should be released.
+ */
+ KEY_TYPE_RELEASE = 3
+} AMediaDrmKeyType;
+
+/**
+ * Data type containing {key, value} pair
+ */
+typedef struct AMediaDrmKeyValuePair {
+ const char *mKey;
+ const char *mValue;
+} AMediaDrmKeyValue;
+
+typedef enum AMediaKeyStatusType {
+ /**
+ * The key is currently usable to decrypt media data.
+ */
+ KEY_STATUS_TYPE_USABLE,
+
+ /**
+ * The key is no longer usable to decrypt media data because its expiration
+ * time has passed.
+ */
+ KEY_STATUS_TYPE_EXPIRED,
+
+ /**
+ * The key is not currently usable to decrypt media data because its output
+ * requirements cannot currently be met.
+ */
+ KEY_STATUS_TYPE_OUTPUTNOTALLOWED,
+
+ /**
+ * The status of the key is not yet known and is being determined.
+ */
+ KEY_STATUS_TYPE_STATUSPENDING,
+
+ /**
+ * The key is not currently usable to decrypt media data because of an
+ * internal error in processing unrelated to input parameters.
+ */
+ KEY_STATUS_TYPE_INTERNALERROR,
+
+} AMediaDrmKeyStatusType;
+
+typedef struct AMediaDrmKeyStatus {
+ AMediaDrmKeyId keyId;
+ AMediaDrmKeyStatusType keyType;
+} AMediaDrmKeyStatus;
+
typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId,
AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize);
+typedef void (*AMediaDrmExpirationUpdateListener)(AMediaDrm *,
+ const AMediaDrmSessionId *sessionId, int64_t expiryTimeInMS);
+
+typedef void (*AMediaDrmKeysChangeListener)(AMediaDrm *,
+ const AMediaDrmSessionId *sessionId, const AMediaDrmKeyStatus *keyStatus,
+ size_t numKeys, bool hasNewUsableKey);
+
#if __ANDROID_API__ >= 21
/**
@@ -120,6 +198,22 @@
AMediaDrmEventListener listener) __INTRODUCED_IN(21);
/**
+ * Register a callback to be invoked when an expiration update event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *,
+ AMediaDrmExpirationUpdateListener listener) __INTRODUCED_IN(29);
+
+/**
+ * Register a callback to be invoked when a key status change event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *,
+ AMediaDrmKeysChangeListener listener) __INTRODUCED_IN(29);
+
+/**
* Open a new session with the MediaDrm object. A session ID is returned.
*
* returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
@@ -135,34 +229,6 @@
media_status_t AMediaDrm_closeSession(AMediaDrm *,
const AMediaDrmSessionId *sessionId) __INTRODUCED_IN(21);
-typedef enum AMediaDrmKeyType {
- /**
- * This key request type species that the keys will be for online use, they will
- * not be saved to the device for subsequent use when the device is not connected
- * to a network.
- */
- KEY_TYPE_STREAMING = 1,
-
- /**
- * This key request type specifies that the keys will be for offline use, they
- * will be saved to the device for use when the device is not connected to a network.
- */
- KEY_TYPE_OFFLINE = 2,
-
- /**
- * This key request type specifies that previously saved offline keys should be released.
- */
- KEY_TYPE_RELEASE = 3
-} AMediaDrmKeyType;
-
-/**
- * Data type containing {key, value} pair
- */
-typedef struct AMediaDrmKeyValuePair {
- const char *mKey;
- const char *mValue;
-} AMediaDrmKeyValue;
-
/**
* A key request/response exchange occurs between the app and a license server
* to obtain or release keys used to decrypt encrypted content.