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