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-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:
     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     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 
    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  */
    109 static TunerContext& getNativeContext(JNIEnv *env, JavaRef<jobject> const &jTuner) {
    110     return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
    111 }
    112 
    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 
    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 
    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 
    144 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 
    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 
    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 
    187 static sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
    188     lock_guard<mutex> lk(gContextMutex);
    189     return getHalTuner(getNativeContext(nativeContext));
    190 }
    191 
    192 static sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
    193     lock_guard<mutex> lk(gContextMutex);
    194     return getNativeContext(nativeContext).mHalTuner11;
    195 }
    196 
    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 
    202 Region getRegion(JNIEnv *env, jobject obj) {
    203     return static_cast<Region>(env->GetIntField(obj, gjni.Tuner.region));
    204 }
    205 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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