Home | History | Annotate | Download | only in BroadcastRadio
      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:
     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 {
     82     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 
     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  */
    108 static TunerContext& getNativeContext(JNIEnv *env, JavaRef<jobject> const &jTuner) {
    109     return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
    110 }
    111 
    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 
    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 
    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
    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 
    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 
    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 
    194 sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
    195     lock_guard<mutex> lk(gContextMutex);
    196     return getHalTuner(getNativeContext(nativeContext));
    197 }
    198 
    199 sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
    200     lock_guard<mutex> lk(gContextMutex);
    201     return getNativeContext(nativeContext).mHalTuner11;
    202 }
    203 
    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 
    209 Region getRegion(JNIEnv *env, jobject obj) {
    210     return static_cast<Region>(env->GetIntField(obj, gjni.Tuner.region));
    211 }
    212 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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