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