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