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