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