Handle filter status callback

Test: make; acloud
Change-Id: Ibff3e6e7fd834b2cbb1ceb75028151af575c27eb
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 4ed8f42..7d68d02 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -33,6 +33,8 @@
     private static final boolean DEBUG = false;
 
     private static final int MSG_ON_FRONTEND_EVENT = 1;
+    private static final int MSG_ON_FILTER_EVENT = 2;
+    private static final int MSG_ON_FILTER_STATUS = 3;
 
     static {
         System.loadLibrary("media_tv_tuner");
@@ -86,6 +88,16 @@
         void onEvent(int frontendEventType);
     }
 
+    /**
+     * Frontend Callback.
+     */
+    public interface FilterCallback {
+        /**
+         * Invoked when filter status changed.
+         */
+        void onFilterStatus(int status);
+    }
+
     @Nullable
     private EventHandler createEventHandler() {
         Looper looper;
@@ -110,6 +122,13 @@
                         mFrontend.mCallback.onEvent(msg.arg1);
                     }
                     break;
+                case MSG_ON_FILTER_STATUS: {
+                    Filter filter = (Filter) msg.obj;
+                    if (filter.mCallback != null) {
+                        filter.mCallback.onFilterStatus(msg.arg1);
+                    }
+                    break;
+                }
                 default:
                     // fall through
             }
@@ -171,13 +190,29 @@
     }
 
     protected class Filter {
+        private long mNativeContext;
+        private FilterCallback mCallback;
         int mId;
         private Filter(int id) {
             mId = id;
         }
+
+        private void onFilterStatus(int status) {
+            if (mHandler != null) {
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(MSG_ON_FILTER_STATUS, status, 0, this));
+            }
+        }
     }
 
-    private Filter openFilter(int type, int subType, int bufferSize) {
-        return nativeOpenFilter(type, subType, bufferSize);
+    private Filter openFilter(int type, int subType, int bufferSize, FilterCallback cb) {
+        Filter filter = nativeOpenFilter(type, subType, bufferSize);
+        if (filter != null) {
+            filter.mCallback = cb;
+            if (mHandler == null) {
+                mHandler = createEventHandler();
+            }
+        }
+        return filter;
     }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f815097..2640572 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -34,9 +34,11 @@
 
 struct fields_t {
     jfieldID context;
+    jfieldID filterContext;
     jmethodID frontendInitID;
     jmethodID filterInitID;
     jmethodID onFrontendEventID;
+    jmethodID onFilterStatusID;
 };
 
 static fields_t gFields;
@@ -44,15 +46,27 @@
 namespace android {
 /////////////// FilterCallback ///////////////////////
 //TODO: implement filter callback
-Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /* filterEvent */) {
+Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
     ALOGD("FilterCallback::onFilterEvent");
     return Void();
 }
-Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus /*status*/) {
-    ALOGD("FilterCallback::onFilterStatu");
+
+Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) {
+    ALOGD("FilterCallback::onFilterStatus");
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(
+            mFilter,
+            gFields.onFilterStatusID,
+            (jint)status);
     return Void();
 }
 
+void FilterCallback::setFilter(const jobject filter) {
+    ALOGD("FilterCallback::setFilter");
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    mFilter = env->NewWeakGlobalRef(filter);
+}
+
 /////////////// FrontendCallback ///////////////////////
 
 FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {}
@@ -186,27 +200,35 @@
         }
     }
 
-    sp<IFilter> f;
-    mDemux->openFilter(type, bufferSize, new FilterCallback,
+    sp<IFilter> filterSp;
+    sp<FilterCallback> callback = new FilterCallback();
+    mDemux->openFilter(type, bufferSize, callback,
             [&](Result, const sp<IFilter>& filter) {
-                f = filter;
+                filterSp = filter;
             });
-    if (f == NULL) {
+    if (filterSp == NULL) {
         ALOGD("Failed to open filter, type = %d", type.mainType);
         return NULL;
     }
     int fId;
-    f->getId([&](Result, uint32_t filterId) {
+    filterSp->getId([&](Result, uint32_t filterId) {
         fId = filterId;
     });
-    mFilters[fId] = f;
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    return env->NewObject(
-            env->FindClass("android/media/tv/tuner/Tuner$Filter"),
-            gFields.filterInitID,
-            mObject,
-            (jint) fId);
+    jobject filterObj =
+            env->NewObject(
+                    env->FindClass("android/media/tv/tuner/Tuner$Filter"),
+                    gFields.filterInitID,
+                    mObject,
+                    (jint) fId);
+
+    filterSp->incStrong(filterObj);
+    env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get());
+
+    callback->setFilter(filterObj);
+
+    return filterObj;
 }
 
 }  // namespace android
@@ -233,6 +255,10 @@
     return (JTuner *)env->GetLongField(thiz, gFields.context);
 }
 
+static sp<IFilter> getFilter(JNIEnv *env, jobject filter) {
+    return (IFilter *)env->GetLongField(filter, gFields.filterContext);
+}
+
 static void android_media_tv_Tuner_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
     CHECK(clazz != NULL);
@@ -247,8 +273,11 @@
             env->GetMethodID(frontendClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
 
     jclass filterClazz = env->FindClass("android/media/tv/tuner/Tuner$Filter");
+    gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
     gFields.filterInitID =
             env->GetMethodID(filterClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
+    gFields.onFilterStatusID =
+            env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
 }
 
 static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 7a889c3..ab48761 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -44,6 +44,10 @@
 struct FilterCallback : public IFilterCallback {
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
     virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
+
+    void setFilter(const jobject filter);
+private:
+    jweak mFilter;
 };
 
 struct FrontendCallback : public IFrontendCallback {
@@ -76,7 +80,6 @@
     sp<IFrontend> mFe;
     sp<IDemux> mDemux;
     int mDemuxId;
-    std::unordered_map<int, sp<IFilter>> mFilters;
 };
 
 }  // namespace android