DVR API: class, methods, JNI

Test: make; alcoud;
Change-Id: Iddcffef436dc520c969a3dc4f854e3d4c902c2b8
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 3d89909..c91325a 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -86,6 +86,7 @@
 
     private native Descrambler nativeOpenDescrambler();
 
+    private native Dvr nativeOpenDvr(int type, int bufferSize);
 
     /**
      * Frontend Callback.
@@ -118,6 +119,20 @@
         void onFilterStatus(int status);
     }
 
+    /**
+     * DVR Callback.
+     */
+    public interface DvrCallback {
+        /**
+         * Invoked when record status changed.
+         */
+        void onRecordStatus(int status);
+        /**
+         * Invoked when playback status changed.
+         */
+        void onPlaybackStatus(int status);
+    }
+
     @Nullable
     private EventHandler createEventHandler() {
         Looper looper;
@@ -321,4 +336,39 @@
         Descrambler descrambler = nativeOpenDescrambler();
         return descrambler;
     }
+
+    // TODO: consider splitting Dvr to Playback and Recording
+    protected class Dvr {
+        private long mNativeContext;
+        private DvrCallback mCallback;
+
+        private native boolean nativeAttachFilter(Filter filter);
+        private native boolean nativeDetachFilter(Filter filter);
+        private native boolean nativeStartDvr();
+        private native boolean nativeStopDvr();
+        private native boolean nativeFlushDvr();
+
+        private Dvr() {}
+
+        public boolean attachFilter(Filter filter) {
+            return nativeAttachFilter(filter);
+        }
+        public boolean detachFilter(Filter filter) {
+            return nativeDetachFilter(filter);
+        }
+        public boolean start() {
+            return nativeStartDvr();
+        }
+        public boolean stop() {
+            return nativeStopDvr();
+        }
+        public boolean flush() {
+            return nativeFlushDvr();
+        }
+    }
+
+    private Dvr openDvr(int type, int bufferSize) {
+        Dvr dvr = nativeOpenDvr(type, bufferSize);
+        return dvr;
+    }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index efdd333..f5202fc 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -38,8 +38,10 @@
     jfieldID tunerContext;
     jfieldID filterContext;
     jfieldID descramblerContext;
+    jfieldID dvrContext;
     jmethodID frontendInitID;
     jmethodID filterInitID;
+    jmethodID dvrInitID;
     jmethodID onFrontendEventID;
     jmethodID onFilterStatusID;
     jmethodID lnbInitID;
@@ -67,6 +69,23 @@
     return Void();
 }
 
+/////////////// DvrCallback ///////////////////////
+Return<void> DvrCallback::onRecordStatus(RecordStatus /*status*/) {
+    ALOGD("DvrCallback::onRecordStatus");
+    return Void();
+}
+
+Return<void> DvrCallback::onPlaybackStatus(PlaybackStatus /*status*/) {
+    ALOGD("DvrCallback::onPlaybackStatus");
+    return Void();
+}
+
+void DvrCallback::setDvr(const jobject dvr) {
+    ALOGD("FilterCallback::setDvr");
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    mDvr = env->NewWeakGlobalRef(dvr);
+}
+
 /////////////// FilterCallback ///////////////////////
 //TODO: implement filter callback
 Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -327,6 +346,39 @@
     return filterObj;
 }
 
+jobject JTuner::openDvr(DvrType type, int bufferSize) {
+    ALOGD("JTuner::openDvr");
+    if (mDemux == NULL) {
+        if (!openDemux()) {
+            return NULL;
+        }
+    }
+    sp<IDvr> dvrSp;
+    sp<DvrCallback> callback = new DvrCallback();
+    mDemux->openDvr(type, bufferSize, callback,
+            [&](Result, const sp<IDvr>& dvr) {
+                dvrSp = dvr;
+            });
+
+    if (dvrSp == NULL) {
+        return NULL;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jobject dvrObj =
+            env->NewObject(
+                    env->FindClass("android/media/tv/tuner/Tuner$Dvr"),
+                    gFields.dvrInitID,
+                    mObject);
+
+    dvrSp->incStrong(dvrObj);
+    env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get());
+
+    callback->setDvr(dvrObj);
+
+    return dvrObj;
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -369,6 +421,10 @@
     return (IFilter *)env->GetLongField(filter, gFields.filterContext);
 }
 
+static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) {
+    return (IDvr *)env->GetLongField(dvr, gFields.dvrContext);
+}
+
 static void android_media_tv_Tuner_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
     CHECK(clazz != NULL);
@@ -399,6 +455,10 @@
     gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
     gFields.descramblerInitID =
             env->GetMethodID(descramblerClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
+
+    jclass dvrClazz = env->FindClass("android/media/tv/tuner/Tuner$Dvr");
+    gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
+    gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
 }
 
 static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
@@ -493,6 +553,58 @@
     return result == Result::SUCCESS;
 }
 
+static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint type, jint bufferSize) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
+}
+
+static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IFilter> filterSp = getFilter(env, filter);
+    if (dvrSp == NULL || filterSp == NULL) {
+        return false;
+    }
+    Result result = dvrSp->attachFilter(filterSp);
+    return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IFilter> filterSp = getFilter(env, filter);
+    if (dvrSp == NULL || filterSp == NULL) {
+        return false;
+    }
+    Result result = dvrSp->detachFilter(filterSp);
+    return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to start dvr: dvr not found");
+        return false;
+    }
+    return dvrSp->start() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to stop dvr: dvr not found");
+        return false;
+    }
+    return dvrSp->stop() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to flush dvr: dvr not found");
+        return false;
+    }
+    return dvrSp->flush() == Result::SUCCESS;
+}
+
 static const JNINativeMethod gTunerMethods[] = {
     { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
     { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
@@ -508,6 +620,8 @@
             (void *)android_media_tv_Tuner_open_lnb_by_id },
     { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Tuner$Descrambler;",
             (void *)android_media_tv_Tuner_open_descrambler },
+    { "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
+            (void *)android_media_tv_Tuner_open_dvr },
 };
 
 static const JNINativeMethod gFilterMethods[] = {
@@ -523,6 +637,16 @@
             (void *)android_media_tv_Tuner_remove_pid },
 };
 
+static const JNINativeMethod gDvrMethods[] = {
+    { "nativeAttachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
+            (void *)android_media_tv_Tuner_attach_filter },
+    { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
+            (void *)android_media_tv_Tuner_detach_filter },
+    { "nativeStartDvr", "()Z", (void *)android_media_tv_Tuner_start_dvr },
+    { "nativeStopDvr", "()Z", (void *)android_media_tv_Tuner_stop_dvr },
+    { "nativeFlushDvr", "()Z", (void *)android_media_tv_Tuner_flush_dvr },
+};
+
 static bool register_android_media_tv_Tuner(JNIEnv *env) {
     if (AndroidRuntime::registerNativeMethods(
             env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) {
@@ -543,6 +667,13 @@
         ALOGE("Failed to register descrambler native methods");
         return false;
     }
+    if (AndroidRuntime::registerNativeMethods(
+            env, "android/media/tv/tuner/Tuner$Dvr",
+            gDvrMethods,
+            NELEM(gDvrMethods)) != JNI_OK) {
+        ALOGE("Failed to register dvr 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 d3aec64..3bf6ec8 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -29,12 +29,15 @@
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxPid;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
 using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
 using ::android::hardware::tv::tuner::V1_0::IDemux;
 using ::android::hardware::tv::tuner::V1_0::IDescrambler;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
 using ::android::hardware::tv::tuner::V1_0::IFilter;
 using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
 using ::android::hardware::tv::tuner::V1_0::IFrontend;
@@ -44,6 +47,8 @@
 using ::android::hardware::tv::tuner::V1_0::ITuner;
 using ::android::hardware::tv::tuner::V1_0::LnbEventType;
 using ::android::hardware::tv::tuner::V1_0::LnbId;
+using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using ::android::hardware::tv::tuner::V1_0::RecordStatus;
 
 namespace android {
 
@@ -55,6 +60,15 @@
     LnbId mId;
 };
 
+struct DvrCallback : public IDvrCallback {
+    virtual Return<void> onRecordStatus(RecordStatus status);
+    virtual Return<void> onPlaybackStatus(PlaybackStatus status);
+
+    void setDvr(const jobject dvr);
+private:
+    jweak mDvr;
+};
+
 struct FilterCallback : public IFilterCallback {
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
     virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
@@ -85,6 +99,7 @@
     jobject openLnbById(int id);
     jobject openFilter(DemuxFilterType type, int bufferSize);
     jobject openDescrambler();
+    jobject openDvr(DvrType type, int bufferSize);
 
 protected:
     bool openDemux();