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.Tuner.jni"
18 #define LOG_NDEBUG 0
19
20 #include "Tuner.h"
21
22 #include "convert.h"
23 #include "TunerCallback.h"
24
25 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
26 #include <binder/IPCThreadState.h>
27 #include <broadcastradio-utils-1x/Utils.h>
28 #include <core_jni_helpers.h>
29 #include <nativehelper/JNIHelp.h>
30 #include <utils/Log.h>
31
32 namespace android {
33 namespace server {
34 namespace BroadcastRadio {
35 namespace Tuner {
36
37 using std::lock_guard;
38 using std::mutex;
39
40 using hardware::Return;
41 using hardware::hidl_death_recipient;
42 using hardware::hidl_vec;
43
44 namespace V1_0 = hardware::broadcastradio::V1_0;
45 namespace V1_1 = hardware::broadcastradio::V1_1;
46 namespace utils = hardware::broadcastradio::utils;
47
48 using V1_0::Band;
49 using V1_0::BandConfig;
50 using V1_0::MetaData;
51 using V1_0::Result;
52 using V1_1::ITunerCallback;
53 using V1_1::ProgramListResult;
54 using V1_1::VendorKeyValue;
55 using utils::HalRevision;
56
57 static mutex gContextMutex;
58
59 static struct {
60 struct {
61 jclass clazz;
62 jmethodID cstor;
63 jmethodID add;
64 } ArrayList;
65 struct {
66 jfieldID nativeContext;
67 jfieldID region;
68 jfieldID tunerCallback;
69 } Tuner;
70 } gjni;
71
72 class HalDeathRecipient : public hidl_death_recipient {
73 wp<V1_1::ITunerCallback> mTunerCallback;
74
75 public:
HalDeathRecipient(wp<V1_1::ITunerCallback> tunerCallback)76 HalDeathRecipient(wp<V1_1::ITunerCallback> tunerCallback):mTunerCallback(tunerCallback) {}
77
78 virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who);
79 };
80
81 struct TunerContext {
TunerContextandroid::server::BroadcastRadio::Tuner::TunerContext82 TunerContext() {}
83
84 bool mIsClosed = false;
85 HalRevision mHalRev;
86 bool mWithAudio;
87 bool mIsAudioConnected = false;
88 Band mBand;
89 wp<V1_0::IBroadcastRadio> mHalModule;
90 sp<V1_0::ITuner> mHalTuner;
91 sp<V1_1::ITuner> mHalTuner11;
92 sp<HalDeathRecipient> mHalDeathRecipient;
93
94 sp<V1_1::IBroadcastRadio> getHalModule11() const;
95
96 private:
97 DISALLOW_COPY_AND_ASSIGN(TunerContext);
98 };
99
getNativeContext(jlong nativeContextHandle)100 static TunerContext& getNativeContext(jlong nativeContextHandle) {
101 auto nativeContext = reinterpret_cast<TunerContext*>(nativeContextHandle);
102 LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
103 return *nativeContext;
104 }
105
106 /**
107 * Always lock gContextMutex when using native context.
108 */
getNativeContext(JNIEnv * env,JavaRef<jobject> const & jTuner)109 static TunerContext& getNativeContext(JNIEnv *env, JavaRef<jobject> const &jTuner) {
110 return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
111 }
112
nativeInit(JNIEnv * env,jobject obj,jint halRev,bool withAudio,jint band)113 static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio, jint band) {
114 ALOGV("%s", __func__);
115 lock_guard<mutex> lk(gContextMutex);
116
117 auto ctx = new TunerContext();
118 ctx->mHalRev = static_cast<HalRevision>(halRev);
119 ctx->mWithAudio = withAudio;
120 ctx->mBand = static_cast<Band>(band);
121
122 static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
123 return reinterpret_cast<jlong>(ctx);
124 }
125
nativeFinalize(JNIEnv * env,jobject obj,jlong nativeContext)126 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
127 ALOGV("%s", __func__);
128 lock_guard<mutex> lk(gContextMutex);
129
130 auto ctx = reinterpret_cast<TunerContext*>(nativeContext);
131 delete ctx;
132 }
133
serviceDied(uint64_t cookie __unused,const wp<hidl::base::V1_0::IBase> & who __unused)134 void HalDeathRecipient::serviceDied(uint64_t cookie __unused,
135 const wp<hidl::base::V1_0::IBase>& who __unused) {
136 ALOGW("HAL Tuner died unexpectedly");
137
138 auto tunerCallback = mTunerCallback.promote();
139 if (tunerCallback == nullptr) return;
140
141 tunerCallback->hardwareFailure();
142 }
143
getHalModule11() const144 sp<V1_1::IBroadcastRadio> TunerContext::getHalModule11() const {
145 auto halModule = mHalModule.promote();
146 if (halModule == nullptr) {
147 ALOGE("HAL module is gone");
148 return nullptr;
149 }
150
151 return V1_1::IBroadcastRadio::castFrom(halModule).withDefault(nullptr);
152 }
153
assignHalInterfaces(JNIEnv * env,JavaRef<jobject> const & jTuner,sp<V1_0::IBroadcastRadio> halModule,sp<V1_0::ITuner> halTuner)154 void assignHalInterfaces(JNIEnv *env, JavaRef<jobject> const &jTuner,
155 sp<V1_0::IBroadcastRadio> halModule, sp<V1_0::ITuner> halTuner) {
156 ALOGV("%s(%p)", __func__, halTuner.get());
157 ALOGE_IF(halTuner == nullptr, "HAL tuner is a nullptr");
158 lock_guard<mutex> lk(gContextMutex);
159 auto& ctx = getNativeContext(env, jTuner);
160
161 if (ctx.mIsClosed) {
162 ALOGD("Tuner was closed during initialization");
163 // dropping the last reference will close HAL tuner
164 return;
165 }
166 if (ctx.mHalTuner != nullptr) {
167 ALOGE("HAL tuner is already set.");
168 return;
169 }
170
171 ctx.mHalModule = halModule;
172 ctx.mHalTuner = halTuner;
173 ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
174 ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
175 "Provided tuner does not implement 1.1 HAL");
176
177 ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner));
178 halTuner->linkToDeath(ctx.mHalDeathRecipient, 0);
179 }
180
getHalTuner(const TunerContext & ctx)181 static sp<V1_0::ITuner> getHalTuner(const TunerContext& ctx) {
182 auto tuner = ctx.mHalTuner;
183 LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner is not open");
184 return tuner;
185 }
186
getHalTuner(jlong nativeContext)187 static sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
188 lock_guard<mutex> lk(gContextMutex);
189 return getHalTuner(getNativeContext(nativeContext));
190 }
191
getHalTuner11(jlong nativeContext)192 static sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
193 lock_guard<mutex> lk(gContextMutex);
194 return getNativeContext(nativeContext).mHalTuner11;
195 }
196
getNativeCallback(JNIEnv * env,JavaRef<jobject> const & tuner)197 sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
198 return TunerCallback::getNativeCallback(env,
199 env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
200 }
201
getRegion(JNIEnv * env,jobject obj)202 Region getRegion(JNIEnv *env, jobject obj) {
203 return static_cast<Region>(env->GetIntField(obj, gjni.Tuner.region));
204 }
205
nativeClose(JNIEnv * env,jobject obj,jlong nativeContext)206 static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) {
207 lock_guard<mutex> lk(gContextMutex);
208 auto& ctx = getNativeContext(nativeContext);
209
210 if (ctx.mIsClosed) return;
211 ctx.mIsClosed = true;
212
213 if (ctx.mHalTuner == nullptr) {
214 ALOGI("Tuner closed during initialization");
215 return;
216 }
217
218 ALOGI("Closing tuner %p", ctx.mHalTuner.get());
219
220 ctx.mHalTuner->unlinkToDeath(ctx.mHalDeathRecipient);
221 ctx.mHalDeathRecipient = nullptr;
222
223 ctx.mHalTuner11 = nullptr;
224 ctx.mHalTuner = nullptr;
225 }
226
nativeSetConfiguration(JNIEnv * env,jobject obj,jlong nativeContext,jobject config)227 static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) {
228 ALOGV("%s", __func__);
229 lock_guard<mutex> lk(gContextMutex);
230 auto& ctx = getNativeContext(nativeContext);
231
232 auto halTuner = getHalTuner(ctx);
233 if (halTuner == nullptr) return;
234
235 Region region_unused;
236 BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused);
237
238 if (convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal))) return;
239
240 ctx.mBand = bandConfigHal.type;
241 }
242
nativeGetConfiguration(JNIEnv * env,jobject obj,jlong nativeContext,Region region)243 static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext,
244 Region region) {
245 ALOGV("%s", __func__);
246 auto halTuner = getHalTuner(nativeContext);
247 if (halTuner == nullptr) return nullptr;
248
249 BandConfig halConfig;
250 Result halResult;
251 auto hidlResult = halTuner->getConfiguration([&](Result result, const BandConfig& config) {
252 halResult = result;
253 halConfig = config;
254 });
255 if (convert::ThrowIfFailed(env, hidlResult, halResult)) {
256 return nullptr;
257 }
258
259 return convert::BandConfigFromHal(env, halConfig, region).release();
260 }
261
nativeStep(JNIEnv * env,jobject obj,jlong nativeContext,bool directionDown,bool skipSubChannel)262 static void nativeStep(JNIEnv *env, jobject obj, jlong nativeContext,
263 bool directionDown, bool skipSubChannel) {
264 ALOGV("%s", __func__);
265 auto halTuner = getHalTuner(nativeContext);
266 if (halTuner == nullptr) return;
267
268 auto dir = convert::DirectionToHal(directionDown);
269 convert::ThrowIfFailed(env, halTuner->step(dir, skipSubChannel));
270 }
271
nativeScan(JNIEnv * env,jobject obj,jlong nativeContext,bool directionDown,bool skipSubChannel)272 static void nativeScan(JNIEnv *env, jobject obj, jlong nativeContext,
273 bool directionDown, bool skipSubChannel) {
274 ALOGV("%s", __func__);
275 auto halTuner = getHalTuner(nativeContext);
276 if (halTuner == nullptr) return;
277
278 auto dir = convert::DirectionToHal(directionDown);
279 convert::ThrowIfFailed(env, halTuner->scan(dir, skipSubChannel));
280 }
281
nativeTune(JNIEnv * env,jobject obj,jlong nativeContext,jobject jSelector)282 static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) {
283 ALOGV("%s", __func__);
284 lock_guard<mutex> lk(gContextMutex);
285 auto& ctx = getNativeContext(nativeContext);
286
287 auto halTuner10 = getHalTuner(ctx);
288 auto halTuner11 = ctx.mHalTuner11;
289 if (halTuner10 == nullptr) return;
290
291 auto selector = convert::ProgramSelectorToHal(env, jSelector);
292 if (halTuner11 != nullptr) {
293 convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));
294 } else {
295 uint32_t channel, subChannel;
296 if (!utils::getLegacyChannel(selector, &channel, &subChannel)) {
297 jniThrowException(env, "java/lang/IllegalArgumentException",
298 "Can't tune to non-AM/FM channel with HAL<1.1");
299 return;
300 }
301 convert::ThrowIfFailed(env, halTuner10->tune(channel, subChannel));
302 }
303 }
304
nativeCancel(JNIEnv * env,jobject obj,jlong nativeContext)305 static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) {
306 ALOGV("%s", __func__);
307 auto halTuner = getHalTuner(nativeContext);
308 if (halTuner == nullptr) return;
309
310 convert::ThrowIfFailed(env, halTuner->cancel());
311 }
312
nativeCancelAnnouncement(JNIEnv * env,jobject obj,jlong nativeContext)313 static void nativeCancelAnnouncement(JNIEnv *env, jobject obj, jlong nativeContext) {
314 ALOGV("%s", __func__);
315 auto halTuner = getHalTuner11(nativeContext);
316 if (halTuner == nullptr) {
317 ALOGI("cancelling announcements is not supported with HAL < 1.1");
318 return;
319 }
320
321 convert::ThrowIfFailed(env, halTuner->cancelAnnouncement());
322 }
323
nativeStartBackgroundScan(JNIEnv * env,jobject obj,jlong nativeContext)324 static bool nativeStartBackgroundScan(JNIEnv *env, jobject obj, jlong nativeContext) {
325 ALOGV("%s", __func__);
326 auto halTuner = getHalTuner11(nativeContext);
327 if (halTuner == nullptr) {
328 ALOGI("Background scan is not supported with HAL < 1.1");
329 return false;
330 }
331
332 auto halResult = halTuner->startBackgroundScan();
333
334 if (halResult.isOk() && halResult == ProgramListResult::UNAVAILABLE) return false;
335 return !convert::ThrowIfFailed(env, halResult);
336 }
337
nativeGetProgramList(JNIEnv * env,jobject obj,jlong nativeContext,jobject jVendorFilter)338 static jobject nativeGetProgramList(JNIEnv *env, jobject obj, jlong nativeContext, jobject jVendorFilter) {
339 ALOGV("%s", __func__);
340 auto halTuner = getHalTuner11(nativeContext);
341 if (halTuner == nullptr) {
342 ALOGI("Program list is not supported with HAL < 1.1");
343 return nullptr;
344 }
345
346 JavaRef<jobject> jList;
347 ProgramListResult halResult = ProgramListResult::NOT_INITIALIZED;
348 auto filter = convert::VendorInfoToHal(env, jVendorFilter);
349 auto hidlResult = halTuner->getProgramList(filter,
350 [&](ProgramListResult result, const hidl_vec<V1_1::ProgramInfo>& programList) {
351 halResult = result;
352 if (halResult != ProgramListResult::OK) return;
353
354 jList = make_javaref(env, env->NewObject(gjni.ArrayList.clazz, gjni.ArrayList.cstor));
355 for (auto& program : programList) {
356 auto jProgram = convert::ProgramInfoFromHal(env, program);
357 env->CallBooleanMethod(jList.get(), gjni.ArrayList.add, jProgram.get());
358 }
359 });
360
361 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr;
362
363 return jList.release();
364 }
365
nativeGetImage(JNIEnv * env,jobject obj,jlong nativeContext,jint id)366 static jbyteArray nativeGetImage(JNIEnv *env, jobject obj, jlong nativeContext, jint id) {
367 ALOGV("%s(%x)", __func__, id);
368 lock_guard<mutex> lk(gContextMutex);
369 auto& ctx = getNativeContext(nativeContext);
370
371 auto halModule = ctx.getHalModule11();
372 if (halModule == nullptr) {
373 jniThrowException(env, "java/lang/IllegalStateException",
374 "Out-of-band images are not supported with HAL < 1.1");
375 return nullptr;
376 }
377
378 JavaRef<jbyteArray> jRawImage = nullptr;
379
380 auto hidlResult = halModule->getImage(id, [&](hidl_vec<uint8_t> rawImage) {
381 auto len = rawImage.size();
382 if (len == 0) return;
383
384 jRawImage = make_javaref(env, env->NewByteArray(len));
385 if (jRawImage == nullptr) {
386 ALOGE("Failed to allocate byte array of len %zu", len);
387 return;
388 }
389
390 env->SetByteArrayRegion(jRawImage.get(), 0, len,
391 reinterpret_cast<const jbyte*>(rawImage.data()));
392 });
393
394 if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
395
396 return jRawImage.release();
397 }
398
nativeIsAnalogForced(JNIEnv * env,jobject obj,jlong nativeContext)399 static bool nativeIsAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext) {
400 ALOGV("%s", __func__);
401 auto halTuner = getHalTuner11(nativeContext);
402 if (halTuner == nullptr) {
403 jniThrowException(env, "java/lang/IllegalStateException",
404 "Forced analog switch is not supported with HAL < 1.1");
405 return false;
406 }
407
408 bool isForced;
409 Result halResult;
410 auto hidlResult = halTuner->isAnalogForced([&](Result result, bool isForcedRet) {
411 halResult = result;
412 isForced = isForcedRet;
413 });
414
415 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return false;
416
417 return isForced;
418 }
419
nativeSetAnalogForced(JNIEnv * env,jobject obj,jlong nativeContext,bool isForced)420 static void nativeSetAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext, bool isForced) {
421 ALOGV("%s(%d)", __func__, isForced);
422 auto halTuner = getHalTuner11(nativeContext);
423 if (halTuner == nullptr) {
424 jniThrowException(env, "java/lang/IllegalStateException",
425 "Forced analog switch is not supported with HAL < 1.1");
426 return;
427 }
428
429 auto halResult = halTuner->setAnalogForced(isForced);
430 convert::ThrowIfFailed(env, halResult);
431 }
432
433 static const JNINativeMethod gTunerMethods[] = {
434 { "nativeInit", "(IZI)J", (void*)nativeInit },
435 { "nativeFinalize", "(J)V", (void*)nativeFinalize },
436 { "nativeClose", "(J)V", (void*)nativeClose },
437 { "nativeSetConfiguration", "(JLandroid/hardware/radio/RadioManager$BandConfig;)V",
438 (void*)nativeSetConfiguration },
439 { "nativeGetConfiguration", "(JI)Landroid/hardware/radio/RadioManager$BandConfig;",
440 (void*)nativeGetConfiguration },
441 { "nativeStep", "(JZZ)V", (void*)nativeStep },
442 { "nativeScan", "(JZZ)V", (void*)nativeScan },
443 { "nativeTune", "(JLandroid/hardware/radio/ProgramSelector;)V", (void*)nativeTune },
444 { "nativeCancel", "(J)V", (void*)nativeCancel },
445 { "nativeCancelAnnouncement", "(J)V", (void*)nativeCancelAnnouncement },
446 { "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan },
447 { "nativeGetProgramList", "(JLjava/util/Map;)Ljava/util/List;",
448 (void*)nativeGetProgramList },
449 { "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
450 { "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
451 { "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
452 };
453
454 } // namespace Tuner
455 } // namespace BroadcastRadio
456 } // namespace server
457
register_android_server_broadcastradio_Tuner(JavaVM * vm,JNIEnv * env)458 void register_android_server_broadcastradio_Tuner(JavaVM *vm, JNIEnv *env) {
459 using namespace server::BroadcastRadio::Tuner;
460
461 register_android_server_broadcastradio_TunerCallback(vm, env);
462
463 auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
464 gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J");
465 gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I");
466 gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback",
467 "Lcom/android/server/broadcastradio/hal1/TunerCallback;");
468
469 auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
470 gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
471 gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
472 gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
473
474 auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/Tuner",
475 gTunerMethods, NELEM(gTunerMethods));
476 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
477 }
478
479 } // namespace android
480