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> ®istered) { 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