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.