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