1 /**
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "BroadcastRadioService.TunerCallback.jni"
18 #define LOG_NDEBUG 0
19 
20 #include "TunerCallback.h"
21 
22 #include "Tuner.h"
23 #include "convert.h"
24 
25 #include <broadcastradio-utils-1x/Utils.h>
26 #include <core_jni_helpers.h>
27 #include <nativehelper/JNIHelp.h>
28 #include <utils/Log.h>
29 
30 namespace android {
31 namespace server {
32 namespace BroadcastRadio {
33 namespace TunerCallback {
34 
35 using std::lock_guard;
36 using std::mutex;
37 
38 using hardware::Return;
39 using hardware::hidl_vec;
40 
41 namespace V1_0 = hardware::broadcastradio::V1_0;
42 namespace V1_1 = hardware::broadcastradio::V1_1;
43 namespace utils = hardware::broadcastradio::utils;
44 
45 using V1_0::Band;
46 using V1_0::BandConfig;
47 using V1_0::MetaData;
48 using V1_0::Result;
49 using V1_1::ITunerCallback;
50 using V1_1::ProgramInfo;
51 using V1_1::ProgramListResult;
52 using V1_1::ProgramSelector;
53 using V1_1::VendorKeyValue;
54 using utils::HalRevision;
55 
56 static JavaVM *gvm = nullptr;
57 
58 static struct {
59     struct {
60         jclass clazz;
61         jfieldID nativeContext;
62         jmethodID handleHwFailure;
63         jmethodID onError;
64         jmethodID onConfigurationChanged;
65         jmethodID onCurrentProgramInfoChanged;
66         jmethodID onTrafficAnnouncement;
67         jmethodID onEmergencyAnnouncement;
68         jmethodID onAntennaState;
69         jmethodID onBackgroundScanAvailabilityChange;
70         jmethodID onBackgroundScanComplete;
71         jmethodID onProgramListChanged;
72     } TunerCallback;
73 } gjni;
74 
75 // from frameworks/base/core/java/android/hardware/radio/RadioTuner.java
76 enum class TunerError : jint {
77     HARDWARE_FAILURE = 0,
78     SERVER_DIED = 1,
79     CANCELLED = 2,
80     SCAN_TIMEOUT = 3,
81     CONFIG = 4,
82     BACKGROUND_SCAN_UNAVAILABLE = 5,
83     BACKGROUND_SCAN_FAILED = 6,
84 };
85 
86 static mutex gContextMutex;
87 
88 class NativeCallback : public ITunerCallback {
89     mutex mMut;
90 
91     jobject mJTuner;
92     jobject mJCallback;
93     NativeCallbackThread mCallbackThread;
94     HalRevision mHalRev;
95 
96     Band mBand;
97 
98     // Carries current program info data for 1.0 newMetadata callback.
99     V1_0::ProgramInfo mCurrentProgramInfo;
100 
101     DISALLOW_COPY_AND_ASSIGN(NativeCallback);
102 
103 public:
104     NativeCallback(JNIEnv *env, jobject jTuner, jobject jCallback, HalRevision halRev);
105     virtual ~NativeCallback();
106 
107     void detach();
108 
109     virtual Return<void> hardwareFailure();
110     virtual Return<void> configChange(Result result, const BandConfig& config);
111     virtual Return<void> tuneComplete(Result result, const V1_0::ProgramInfo& info);
112     virtual Return<void> afSwitch(const V1_0::ProgramInfo& info);
113     virtual Return<void> antennaStateChange(bool connected);
114     virtual Return<void> trafficAnnouncement(bool active);
115     virtual Return<void> emergencyAnnouncement(bool active);
116     virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel,
117             const hidl_vec<MetaData>& metadata);
118     virtual Return<void> tuneComplete_1_1(Result result, const ProgramSelector& selector);
119     virtual Return<void> backgroundScanAvailable(bool isAvailable);
120     virtual Return<void> backgroundScanComplete(ProgramListResult result);
121     virtual Return<void> programListChanged();
122     virtual Return<void> currentProgramInfoChanged(const ProgramInfo& info);
123 };
124 
125 struct TunerCallbackContext {
TunerCallbackContextandroid::server::BroadcastRadio::TunerCallback::TunerCallbackContext126     TunerCallbackContext() {}
127 
128     sp<NativeCallback> mNativeCallback;
129 
130 private:
131     DISALLOW_COPY_AND_ASSIGN(TunerCallbackContext);
132 };
133 
NativeCallback(JNIEnv * env,jobject jTuner,jobject jCallback,HalRevision halRev)134 NativeCallback::NativeCallback(JNIEnv *env, jobject jTuner, jobject jCallback, HalRevision halRev)
135         : mCallbackThread(gvm), mHalRev(halRev) {
136     ALOGV("%s", __func__);
137     mJTuner = env->NewGlobalRef(jTuner);
138     mJCallback = env->NewGlobalRef(jCallback);
139 }
140 
~NativeCallback()141 NativeCallback::~NativeCallback() {
142     ALOGV("%s", __func__);
143 
144     // stop callback thread before dereferencing client callback
145     mCallbackThread.stop();
146 
147     JNIEnv *env = nullptr;
148     gvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4);
149     if (env != nullptr) {
150         env->DeleteGlobalRef(mJTuner);
151         env->DeleteGlobalRef(mJCallback);
152     }
153 }
154 
detach()155 void NativeCallback::detach() {
156     // stop callback thread to ignore further calls
157     mCallbackThread.stop();
158 }
159 
hardwareFailure()160 Return<void> NativeCallback::hardwareFailure() {
161     mCallbackThread.enqueue([this](JNIEnv *env) {
162         env->CallVoidMethod(mJCallback, gjni.TunerCallback.handleHwFailure);
163     });
164 
165     return Return<void>();
166 }
167 
configChange(Result result,const BandConfig & config)168 Return<void> NativeCallback::configChange(Result result, const BandConfig& config) {
169     ALOGV("%s(%d)", __func__, result);
170 
171     mCallbackThread.enqueue([result, config, this](JNIEnv *env) {
172         if (result == Result::OK) {
173             auto region = Tuner::getRegion(env, mJTuner);
174             auto jConfig = convert::BandConfigFromHal(env, config, region);
175             if (jConfig == nullptr) return;
176             env->CallVoidMethod(mJCallback, gjni.TunerCallback.onConfigurationChanged,
177                     jConfig.get());
178         } else {
179             env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, TunerError::CONFIG);
180         }
181     });
182 
183     return Return<void>();
184 }
185 
tuneComplete(Result result,const V1_0::ProgramInfo & info)186 Return<void> NativeCallback::tuneComplete(Result result, const V1_0::ProgramInfo& info) {
187     ALOGV("%s(%d)", __func__, result);
188 
189     if (mHalRev > HalRevision::V1_0) {
190         ALOGW("1.0 callback was ignored");
191         return {};
192     }
193 
194     if (result == Result::OK) {
195         {
196             lock_guard<mutex> lk(mMut);
197             mCurrentProgramInfo = info;
198         }
199 
200         // tuneComplete_1_1 implementation does not handle success case, see the implementation
201         mCallbackThread.enqueue([this, info](JNIEnv *env) {
202             auto jInfo = convert::ProgramInfoFromHal(env, info, mBand);
203             env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged,
204                     jInfo.get());
205         });
206         return {};
207     }
208 
209     auto selector = utils::make_selector(mBand, info.channel, info.subChannel);
210     return tuneComplete_1_1(result, selector);
211 }
212 
tuneComplete_1_1(Result result,const ProgramSelector & selector)213 Return<void> NativeCallback::tuneComplete_1_1(Result result, const ProgramSelector& selector) {
214     ALOGV("%s(%d)", __func__, result);
215 
216     mCallbackThread.enqueue([result, this](JNIEnv *env) {
217         /* for HAL 1.1, onCurrentProgramInfoChanged will be called from currentProgramInfoChanged,
218          * so we don't need to handle success case here.
219          */
220         if (result == Result::OK) return;
221 
222         TunerError cause = TunerError::CANCELLED;
223         if (result == Result::TIMEOUT) cause = TunerError::SCAN_TIMEOUT;
224         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause);
225     });
226 
227     return Return<void>();
228 }
229 
afSwitch(const V1_0::ProgramInfo & info)230 Return<void> NativeCallback::afSwitch(const V1_0::ProgramInfo& info) {
231     ALOGV("%s", __func__);
232     return tuneComplete(Result::OK, info);
233 }
234 
antennaStateChange(bool connected)235 Return<void> NativeCallback::antennaStateChange(bool connected) {
236     ALOGV("%s(%d)", __func__, connected);
237 
238     mCallbackThread.enqueue([this, connected](JNIEnv *env) {
239         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onAntennaState, connected);
240     });
241 
242     return Return<void>();
243 }
244 
trafficAnnouncement(bool active)245 Return<void> NativeCallback::trafficAnnouncement(bool active) {
246     ALOGV("%s(%d)", __func__, active);
247 
248     mCallbackThread.enqueue([this, active](JNIEnv *env) {
249         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onTrafficAnnouncement, active);
250     });
251 
252     return Return<void>();
253 }
254 
emergencyAnnouncement(bool active)255 Return<void> NativeCallback::emergencyAnnouncement(bool active) {
256     ALOGV("%s(%d)", __func__, active);
257 
258     mCallbackThread.enqueue([this, active](JNIEnv *env) {
259         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onEmergencyAnnouncement, active);
260     });
261 
262     return Return<void>();
263 }
264 
newMetadata(uint32_t channel,uint32_t subChannel,const hidl_vec<MetaData> & metadata)265 Return<void> NativeCallback::newMetadata(uint32_t channel, uint32_t subChannel,
266         const hidl_vec<MetaData>& metadata) {
267     ALOGV("%s(%d, %d)", __func__, channel, subChannel);
268 
269     if (mHalRev > HalRevision::V1_0) {
270         ALOGW("1.0 callback was ignored");
271         return {};
272     }
273 
274     V1_0::ProgramInfo info;
275     {
276         lock_guard<mutex> lk(mMut);
277         info = mCurrentProgramInfo;
278     }
279     if (channel != info.channel || subChannel != info.subChannel) {
280         ALOGE("Channel mismatch on newMetadata callback (%d.%d != %d.%d)",
281                 channel, subChannel, info.channel, info.subChannel);
282         return {};
283     }
284     info.metadata = metadata;
285 
286     mCallbackThread.enqueue([this, info](JNIEnv *env) {
287         auto jInfo = convert::ProgramInfoFromHal(env, info, mBand);
288         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged,
289                 jInfo.get());
290     });
291 
292     return {};
293 }
294 
backgroundScanAvailable(bool isAvailable)295 Return<void> NativeCallback::backgroundScanAvailable(bool isAvailable) {
296     ALOGV("%s(%d)", __func__, isAvailable);
297 
298     mCallbackThread.enqueue([this, isAvailable](JNIEnv *env) {
299         env->CallVoidMethod(mJCallback,
300                 gjni.TunerCallback.onBackgroundScanAvailabilityChange, isAvailable);
301     });
302 
303     return Return<void>();
304 }
305 
backgroundScanComplete(ProgramListResult result)306 Return<void> NativeCallback::backgroundScanComplete(ProgramListResult result) {
307     ALOGV("%s(%d)", __func__, result);
308 
309     mCallbackThread.enqueue([this, result](JNIEnv *env) {
310         if (result == ProgramListResult::OK) {
311             env->CallVoidMethod(mJCallback, gjni.TunerCallback.onBackgroundScanComplete);
312         } else {
313             auto cause = (result == ProgramListResult::UNAVAILABLE) ?
314                     TunerError::BACKGROUND_SCAN_UNAVAILABLE : TunerError::BACKGROUND_SCAN_FAILED;
315             env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause);
316         }
317     });
318 
319     return Return<void>();
320 }
321 
programListChanged()322 Return<void> NativeCallback::programListChanged() {
323     ALOGV("%s", __func__);
324 
325     mCallbackThread.enqueue([this](JNIEnv *env) {
326         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramListChanged);
327     });
328 
329     return Return<void>();
330 }
331 
currentProgramInfoChanged(const ProgramInfo & info)332 Return<void> NativeCallback::currentProgramInfoChanged(const ProgramInfo& info) {
333     ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
334 
335     mCallbackThread.enqueue([this, info](JNIEnv *env) {
336         auto jInfo = convert::ProgramInfoFromHal(env, info);
337         env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged,
338                 jInfo.get());
339     });
340 
341     return Return<void>();
342 }
343 
getNativeContext(jlong nativeContextHandle)344 static TunerCallbackContext& getNativeContext(jlong nativeContextHandle) {
345     auto nativeContext = reinterpret_cast<TunerCallbackContext*>(nativeContextHandle);
346     LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
347     return *nativeContext;
348 }
349 
350 /**
351  * Always lock gContextMutex when using native context.
352  */
getNativeContext(JNIEnv * env,jobject jTunerCb)353 static TunerCallbackContext& getNativeContext(JNIEnv *env, jobject jTunerCb) {
354     return getNativeContext(env->GetLongField(jTunerCb, gjni.TunerCallback.nativeContext));
355 }
356 
nativeInit(JNIEnv * env,jobject obj,jobject jTuner,jint jHalRev)357 static jlong nativeInit(JNIEnv *env, jobject obj, jobject jTuner, jint jHalRev) {
358     ALOGV("%s", __func__);
359     lock_guard<mutex> lk(gContextMutex);
360 
361     auto halRev = static_cast<HalRevision>(jHalRev);
362 
363     auto ctx = new TunerCallbackContext();
364     ctx->mNativeCallback = new NativeCallback(env, jTuner, obj, halRev);
365 
366     static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
367     return reinterpret_cast<jlong>(ctx);
368 }
369 
nativeFinalize(JNIEnv * env,jobject obj,jlong nativeContext)370 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
371     ALOGV("%s", __func__);
372     lock_guard<mutex> lk(gContextMutex);
373 
374     auto ctx = reinterpret_cast<TunerCallbackContext*>(nativeContext);
375     delete ctx;
376 }
377 
nativeDetach(JNIEnv * env,jobject obj,jlong nativeContext)378 static void nativeDetach(JNIEnv *env, jobject obj, jlong nativeContext) {
379     ALOGV("%s", __func__);
380     lock_guard<mutex> lk(gContextMutex);
381     auto& ctx = getNativeContext(nativeContext);
382 
383     if (ctx.mNativeCallback == nullptr) return;
384     ctx.mNativeCallback->detach();
385     ctx.mNativeCallback = nullptr;
386 }
387 
getNativeCallback(JNIEnv * env,jobject jTunerCallback)388 sp<ITunerCallback> getNativeCallback(JNIEnv *env, jobject jTunerCallback) {
389     lock_guard<mutex> lk(gContextMutex);
390     auto& ctx = getNativeContext(env, jTunerCallback);
391     return ctx.mNativeCallback;
392 }
393 
394 static const JNINativeMethod gTunerCallbackMethods[] = {
395     { "nativeInit", "(Lcom/android/server/broadcastradio/hal1/Tuner;I)J", (void*)nativeInit },
396     { "nativeFinalize", "(J)V", (void*)nativeFinalize },
397     { "nativeDetach", "(J)V", (void*)nativeDetach },
398 };
399 
400 } // namespace TunerCallback
401 } // namespace BroadcastRadio
402 } // namespace server
403 
register_android_server_broadcastradio_TunerCallback(JavaVM * vm,JNIEnv * env)404 void register_android_server_broadcastradio_TunerCallback(JavaVM *vm, JNIEnv *env) {
405     using namespace server::BroadcastRadio::TunerCallback;
406 
407     gvm = vm;
408 
409     auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/TunerCallback");
410     gjni.TunerCallback.clazz = MakeGlobalRefOrDie(env, tunerCbClass);
411     gjni.TunerCallback.nativeContext = GetFieldIDOrDie(env, tunerCbClass, "mNativeContext", "J");
412     gjni.TunerCallback.handleHwFailure = GetMethodIDOrDie(env, tunerCbClass, "handleHwFailure", "()V");
413     gjni.TunerCallback.onError = GetMethodIDOrDie(env, tunerCbClass, "onError", "(I)V");
414     gjni.TunerCallback.onConfigurationChanged = GetMethodIDOrDie(env, tunerCbClass,
415             "onConfigurationChanged", "(Landroid/hardware/radio/RadioManager$BandConfig;)V");
416     gjni.TunerCallback.onCurrentProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass,
417             "onCurrentProgramInfoChanged", "(Landroid/hardware/radio/RadioManager$ProgramInfo;)V");
418     gjni.TunerCallback.onTrafficAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
419             "onTrafficAnnouncement", "(Z)V");
420     gjni.TunerCallback.onEmergencyAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
421             "onEmergencyAnnouncement", "(Z)V");
422     gjni.TunerCallback.onAntennaState = GetMethodIDOrDie(env, tunerCbClass,
423             "onAntennaState", "(Z)V");
424     gjni.TunerCallback.onBackgroundScanAvailabilityChange = GetMethodIDOrDie(env, tunerCbClass,
425             "onBackgroundScanAvailabilityChange", "(Z)V");
426     gjni.TunerCallback.onBackgroundScanComplete = GetMethodIDOrDie(env, tunerCbClass,
427             "onBackgroundScanComplete", "()V");
428     gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass,
429             "onProgramListChanged", "()V");
430 
431     auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/TunerCallback",
432             gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
433     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
434 }
435 
436 } // namespace android
437