Tuner JNI: linearBlock and OnDestroyNotify

referred to ag/10877916 by Henry quxiangfang@

Bug: 139308734
Test: mmm
Change-Id: I7574d27cb1f582a5556e22eac4fff87335230b00
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 247c1c4..af63070 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -28,14 +28,19 @@
  */
 @SystemApi
 public class MediaEvent extends FilterEvent {
-    private native int nativeGetAudioHandle();
+    private long mNativeContext;
+    private final Object mLock = new Object();
+
+    private native Long nativeGetAudioHandle();
+    private native LinearBlock nativeGetLinearBlock();
+    private native void nativeFinalize();
 
     private final int mStreamId;
     private final boolean mIsPtsPresent;
     private final long mPts;
     private final long mDataLength;
     private final long mOffset;
-    private final LinearBlock mLinearBlock;
+    private LinearBlock mLinearBlock;
     private final boolean mIsSecureMemory;
     private final long mDataId;
     private final int mMpuSequenceNumber;
@@ -103,7 +108,12 @@
      */
     @Nullable
     public LinearBlock getLinearBlock() {
-        return mLinearBlock;
+        synchronized (mLock) {
+            if (mLinearBlock == null) {
+                mLinearBlock = nativeGetLinearBlock();
+            }
+            return mLinearBlock;
+        }
     }
 
     /**
@@ -163,4 +173,15 @@
     public AudioDescriptor getExtraMetaData() {
         return mExtraMetaData;
     }
+
+
+    /**
+     * Finalize the MediaEvent object.
+     * @hide
+     */
+    @Override
+    protected void finalize() {
+        nativeFinalize();
+        mNativeContext = 0;
+    }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index ab311c0e..8058314 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -21,8 +21,7 @@
 #include "android_media_tv_Tuner.h"
 #include "android_runtime/AndroidRuntime.h"
 
-#include <C2BlockInternal.h>
-#include <C2HandleIonInternal.h>
+#include <android-base/logging.h>
 #include <android/hardware/tv/tuner/1.0/ITuner.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <nativehelper/JNIHelp.h>
@@ -145,6 +144,7 @@
     jfieldID descramblerContext;
     jfieldID dvrRecorderContext;
     jfieldID dvrPlaybackContext;
+    jfieldID mediaEventContext;
     jmethodID frontendInitID;
     jmethodID filterInitID;
     jmethodID timeFilterInitID;
@@ -169,6 +169,12 @@
 static int IP_V4_LENGTH = 4;
 static int IP_V6_LENGTH = 16;
 
+void DestroyCallback(const C2Buffer * /* buf */, void *arg) {
+    android::sp<android::MediaEvent> event = (android::MediaEvent *)arg;
+    event->mAvHandleRefCnt--;
+    event->finalize();
+}
+
 namespace android {
 /////////////// LnbCallback ///////////////////////
 LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) {
@@ -280,17 +286,69 @@
     return *mDvrMQ;
 }
 
-/////////////// FilterCallback ///////////////////////
-//TODO: implement filter callback
-jobject FilterCallback::handleToLinearBlock(const native_handle_t* handle, uint32_t size) {
-    ALOGD("FilterCallback::handleToLinearBlock");
-    C2HandleIon* ion = new C2HandleIon(handle->data[0], size);
-    std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(ion);
+/////////////// C2DataIdInfo ///////////////////////
+
+C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) {
+    CHECK(isGlobal());
+    CHECK_EQ(C2Param::INFO, kind());
+    DummyInfo info{value};
+    memcpy(this + 1, static_cast<C2Param *>(&info) + 1, kParamSize - sizeof(C2Param));
+}
+
+/////////////// MediaEvent ///////////////////////
+
+MediaEvent::MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle,
+        uint64_t dataId, uint64_t dataLength, jobject obj) : mIFilter(iFilter),
+        mDataId(dataId), mDataLength(dataLength), mBuffer(nullptr),
+        mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    mMediaEventObj = env->NewWeakGlobalRef(obj);
+    mAvHandle = native_handle_clone(avHandle.getNativeHandle());
+}
+
+MediaEvent::~MediaEvent() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->DeleteWeakGlobalRef(mMediaEventObj);
+    mMediaEventObj = NULL;
+    native_handle_delete(mAvHandle);
+    if (mIonHandle != NULL) {
+        delete mIonHandle;
+    }
+    if (mC2Buffer != NULL) {
+        mC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this);
+    }
+}
+
+void MediaEvent::finalize() {
+    if (mAvHandleRefCnt == 0) {
+        mIFilter->releaseAvHandle(hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0);
+        native_handle_close(mAvHandle);
+    }
+}
+
+jobject MediaEvent::getLinearBlock() {
+    ALOGD("MediaEvent::getLinearBlock");
+    if (mAvHandle == NULL) {
+        return NULL;
+    }
+    if (mLinearBlockObj != NULL) {
+        return mLinearBlockObj;
+    }
+    mIonHandle = new C2HandleIon(mAvHandle->data[0], mDataLength);
+    std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
     context->mBlock = block;
-
+    mC2Buffer = context->toC2Buffer(0, mDataLength);
+    if (mAvHandle->numInts > 0) {
+        // use first int in the native_handle as the index
+        int index = mAvHandle->data[mAvHandle->numFds];
+        std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+        std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
+        mC2Buffer->setInfo(info);
+    }
+    mC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
     jobject linearBlock =
             env->NewObject(
                     env->FindClass("android/media/MediaCodec$LinearBlock"),
@@ -300,9 +358,18 @@
             gFields.linearBlockSetInternalStateID,
             (jlong)context.release(),
             true);
-    return linearBlock;
+    mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
+    mAvHandleRefCnt++;
+    return mLinearBlockObj;
 }
 
+uint64_t MediaEvent::getAudioHandle() {
+    mDataIdRefCnt++;
+    return mDataId;
+}
+
+/////////////// FilterCallback ///////////////////////
+
 jobjectArray FilterCallback::getSectionEvent(
         jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -333,6 +400,7 @@
             "<init>",
             "(IZJJJLandroid/media/MediaCodec$LinearBlock;"
             "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
+    jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
 
     for (int i = 0; i < events.size(); i++) {
         auto event = events[i];
@@ -358,12 +426,6 @@
         }
 
         jlong dataLength = static_cast<jlong>(mediaEvent.dataLength);
-        const native_handle_t* h = NULL;
-        jobject block = NULL;
-        if (mediaEvent.avMemory != NULL) {
-            h = mediaEvent.avMemory.getNativeHandle();
-            block = handleToLinearBlock(h, dataLength);
-        }
 
         jint streamId = static_cast<jint>(mediaEvent.streamId);
         jboolean isPtsPresent = static_cast<jboolean>(mediaEvent.isPtsPresent);
@@ -376,8 +438,18 @@
 
         jobject obj =
                 env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
-                offset, block, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData,
+                offset, NULL, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData,
                 audioDescriptor);
+
+        if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
+            sp<MediaEvent> mediaEventSp =
+                           new MediaEvent(mIFilter, mediaEvent.avMemory,
+                               mediaEvent.avDataId, dataLength, obj);
+            mediaEventSp->mAvHandleRefCnt++;
+            env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
+            mediaEventSp->incStrong(obj);
+        }
+
         env->SetObjectArrayElement(arr, i, obj);
     }
     return arr;
@@ -594,10 +666,10 @@
     return Void();
 }
 
-void FilterCallback::setFilter(const jobject filter) {
+void FilterCallback::setFilter(const sp<Filter> filter) {
     ALOGD("FilterCallback::setFilter");
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    mFilter = env->NewWeakGlobalRef(filter);
+    mFilter = filter->mFilterObj;
+    mIFilter = filter->mFilterSp;
 }
 
 FilterCallback::~FilterCallback() {
@@ -1431,7 +1503,7 @@
     filterSp->incStrong(filterObj);
     env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get());
 
-    callback->setFilter(filterObj);
+    callback->setFilter(filterSp);
 
     return filterObj;
 }
@@ -2390,6 +2462,9 @@
     gFields.onDvrPlaybackStatusID =
             env->GetMethodID(dvrPlaybackClazz, "onPlaybackStatusChanged", "(I)V");
 
+    jclass mediaEventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
+    gFields.mediaEventContext = env->GetFieldID(mediaEventClazz, "mNativeContext", "J");
+
     jclass linearBlockClazz = env->FindClass("android/media/MediaCodec$LinearBlock");
     gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V");
     gFields.linearBlockSetInternalStateID =
@@ -3507,6 +3582,52 @@
     return copyData(env, dvrSp->mDvrMQ, dvrSp->mDvrMQEventFlag, buffer, offset, size);
 }
 
+static sp<MediaEvent> getMediaEventSp(JNIEnv *env, jobject mediaEventObj) {
+    return (MediaEvent *)env->GetLongField(mediaEventObj, gFields.mediaEventContext);
+}
+
+static jobject android_media_tv_Tuner_media_event_get_linear_block(
+        JNIEnv* env, jobject mediaEventObj) {
+    sp<MediaEvent> mediaEventSp = getMediaEventSp(env, mediaEventObj);
+    if (mediaEventSp == NULL) {
+        ALOGD("Failed get MediaEvent");
+        return NULL;
+    }
+
+    return mediaEventSp->getLinearBlock();
+}
+
+static jobject android_media_tv_Tuner_media_event_get_audio_handle(
+        JNIEnv* env, jobject mediaEventObj) {
+    sp<MediaEvent> mediaEventSp = getMediaEventSp(env, mediaEventObj);
+    if (mediaEventSp == NULL) {
+        ALOGD("Failed get MediaEvent");
+        return NULL;
+    }
+
+    android::Mutex::Autolock autoLock(mediaEventSp->mLock);
+    uint64_t audioHandle = mediaEventSp->getAudioHandle();
+    jclass longClazz = env->FindClass("java/lang/Long");
+    jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
+
+    jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(audioHandle));
+    return longObj;
+}
+
+static void android_media_tv_Tuner_media_event_finalize(JNIEnv* env, jobject mediaEventObj) {
+    sp<MediaEvent> mediaEventSp = getMediaEventSp(env, mediaEventObj);
+    if (mediaEventSp == NULL) {
+        ALOGD("Failed get MediaEvent");
+        return;
+    }
+
+    android::Mutex::Autolock autoLock(mediaEventSp->mLock);
+    mediaEventSp->mAvHandleRefCnt--;
+    mediaEventSp->finalize();
+
+    mediaEventSp->decStrong(mediaEventObj);
+}
+
 static const JNINativeMethod gTunerMethods[] = {
     { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
     { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
@@ -3629,6 +3750,15 @@
     { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_lnb },
 };
 
+static const JNINativeMethod gMediaEventMethods[] = {
+    { "nativeGetLinearBlock", "()Landroid/media/MediaCodec$LinearBlock;",
+            (void *)android_media_tv_Tuner_media_event_get_linear_block },
+    { "nativeGetAudioHandle", "()Ljava/lang/Long;",
+            (void *)android_media_tv_Tuner_media_event_get_audio_handle },
+    { "nativeFinalize", "()V",
+            (void *)android_media_tv_Tuner_media_event_finalize },
+};
+
 static bool register_android_media_tv_Tuner(JNIEnv *env) {
     if (AndroidRuntime::registerNativeMethods(
             env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) {
@@ -3677,6 +3807,13 @@
         ALOGE("Failed to register lnb native methods");
         return false;
     }
+    if (AndroidRuntime::registerNativeMethods(
+            env, "android/media/tv/tuner/filter/MediaEvent",
+            gMediaEventMethods,
+            NELEM(gMediaEventMethods)) != JNI_OK) {
+        ALOGE("Failed to register MediaEvent native methods");
+        return false;
+    }
     return true;
 }
 
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 3da78ac..c469a3a 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -18,10 +18,14 @@
 #define _ANDROID_MEDIA_TV_TUNER_H_
 
 #include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <C2BlockInternal.h>
+#include <C2HandleIonInternal.h>
+#include <C2ParamDef.h>
 #include <fmq/MessageQueue.h>
 #include <fstream>
 #include <string>
 #include <unordered_map>
+#include <utils/Mutex.h>
 #include <utils/RefBase.h>
 
 #include "jni.h"
@@ -30,6 +34,7 @@
 using ::android::hardware::MQDescriptorSync;
 using ::android::hardware::MessageQueue;
 using ::android::hardware::Return;
+using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::kSynchronizedReadWrite;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
@@ -106,15 +111,48 @@
     int mFd;
 };
 
+struct MediaEvent : public RefBase {
+    MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle, uint64_t dataId,
+        uint64_t dataLength, jobject obj);
+    ~MediaEvent();
+    jobject getLinearBlock();
+    uint64_t getAudioHandle();
+    void finalize();
+
+    sp<IFilter> mIFilter;
+    native_handle_t* mAvHandle;
+    uint64_t mDataId;
+    uint64_t mDataLength;
+    uint8_t* mBuffer;
+    android::Mutex mLock;
+    int mDataIdRefCnt;
+    int mAvHandleRefCnt;
+    jweak mMediaEventObj;
+    jweak mLinearBlockObj;
+    C2HandleIon* mIonHandle;
+    std::shared_ptr<C2Buffer> mC2Buffer;
+};
+
+struct Filter : public RefBase {
+    Filter(sp<IFilter> sp, jobject obj);
+    ~Filter();
+    int close();
+    sp<IFilter> getIFilter();
+    sp<IFilter> mFilterSp;
+    std::unique_ptr<MQ> mFilterMQ;
+    EventFlag* mFilterMQEventFlag;
+    jweak mFilterObj;
+};
+
 struct FilterCallback : public IFilterCallback {
     ~FilterCallback();
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
     virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
 
-    void setFilter(const jobject filter);
-    jobject handleToLinearBlock(const native_handle_t* handle, uint32_t size);
+    void setFilter(const sp<Filter> filter);
 private:
     jweak mFilter;
+    sp<IFilter> mIFilter;
     jobjectArray getSectionEvent(
             jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
     jobjectArray getMediaEvent(
@@ -144,17 +182,6 @@
     FrontendId mId;
 };
 
-struct Filter : public RefBase {
-    Filter(sp<IFilter> sp, jobject obj);
-    ~Filter();
-    int close();
-    sp<IFilter> getIFilter();
-    sp<IFilter> mFilterSp;
-    std::unique_ptr<MQ> mFilterMQ;
-    EventFlag* mFilterMQEventFlag;
-    jweak mFilterObj;
-};
-
 struct TimeFilter : public RefBase {
     TimeFilter(sp<ITimeFilter> sp, jweak obj);
     ~TimeFilter();
@@ -219,6 +246,14 @@
     static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
 };
 
+class C2DataIdInfo : public C2Param {
+public:
+    C2DataIdInfo(uint32_t index, uint64_t value);
+private:
+    typedef C2GlobalParam<C2Info, C2Int64Value, 0> DummyInfo;
+    static const size_t kParamSize = sizeof(DummyInfo);
+};
+
 }  // namespace android
 
 #endif  // _ANDROID_MEDIA_TV_TUNER_H_