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.jni"
     18 #define LOG_NDEBUG 0
     19 
     20 #include "BroadcastRadioService.h"
     21 
     22 #include "Tuner.h"
     23 #include "convert.h"
     24 
     25 #include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
     26 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
     27 #include <android/hidl/manager/1.0/IServiceManager.h>
     28 #include <broadcastradio-utils-1x/Utils.h>
     29 #include <core_jni_helpers.h>
     30 #include <hidl/ServiceManagement.h>
     31 #include <nativehelper/JNIHelp.h>
     32 #include <utils/Log.h>
     33 
     34 namespace android {
     35 namespace server {
     36 namespace BroadcastRadio {
     37 namespace BroadcastRadioService {
     38 
     39 using std::lock_guard;
     40 using std::mutex;
     41 
     42 using hardware::Return;
     43 using hardware::hidl_string;
     44 using hardware::hidl_vec;
     45 
     46 namespace V1_0 = hardware::broadcastradio::V1_0;
     47 namespace V1_1 = hardware::broadcastradio::V1_1;
     48 namespace utils = hardware::broadcastradio::utils;
     49 
     50 using V1_0::BandConfig;
     51 using V1_0::Class;
     52 using V1_0::ITuner;
     53 using V1_0::MetaData;
     54 using V1_0::ProgramInfo;
     55 using V1_0::Result;
     56 using utils::HalRevision;
     57 
     58 static mutex gContextMutex;
     59 
     60 static struct {
     61     struct {
     62         jclass clazz;
     63         jmethodID cstor;
     64         jmethodID add;
     65     } ArrayList;
     66     struct {
     67         jclass clazz;
     68         jmethodID cstor;
     69     } Tuner;
     70 } gjni;
     71 
     72 struct Module {
     73     sp<V1_0::IBroadcastRadio> radioModule;
     74     HalRevision halRev;
     75     std::vector<hardware::broadcastradio::V1_0::BandConfig> bands;
     76 };
     77 
     78 struct ServiceContext {
     79     ServiceContext() {}
     80 
     81     std::vector<Module> mModules;
     82 
     83 private:
     84     DISALLOW_COPY_AND_ASSIGN(ServiceContext);
     85 };
     86 
     87 const std::vector<Class> gAllClasses = {
     88     Class::AM_FM,
     89     Class::SAT,
     90     Class::DT,
     91 };
     92 
     93 
     94 /**
     95  * Always lock gContextMutex when using native context.
     96  */
     97 static ServiceContext& getNativeContext(jlong nativeContextHandle) {
     98     auto nativeContext = reinterpret_cast<ServiceContext*>(nativeContextHandle);
     99     LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
    100     return *nativeContext;
    101 }
    102 
    103 static jlong nativeInit(JNIEnv *env, jobject obj) {
    104     ALOGV("%s", __func__);
    105     lock_guard<mutex> lk(gContextMutex);
    106 
    107     auto nativeContext = new ServiceContext();
    108     static_assert(sizeof(jlong) >= sizeof(nativeContext), "jlong is smaller than a pointer");
    109     return reinterpret_cast<jlong>(nativeContext);
    110 }
    111 
    112 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
    113     ALOGV("%s", __func__);
    114     lock_guard<mutex> lk(gContextMutex);
    115 
    116     auto ctx = reinterpret_cast<ServiceContext*>(nativeContext);
    117     delete ctx;
    118 }
    119 
    120 static jobject nativeLoadModules(JNIEnv *env, jobject obj, jlong nativeContext) {
    121     ALOGV("%s", __func__);
    122     lock_guard<mutex> lk(gContextMutex);
    123     auto& ctx = getNativeContext(nativeContext);
    124 
    125     // Get list of registered HIDL HAL implementations.
    126     auto manager = hardware::defaultServiceManager();
    127     hidl_vec<hidl_string> services;
    128     if (manager == nullptr) {
    129         ALOGE("Can't reach service manager, using default service implementation only");
    130         services = std::vector<hidl_string>({ "default" });
    131     } else {
    132         manager->listByInterface(V1_0::IBroadcastRadioFactory::descriptor,
    133                 [&services](const hidl_vec<hidl_string> &registered) {
    134             services = registered;
    135         });
    136     }
    137 
    138     // Scan provided list for actually implemented modules.
    139     ctx.mModules.clear();
    140     auto jModules = make_javaref(env, env->NewObject(gjni.ArrayList.clazz, gjni.ArrayList.cstor));
    141     for (auto&& serviceName : services) {
    142         ALOGV("checking service: %s", serviceName.c_str());
    143 
    144         auto factory = V1_0::IBroadcastRadioFactory::getService(serviceName);
    145         if (factory == nullptr) {
    146             ALOGE("can't load service %s", serviceName.c_str());
    147             continue;
    148         }
    149 
    150         auto halRev = HalRevision::V1_0;
    151         auto halMinor = 0;
    152         if (V1_1::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr) != nullptr) {
    153             halRev = HalRevision::V1_1;
    154             halMinor = 1;
    155         }
    156 
    157         // Second level of scanning - that's unfortunate.
    158         for (auto&& clazz : gAllClasses) {
    159             sp<V1_0::IBroadcastRadio> module10 = nullptr;
    160             sp<V1_1::IBroadcastRadio> module11 = nullptr;
    161             factory->connectModule(clazz, [&](Result res, const sp<V1_0::IBroadcastRadio>& module) {
    162                 if (res == Result::OK) {
    163                     module10 = module;
    164                     module11 = V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr);
    165                 } else if (res != Result::INVALID_ARGUMENTS) {
    166                     ALOGE("couldn't load %s:%s module",
    167                             serviceName.c_str(), V1_0::toString(clazz).c_str());
    168                 }
    169             });
    170             if (module10 == nullptr) continue;
    171 
    172             auto idx = ctx.mModules.size();
    173             ctx.mModules.push_back({module10, halRev, {}});
    174             auto& nModule = ctx.mModules[idx];
    175             ALOGI("loaded broadcast radio module %zu: %s:%s (HAL 1.%d)",
    176                     idx, serviceName.c_str(), V1_0::toString(clazz).c_str(), halMinor);
    177 
    178             JavaRef<jobject> jModule = nullptr;
    179             Result halResult = Result::OK;
    180             Return<void> hidlResult;
    181             if (module11 != nullptr) {
    182                 hidlResult = module11->getProperties_1_1([&](const V1_1::Properties& properties) {
    183                     nModule.bands = properties.base.bands;
    184                     jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
    185                 });
    186             } else {
    187                 hidlResult = module10->getProperties([&](Result result,
    188                         const V1_0::Properties& properties) {
    189                     halResult = result;
    190                     if (result != Result::OK) return;
    191                     nModule.bands = properties.bands;
    192                     jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
    193                 });
    194             }
    195             if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr;
    196 
    197             env->CallBooleanMethod(jModules.get(), gjni.ArrayList.add, jModule.get());
    198         }
    199     }
    200 
    201     return jModules.release();
    202 }
    203 
    204 static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,
    205         jobject bandConfig, bool withAudio, jobject callback) {
    206     ALOGV("%s", __func__);
    207     lock_guard<mutex> lk(gContextMutex);
    208     auto& ctx = getNativeContext(nativeContext);
    209 
    210     if (callback == nullptr) {
    211         ALOGE("Callback is empty");
    212         return nullptr;
    213     }
    214 
    215     if (moduleId < 0 || static_cast<size_t>(moduleId) >= ctx.mModules.size()) {
    216         ALOGE("Invalid module ID: %d", moduleId);
    217         return nullptr;
    218     }
    219 
    220     ALOGI("Opening tuner %d", moduleId);
    221     auto module = ctx.mModules[moduleId];
    222 
    223     Region region;
    224     BandConfig bandConfigHal;
    225     if (bandConfig != nullptr) {
    226         bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
    227     } else {
    228         region = Region::INVALID;
    229         if (module.bands.size() == 0) {
    230             ALOGE("No bands defined");
    231             return nullptr;
    232         }
    233         bandConfigHal = module.bands[0];
    234 
    235         /* Prefer FM to workaround possible program list fetching limitation
    236          * (if tuner scans only configured band for programs). */
    237         auto fmIt = std::find_if(module.bands.begin(), module.bands.end(),
    238             [](const BandConfig & band) { return utils::isFm(band.type); });
    239         if (fmIt != module.bands.end()) bandConfigHal = *fmIt;
    240 
    241         if (bandConfigHal.spacings.size() > 1) {
    242             bandConfigHal.spacings = hidl_vec<uint32_t>({ *std::min_element(
    243                     bandConfigHal.spacings.begin(), bandConfigHal.spacings.end()) });
    244         }
    245     }
    246 
    247     auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
    248             callback, module.halRev, region, withAudio, bandConfigHal.type));
    249     if (tuner == nullptr) {
    250         ALOGE("Unable to create new tuner object.");
    251         return nullptr;
    252     }
    253 
    254     auto tunerCb = Tuner::getNativeCallback(env, tuner);
    255     Result halResult;
    256     sp<ITuner> halTuner = nullptr;
    257 
    258     auto hidlResult = module.radioModule->openTuner(bandConfigHal, withAudio, tunerCb,
    259             [&](Result result, const sp<ITuner>& tuner) {
    260                 halResult = result;
    261                 halTuner = tuner;
    262             });
    263     if (!hidlResult.isOk() || halResult != Result::OK || halTuner == nullptr) {
    264         ALOGE("Couldn't open tuner");
    265         ALOGE_IF(hidlResult.isOk(), "halResult = %d", halResult);
    266         ALOGE_IF(!hidlResult.isOk(), "hidlResult = %s", hidlResult.description().c_str());
    267         return nullptr;
    268     }
    269 
    270     Tuner::assignHalInterfaces(env, tuner, module.radioModule, halTuner);
    271     ALOGD("Opened tuner %p", halTuner.get());
    272 
    273     bool isConnected = true;
    274     halTuner->getConfiguration([&](Result result, const BandConfig& config) {
    275         if (result == Result::OK) isConnected = config.antennaConnected;
    276     });
    277     if (!isConnected) {
    278         tunerCb->antennaStateChange(false);
    279     }
    280 
    281     return tuner.release();
    282 }
    283 
    284 static const JNINativeMethod gRadioServiceMethods[] = {
    285     { "nativeInit", "()J", (void*)nativeInit },
    286     { "nativeFinalize", "(J)V", (void*)nativeFinalize },
    287     { "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules },
    288     { "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z"
    289             "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/hal1/Tuner;",
    290             (void*)nativeOpenTuner },
    291 };
    292 
    293 } // namespace BroadcastRadioService
    294 } // namespace BroadcastRadio
    295 } // namespace server
    296 
    297 void register_android_server_broadcastradio_BroadcastRadioService(JNIEnv *env) {
    298     using namespace server::BroadcastRadio::BroadcastRadioService;
    299 
    300     register_android_server_broadcastradio_convert(env);
    301 
    302     auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
    303     gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
    304     gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
    305             "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
    306 
    307     auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
    308     gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
    309     gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
    310     gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
    311 
    312     auto res = jniRegisterNativeMethods(env,
    313             "com/android/server/broadcastradio/hal1/BroadcastRadioService",
    314             gRadioServiceMethods, NELEM(gRadioServiceMethods));
    315     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
    316 }
    317 
    318 } // namespace android
    319