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/Utils.h> 28 #include <core_jni_helpers.h> 29 #include <media/AudioSystem.h> 30 #include <nativehelper/JNIHelp.h> 31 #include <utils/Log.h> 32 33 namespace android { 34 namespace server { 35 namespace BroadcastRadio { 36 namespace Tuner { 37 38 using std::lock_guard; 39 using std::mutex; 40 41 using hardware::Return; 42 using hardware::hidl_death_recipient; 43 using hardware::hidl_vec; 44 45 namespace V1_0 = hardware::broadcastradio::V1_0; 46 namespace V1_1 = hardware::broadcastradio::V1_1; 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 55 static mutex gContextMutex; 56 57 static struct { 58 struct { 59 jclass clazz; 60 jmethodID cstor; 61 jmethodID add; 62 } ArrayList; 63 struct { 64 jfieldID nativeContext; 65 jfieldID region; 66 jfieldID tunerCallback; 67 } Tuner; 68 } gjni; 69 70 static const char* const kAudioDeviceName = "Radio tuner source"; 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 wp<V1_1::IBroadcastRadio> mHalModule11; 91 sp<V1_0::ITuner> mHalTuner; 92 sp<V1_1::ITuner> mHalTuner11; 93 sp<HalDeathRecipient> mHalDeathRecipient; 94 95 private: 96 DISALLOW_COPY_AND_ASSIGN(TunerContext); 97 }; 98 99 static TunerContext& getNativeContext(jlong nativeContextHandle) { 100 auto nativeContext = reinterpret_cast<TunerContext*>(nativeContextHandle); 101 LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized"); 102 return *nativeContext; 103 } 104 105 /** 106 * Always lock gContextMutex when using native context. 107 */ 108 static TunerContext& getNativeContext(JNIEnv *env, JavaRef<jobject> const &jTuner) { 109 return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext)); 110 } 111 112 static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio, jint band) { 113 ALOGV("%s", __func__); 114 lock_guard<mutex> lk(gContextMutex); 115 116 auto ctx = new TunerContext(); 117 ctx->mHalRev = static_cast<HalRevision>(halRev); 118 ctx->mWithAudio = withAudio; 119 ctx->mBand = static_cast<Band>(band); 120 121 static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer"); 122 return reinterpret_cast<jlong>(ctx); 123 } 124 125 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) { 126 ALOGV("%s", __func__); 127 lock_guard<mutex> lk(gContextMutex); 128 129 auto ctx = reinterpret_cast<TunerContext*>(nativeContext); 130 delete ctx; 131 } 132 133 void HalDeathRecipient::serviceDied(uint64_t cookie __unused, 134 const wp<hidl::base::V1_0::IBase>& who __unused) { 135 ALOGW("HAL Tuner died unexpectedly"); 136 137 auto tunerCallback = mTunerCallback.promote(); 138 if (tunerCallback == nullptr) return; 139 140 tunerCallback->hardwareFailure(); 141 } 142 143 // TODO(b/62713378): implement support for multiple tuners open at the same time 144 static void notifyAudioService(TunerContext& ctx, bool connected) { 145 if (!ctx.mWithAudio) return; 146 if (ctx.mIsAudioConnected == connected) return; 147 ctx.mIsAudioConnected = connected; 148 149 ALOGD("Notifying AudioService about new state: %d", connected); 150 auto token = IPCThreadState::self()->clearCallingIdentity(); 151 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER, 152 connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, 153 nullptr, kAudioDeviceName); 154 IPCThreadState::self()->restoreCallingIdentity(token); 155 } 156 157 void assignHalInterfaces(JNIEnv *env, JavaRef<jobject> const &jTuner, 158 sp<V1_0::IBroadcastRadio> halModule, sp<V1_0::ITuner> halTuner) { 159 ALOGV("%s(%p)", __func__, halTuner.get()); 160 ALOGE_IF(halTuner == nullptr, "HAL tuner is a nullptr"); 161 lock_guard<mutex> lk(gContextMutex); 162 auto& ctx = getNativeContext(env, jTuner); 163 164 if (ctx.mIsClosed) { 165 ALOGD("Tuner was closed during initialization"); 166 // dropping the last reference will close HAL tuner 167 return; 168 } 169 if (ctx.mHalTuner != nullptr) { 170 ALOGE("HAL tuner is already set."); 171 return; 172 } 173 174 ctx.mHalModule = halModule; 175 ctx.mHalModule11 = V1_1::IBroadcastRadio::castFrom(halModule).withDefault(nullptr); 176 177 ctx.mHalTuner = halTuner; 178 ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr); 179 ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr, 180 "Provided tuner does not implement 1.1 HAL"); 181 182 ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner)); 183 halTuner->linkToDeath(ctx.mHalDeathRecipient, 0); 184 185 notifyAudioService(ctx, true); 186 } 187 188 static sp<V1_0::ITuner> getHalTuner(const TunerContext& ctx) { 189 auto tuner = ctx.mHalTuner; 190 LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner is not open"); 191 return tuner; 192 } 193 194 sp<V1_0::ITuner> getHalTuner(jlong nativeContext) { 195 lock_guard<mutex> lk(gContextMutex); 196 return getHalTuner(getNativeContext(nativeContext)); 197 } 198 199 sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) { 200 lock_guard<mutex> lk(gContextMutex); 201 return getNativeContext(nativeContext).mHalTuner11; 202 } 203 204 sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) { 205 return TunerCallback::getNativeCallback(env, 206 env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback)); 207 } 208 209 Region getRegion(JNIEnv *env, jobject obj) { 210 return static_cast<Region>(env->GetIntField(obj, gjni.Tuner.region)); 211 } 212 213 static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) { 214 lock_guard<mutex> lk(gContextMutex); 215 auto& ctx = getNativeContext(nativeContext); 216 217 if (ctx.mIsClosed) return; 218 ctx.mIsClosed = true; 219 220 if (ctx.mHalTuner == nullptr) { 221 ALOGI("Tuner closed during initialization"); 222 return; 223 } 224 225 ALOGI("Closing tuner %p", ctx.mHalTuner.get()); 226 227 notifyAudioService(ctx, false); 228 229 ctx.mHalTuner->unlinkToDeath(ctx.mHalDeathRecipient); 230 ctx.mHalDeathRecipient = nullptr; 231 232 ctx.mHalTuner11 = nullptr; 233 ctx.mHalTuner = nullptr; 234 } 235 236 static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) { 237 ALOGV("%s", __func__); 238 lock_guard<mutex> lk(gContextMutex); 239 auto& ctx = getNativeContext(nativeContext); 240 241 auto halTuner = getHalTuner(ctx); 242 if (halTuner == nullptr) return; 243 244 Region region_unused; 245 BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused); 246 247 if (convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal))) return; 248 249 ctx.mBand = bandConfigHal.type; 250 } 251 252 static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, 253 Region region) { 254 ALOGV("%s", __func__); 255 auto halTuner = getHalTuner(nativeContext); 256 if (halTuner == nullptr) return nullptr; 257 258 BandConfig halConfig; 259 Result halResult; 260 auto hidlResult = halTuner->getConfiguration([&](Result result, const BandConfig& config) { 261 halResult = result; 262 halConfig = config; 263 }); 264 if (convert::ThrowIfFailed(env, hidlResult, halResult)) { 265 return nullptr; 266 } 267 268 return convert::BandConfigFromHal(env, halConfig, region).release(); 269 } 270 271 static void nativeSetMuted(JNIEnv *env, jobject obj, jlong nativeContext, bool mute) { 272 ALOGV("%s(%d)", __func__, mute); 273 lock_guard<mutex> lk(gContextMutex); 274 auto& ctx = getNativeContext(nativeContext); 275 276 notifyAudioService(ctx, !mute); 277 } 278 279 static void nativeStep(JNIEnv *env, jobject obj, jlong nativeContext, 280 bool directionDown, bool skipSubChannel) { 281 ALOGV("%s", __func__); 282 auto halTuner = getHalTuner(nativeContext); 283 if (halTuner == nullptr) return; 284 285 auto dir = convert::DirectionToHal(directionDown); 286 convert::ThrowIfFailed(env, halTuner->step(dir, skipSubChannel)); 287 } 288 289 static void nativeScan(JNIEnv *env, jobject obj, jlong nativeContext, 290 bool directionDown, bool skipSubChannel) { 291 ALOGV("%s", __func__); 292 auto halTuner = getHalTuner(nativeContext); 293 if (halTuner == nullptr) return; 294 295 auto dir = convert::DirectionToHal(directionDown); 296 convert::ThrowIfFailed(env, halTuner->scan(dir, skipSubChannel)); 297 } 298 299 static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) { 300 ALOGV("%s", __func__); 301 lock_guard<mutex> lk(gContextMutex); 302 auto& ctx = getNativeContext(nativeContext); 303 304 auto halTuner10 = getHalTuner(ctx); 305 auto halTuner11 = ctx.mHalTuner11; 306 if (halTuner10 == nullptr) return; 307 308 auto selector = convert::ProgramSelectorToHal(env, jSelector); 309 if (halTuner11 != nullptr) { 310 convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector)); 311 } else { 312 uint32_t channel, subChannel; 313 if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) { 314 jniThrowException(env, "java/lang/IllegalArgumentException", 315 "Can't tune to non-AM/FM channel with HAL<1.1"); 316 return; 317 } 318 convert::ThrowIfFailed(env, halTuner10->tune(channel, subChannel)); 319 } 320 } 321 322 static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) { 323 ALOGV("%s", __func__); 324 auto halTuner = getHalTuner(nativeContext); 325 if (halTuner == nullptr) return; 326 327 convert::ThrowIfFailed(env, halTuner->cancel()); 328 } 329 330 static void nativeCancelAnnouncement(JNIEnv *env, jobject obj, jlong nativeContext) { 331 ALOGV("%s", __func__); 332 auto halTuner = getHalTuner11(nativeContext); 333 if (halTuner == nullptr) { 334 ALOGI("cancelling announcements is not supported with HAL < 1.1"); 335 return; 336 } 337 338 convert::ThrowIfFailed(env, halTuner->cancelAnnouncement()); 339 } 340 341 static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativeContext) { 342 ALOGV("%s", __func__); 343 lock_guard<mutex> lk(gContextMutex); 344 auto& ctx = getNativeContext(nativeContext); 345 346 auto halTuner10 = getHalTuner(ctx); 347 auto halTuner11 = ctx.mHalTuner11; 348 if (halTuner10 == nullptr) return nullptr; 349 350 JavaRef<jobject> jInfo; 351 Result halResult; 352 Return<void> hidlResult; 353 if (halTuner11 != nullptr) { 354 hidlResult = halTuner11->getProgramInformation_1_1([&](Result result, 355 const V1_1::ProgramInfo& info) { 356 halResult = result; 357 if (result != Result::OK) return; 358 jInfo = convert::ProgramInfoFromHal(env, info); 359 }); 360 } else { 361 hidlResult = halTuner10->getProgramInformation([&](Result result, 362 const V1_0::ProgramInfo& info) { 363 halResult = result; 364 if (result != Result::OK) return; 365 jInfo = convert::ProgramInfoFromHal(env, info, ctx.mBand); 366 }); 367 } 368 369 if (jInfo != nullptr) return jInfo.release(); 370 convert::ThrowIfFailed(env, hidlResult, halResult); 371 return nullptr; 372 } 373 374 static bool nativeStartBackgroundScan(JNIEnv *env, jobject obj, jlong nativeContext) { 375 ALOGV("%s", __func__); 376 auto halTuner = getHalTuner11(nativeContext); 377 if (halTuner == nullptr) { 378 ALOGI("Background scan is not supported with HAL < 1.1"); 379 return false; 380 } 381 382 auto halResult = halTuner->startBackgroundScan(); 383 384 if (halResult.isOk() && halResult == ProgramListResult::UNAVAILABLE) return false; 385 return !convert::ThrowIfFailed(env, halResult); 386 } 387 388 static jobject nativeGetProgramList(JNIEnv *env, jobject obj, jlong nativeContext, jobject jVendorFilter) { 389 ALOGV("%s", __func__); 390 auto halTuner = getHalTuner11(nativeContext); 391 if (halTuner == nullptr) { 392 ALOGI("Program list is not supported with HAL < 1.1"); 393 return nullptr; 394 } 395 396 JavaRef<jobject> jList; 397 ProgramListResult halResult = ProgramListResult::NOT_INITIALIZED; 398 auto filter = convert::VendorInfoToHal(env, jVendorFilter); 399 auto hidlResult = halTuner->getProgramList(filter, 400 [&](ProgramListResult result, const hidl_vec<V1_1::ProgramInfo>& programList) { 401 halResult = result; 402 if (halResult != ProgramListResult::OK) return; 403 404 jList = make_javaref(env, env->NewObject(gjni.ArrayList.clazz, gjni.ArrayList.cstor)); 405 for (auto& program : programList) { 406 auto jProgram = convert::ProgramInfoFromHal(env, program); 407 env->CallBooleanMethod(jList.get(), gjni.ArrayList.add, jProgram.get()); 408 } 409 }); 410 411 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr; 412 413 return jList.release(); 414 } 415 416 static jbyteArray nativeGetImage(JNIEnv *env, jobject obj, jlong nativeContext, jint id) { 417 ALOGV("%s(%x)", __func__, id); 418 lock_guard<mutex> lk(gContextMutex); 419 auto& ctx = getNativeContext(nativeContext); 420 421 if (ctx.mHalModule11 == nullptr) { 422 jniThrowException(env, "java/lang/IllegalStateException", 423 "Out-of-band images are not supported with HAL < 1.1"); 424 return nullptr; 425 } 426 427 auto halModule = ctx.mHalModule11.promote(); 428 if (halModule == nullptr) { 429 ALOGE("HAL module is gone"); 430 return nullptr; 431 } 432 433 JavaRef<jbyteArray> jRawImage = nullptr; 434 435 auto hidlResult = halModule->getImage(id, [&](hidl_vec<uint8_t> rawImage) { 436 auto len = rawImage.size(); 437 if (len == 0) return; 438 439 jRawImage = make_javaref(env, env->NewByteArray(len)); 440 if (jRawImage == nullptr) { 441 ALOGE("Failed to allocate byte array of len %zu", len); 442 return; 443 } 444 445 env->SetByteArrayRegion(jRawImage.get(), 0, len, 446 reinterpret_cast<const jbyte*>(rawImage.data())); 447 }); 448 449 if (convert::ThrowIfFailed(env, hidlResult)) return nullptr; 450 451 return jRawImage.get(); 452 } 453 454 static bool nativeIsAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext) { 455 ALOGV("%s", __func__); 456 auto halTuner = getHalTuner11(nativeContext); 457 if (halTuner == nullptr) { 458 jniThrowException(env, "java/lang/IllegalStateException", 459 "Forced analog switch is not supported with HAL < 1.1"); 460 return false; 461 } 462 463 bool isForced; 464 Result halResult; 465 auto hidlResult = halTuner->isAnalogForced([&](Result result, bool isForcedRet) { 466 halResult = result; 467 isForced = isForcedRet; 468 }); 469 470 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return false; 471 472 return isForced; 473 } 474 475 static void nativeSetAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext, bool isForced) { 476 ALOGV("%s(%d)", __func__, isForced); 477 auto halTuner = getHalTuner11(nativeContext); 478 if (halTuner == nullptr) { 479 jniThrowException(env, "java/lang/IllegalStateException", 480 "Forced analog switch is not supported with HAL < 1.1"); 481 return; 482 } 483 484 auto halResult = halTuner->setAnalogForced(isForced); 485 convert::ThrowIfFailed(env, halResult); 486 } 487 488 static bool nativeIsAntennaConnected(JNIEnv *env, jobject obj, jlong nativeContext) { 489 ALOGV("%s", __func__); 490 auto halTuner = getHalTuner(nativeContext); 491 if (halTuner == nullptr) return false; 492 493 bool isConnected = false; 494 Result halResult; 495 auto hidlResult = halTuner->getConfiguration([&](Result result, const BandConfig& config) { 496 halResult = result; 497 isConnected = config.antennaConnected; 498 }); 499 convert::ThrowIfFailed(env, hidlResult, halResult); 500 return isConnected; 501 } 502 503 static const JNINativeMethod gTunerMethods[] = { 504 { "nativeInit", "(IZI)J", (void*)nativeInit }, 505 { "nativeFinalize", "(J)V", (void*)nativeFinalize }, 506 { "nativeClose", "(J)V", (void*)nativeClose }, 507 { "nativeSetConfiguration", "(JLandroid/hardware/radio/RadioManager$BandConfig;)V", 508 (void*)nativeSetConfiguration }, 509 { "nativeGetConfiguration", "(JI)Landroid/hardware/radio/RadioManager$BandConfig;", 510 (void*)nativeGetConfiguration }, 511 { "nativeSetMuted", "(JZ)V", (void*)nativeSetMuted }, 512 { "nativeStep", "(JZZ)V", (void*)nativeStep }, 513 { "nativeScan", "(JZZ)V", (void*)nativeScan }, 514 { "nativeTune", "(JLandroid/hardware/radio/ProgramSelector;)V", (void*)nativeTune }, 515 { "nativeCancel", "(J)V", (void*)nativeCancel }, 516 { "nativeCancelAnnouncement", "(J)V", (void*)nativeCancelAnnouncement }, 517 { "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;", 518 (void*)nativeGetProgramInformation }, 519 { "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan }, 520 { "nativeGetProgramList", "(JLjava/util/Map;)Ljava/util/List;", 521 (void*)nativeGetProgramList }, 522 { "nativeGetImage", "(JI)[B", (void*)nativeGetImage}, 523 { "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced }, 524 { "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced }, 525 { "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected }, 526 }; 527 528 } // namespace Tuner 529 } // namespace BroadcastRadio 530 } // namespace server 531 532 void register_android_server_broadcastradio_Tuner(JavaVM *vm, JNIEnv *env) { 533 using namespace server::BroadcastRadio::Tuner; 534 535 register_android_server_broadcastradio_TunerCallback(vm, env); 536 537 auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner"); 538 gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J"); 539 gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I"); 540 gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback", 541 "Lcom/android/server/broadcastradio/TunerCallback;"); 542 543 auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); 544 gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass); 545 gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V"); 546 gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); 547 548 auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/Tuner", 549 gTunerMethods, NELEM(gTunerMethods)); 550 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); 551 } 552 553 } // namespace android 554