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