Implement remaining ITunerCallback methods.

Test: instrumentation tests passes, none added
Bug: b/36863239
Change-Id: I57de30992f679624c8fde0657c0e24b5cbfab905
diff --git a/Android.mk b/Android.mk
index 00dc784..1140c6b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -713,6 +713,7 @@
 	frameworks/base/core/java/android/print/PrintJobId.aidl \
 	frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
 	frameworks/base/core/java/android/hardware/radio/RadioManager.aidl \
+	frameworks/base/core/java/android/hardware/radio/RadioMetadata.aidl \
 	frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
 	frameworks/base/core/java/android/hardware/usb/UsbInterface.aidl \
 	frameworks/base/core/java/android/hardware/usb/UsbEndpoint.aidl \
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index b32c683..aed114e 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -17,10 +17,18 @@
 package android.hardware.radio;
 
 import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
 
 /** {@hide} */
 oneway interface ITunerCallback {
     void onError(int status);
     void onConfigurationChanged(in RadioManager.BandConfig config);
     void onProgramInfoChanged(in RadioManager.ProgramInfo info);
+    void onMetadataChanged(in RadioMetadata metadata);
+    void onTrafficAnnouncement(boolean active);
+    void onEmergencyAnnouncement(boolean active);
+    void onAntennaState(boolean connected);
+    void onBackgroundScanAvailabilityChange(boolean isAvailable);
+    void onBackgroundScanComplete();
+    void onProgramListChanged();
 }
diff --git a/core/java/android/hardware/radio/RadioMetadata.aidl b/core/java/android/hardware/radio/RadioMetadata.aidl
new file mode 100644
index 0000000..cbf4713
--- /dev/null
+++ b/core/java/android/hardware/radio/RadioMetadata.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+/** @hide */
+parcelable RadioMetadata;
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 6e1232d..78d7b61 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -19,14 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.content.Context;
-import android.content.Intent;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import java.lang.ref.WeakReference;
+
 import java.util.List;
-import java.util.UUID;
 
 /**
  * RadioTuner interface provides methods to control a radio tuner on the device: selecting and
@@ -303,6 +298,16 @@
     public static final  int ERROR_SCAN_TIMEOUT = 3;
     /** The requested configuration could not be applied */
     public static final  int ERROR_CONFIG = 4;
+    /**
+     * Background scan was interrupted due to hardware becoming temporarily unavailable.
+     * @hide FutureFeature
+     */
+    public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5;
+    /**
+     * Background scan failed due to other error, ie. HW failure.
+     * @hide FutureFeature
+     */
+    public static final int ERROR_BACKGROUND_SCAN_FAILED = 6;
 
     /**
      * Callback provided by the client application when opening a {@link RadioTuner}
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index ba85017..155ffa7 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -51,4 +51,39 @@
     public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
         mHandler.post(() -> mCallback.onProgramInfoChanged(info));
     }
+
+    @Override
+    public void onMetadataChanged(RadioMetadata metadata) {
+        mHandler.post(() -> mCallback.onMetadataChanged(metadata));
+    }
+
+    @Override
+    public void onTrafficAnnouncement(boolean active) {
+        mHandler.post(() -> mCallback.onTrafficAnnouncement(active));
+    }
+
+    @Override
+    public void onEmergencyAnnouncement(boolean active) {
+        mHandler.post(() -> mCallback.onEmergencyAnnouncement(active));
+    }
+
+    @Override
+    public void onAntennaState(boolean connected) {
+        mHandler.post(() -> mCallback.onAntennaState(connected));
+    }
+
+    @Override
+    public void onBackgroundScanAvailabilityChange(boolean isAvailable) {
+        mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
+    }
+
+    @Override
+    public void onBackgroundScanComplete() {
+        mHandler.post(() -> mCallback.onBackgroundScanComplete());
+    }
+
+    @Override
+    public void onProgramListChanged() {
+        mHandler.post(() -> mCallback.onProgramListChanged());
+    }
 }
diff --git a/services/core/java/com/android/server/radio/TunerCallback.java b/services/core/java/com/android/server/radio/TunerCallback.java
index fcc874b..d10f2c6 100644
--- a/services/core/java/com/android/server/radio/TunerCallback.java
+++ b/services/core/java/com/android/server/radio/TunerCallback.java
@@ -20,6 +20,7 @@
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
 import android.hardware.radio.RadioTuner;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -57,6 +58,18 @@
         nativeDetach(mNativeContext);
     }
 
+    private interface RunnableThrowingRemoteException {
+        void run() throws RemoteException;
+    }
+
+    private void dispatch(RunnableThrowingRemoteException func) {
+        try {
+            func.run();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "client died", e);
+        }
+    }
+
     // called from native side
     private void handleHwFailure() {
         onError(RadioTuner.ERROR_HARDWARE_FAILURE);
@@ -65,29 +78,52 @@
 
     @Override
     public void onError(int status) {
-        try {
-            mClientCallback.onError(status);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "client died", e);
-        }
+        dispatch(() -> mClientCallback.onError(status));
     }
 
     @Override
     public void onConfigurationChanged(RadioManager.BandConfig config) {
-        try {
-            mClientCallback.onConfigurationChanged(config);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "client died", e);
-        }
+        dispatch(() -> mClientCallback.onConfigurationChanged(config));
     }
 
     @Override
     public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
-        try {
-            mClientCallback.onProgramInfoChanged(info);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "client died", e);
-        }
+        dispatch(() -> mClientCallback.onProgramInfoChanged(info));
+    }
+
+    @Override
+    public void onMetadataChanged(RadioMetadata metadata) {
+        dispatch(() -> mClientCallback.onMetadataChanged(metadata));
+    }
+
+    @Override
+    public void onTrafficAnnouncement(boolean active) {
+        dispatch(() -> mClientCallback.onTrafficAnnouncement(active));
+    }
+
+    @Override
+    public void onEmergencyAnnouncement(boolean active) {
+        dispatch(() -> mClientCallback.onEmergencyAnnouncement(active));
+    }
+
+    @Override
+    public void onAntennaState(boolean connected) {
+        dispatch(() -> mClientCallback.onAntennaState(connected));
+    }
+
+    @Override
+    public void onBackgroundScanAvailabilityChange(boolean isAvailable) {
+        dispatch(() -> mClientCallback.onBackgroundScanAvailabilityChange(isAvailable));
+    }
+
+    @Override
+    public void onBackgroundScanComplete() {
+        dispatch(() -> mClientCallback.onBackgroundScanComplete());
+    }
+
+    @Override
+    public void onProgramListChanged() {
+        dispatch(() -> mClientCallback.onProgramListChanged());
     }
 
     @Override
diff --git a/services/core/jni/com_android_server_radio_Tuner_TunerCallback.cpp b/services/core/jni/com_android_server_radio_Tuner_TunerCallback.cpp
index 1290c7a..a3cfeff 100644
--- a/services/core/jni/com_android_server_radio_Tuner_TunerCallback.cpp
+++ b/services/core/jni/com_android_server_radio_Tuner_TunerCallback.cpp
@@ -53,6 +53,13 @@
         jmethodID onError;
         jmethodID onConfigurationChanged;
         jmethodID onProgramInfoChanged;
+        jmethodID onMetadataChanged;
+        jmethodID onTrafficAnnouncement;
+        jmethodID onEmergencyAnnouncement;
+        jmethodID onAntennaState;
+        jmethodID onBackgroundScanAvailabilityChange;
+        jmethodID onBackgroundScanComplete;
+        jmethodID onProgramListChanged;
     } TunerCallback;
 } gjni;
 
@@ -63,6 +70,8 @@
     CANCELLED = 2,
     SCAN_TIMEOUT = 3,
     CONFIG = 4,
+    BACKGROUND_SCAN_UNAVAILABLE = 5,
+    BACKGROUND_SCAN_FAILED = 6,
 };
 
 static Mutex gContextMutex;
@@ -191,48 +200,93 @@
 }
 
 Return<void> NativeCallback::afSwitch(const V1_0::ProgramInfo& info) {
-    ALOGE("Not implemented: afSwitch");
-    return Return<void>();
+    ALOGV("afSwitch()");
+    return tuneComplete(Result::OK, info);
 }
 
 Return<void> NativeCallback::afSwitch_1_1(const V1_1::ProgramInfo& info) {
-    ALOGE("Not implemented: afSwitch_1_1");
-    return Return<void>();
+    ALOGV("afSwitch_1_1()");
+    return tuneComplete_1_1(Result::OK, info);
 }
 
 Return<void> NativeCallback::antennaStateChange(bool connected) {
-    ALOGE("Not implemented: antennaStateChange");
+    ALOGV("antennaStateChange(%d)", connected);
+
+    mCallbackThread.enqueue([this, connected](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onAntennaState, connected);
+    });
+
     return Return<void>();
 }
 
 Return<void> NativeCallback::trafficAnnouncement(bool active) {
-    ALOGE("Not implemented: trafficAnnouncement");
+    ALOGV("trafficAnnouncement(%d)", active);
+
+    mCallbackThread.enqueue([this, active](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onTrafficAnnouncement, active);
+    });
+
     return Return<void>();
 }
 
 Return<void> NativeCallback::emergencyAnnouncement(bool active) {
-    ALOGE("Not implemented: emergencyAnnouncement");
+    ALOGV("emergencyAnnouncement(%d)", active);
+
+    mCallbackThread.enqueue([this, active](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onEmergencyAnnouncement, active);
+    });
+
     return Return<void>();
 }
 
 Return<void> NativeCallback::newMetadata(uint32_t channel, uint32_t subChannel,
         const hidl_vec<MetaData>& metadata) {
-    ALOGE("Not implemented: newMetadata");
+    // channel and subChannel are not used
+    ALOGV("newMetadata(%d, %d)", channel, subChannel);
+
+    mCallbackThread.enqueue([this, metadata](JNIEnv *env) {
+        auto jMetadata = convert::MetadataFromHal(env, metadata);
+        if (jMetadata == nullptr) return;
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onMetadataChanged, jMetadata.get());
+    });
+
     return Return<void>();
 }
 
 Return<void> NativeCallback::backgroundScanAvailable(bool isAvailable) {
-    ALOGE("Not implemented: backgroundScanAvailable");
+    ALOGV("backgroundScanAvailable(%d)", isAvailable);
+
+    mCallbackThread.enqueue([this, isAvailable](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback,
+                gjni.TunerCallback.onBackgroundScanAvailabilityChange, isAvailable);
+    });
+
     return Return<void>();
 }
 
 Return<void> NativeCallback::backgroundScanComplete(ProgramListResult result) {
-    ALOGE("Not implemented: backgroundScanComplete");
+    ALOGV("backgroundScanComplete(%d)", result);
+
+    mCallbackThread.enqueue([this, result](JNIEnv *env) {
+        if (result == ProgramListResult::OK) {
+            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onBackgroundScanComplete);
+        } else {
+            auto cause = (result == ProgramListResult::UNAVAILABLE) ?
+                    TunerError::BACKGROUND_SCAN_UNAVAILABLE : TunerError::BACKGROUND_SCAN_FAILED;
+            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause);
+        }
+    });
+
     return Return<void>();
 }
 
 Return<void> NativeCallback::programListChanged() {
-    ALOGE("Not implemented: programListChanged");
+    ALOGV("programListChanged()");
+
+    mCallbackThread.enqueue([this](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramListChanged);
+    });
+
     return Return<void>();
 }
 
@@ -310,6 +364,20 @@
             "onConfigurationChanged", "(Landroid/hardware/radio/RadioManager$BandConfig;)V");
     gjni.TunerCallback.onProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass,
             "onProgramInfoChanged", "(Landroid/hardware/radio/RadioManager$ProgramInfo;)V");
+    gjni.TunerCallback.onMetadataChanged = GetMethodIDOrDie(env, tunerCbClass,
+            "onMetadataChanged", "(Landroid/hardware/radio/RadioMetadata;)V");
+    gjni.TunerCallback.onTrafficAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
+            "onTrafficAnnouncement", "(Z)V");
+    gjni.TunerCallback.onEmergencyAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
+            "onEmergencyAnnouncement", "(Z)V");
+    gjni.TunerCallback.onAntennaState = GetMethodIDOrDie(env, tunerCbClass,
+            "onAntennaState", "(Z)V");
+    gjni.TunerCallback.onBackgroundScanAvailabilityChange = GetMethodIDOrDie(env, tunerCbClass,
+            "onBackgroundScanAvailabilityChange", "(Z)V");
+    gjni.TunerCallback.onBackgroundScanComplete = GetMethodIDOrDie(env, tunerCbClass,
+            "onBackgroundScanComplete", "()V");
+    gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass,
+            "onProgramListChanged", "()V");
 
     auto res = jniRegisterNativeMethods(env, "com/android/server/radio/TunerCallback",
             gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
diff --git a/services/core/jni/com_android_server_radio_convert.cpp b/services/core/jni/com_android_server_radio_convert.cpp
index 19abf8a..3f24a36 100644
--- a/services/core/jni/com_android_server_radio_convert.cpp
+++ b/services/core/jni/com_android_server_radio_convert.cpp
@@ -254,7 +254,7 @@
     return directionDown ? Direction::DOWN : Direction::UP;
 }
 
-static JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hidl_vec<V1_0::MetaData> metadata) {
+JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hidl_vec<V1_0::MetaData> &metadata) {
     ALOGV("MetadataFromHal()");
     EnvWrapper wrap(env);
 
diff --git a/services/core/jni/com_android_server_radio_convert.h b/services/core/jni/com_android_server_radio_convert.h
index 6f6774b..f2e7f92 100644
--- a/services/core/jni/com_android_server_radio_convert.h
+++ b/services/core/jni/com_android_server_radio_convert.h
@@ -39,6 +39,7 @@
 
 V1_0::Direction DirectionToHal(bool directionDown);
 
+JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hardware::hidl_vec<V1_0::MetaData> &metadata);
 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info);
 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info);