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.TunerCallback.jni" 18 #define LOG_NDEBUG 0 19 20 #include "TunerCallback.h" 21 22 #include "Tuner.h" 23 #include "convert.h" 24 25 #include <broadcastradio-utils-1x/Utils.h> 26 #include <core_jni_helpers.h> 27 #include <nativehelper/JNIHelp.h> 28 #include <utils/Log.h> 29 30 namespace android { 31 namespace server { 32 namespace BroadcastRadio { 33 namespace TunerCallback { 34 35 using std::lock_guard; 36 using std::mutex; 37 38 using hardware::Return; 39 using hardware::hidl_vec; 40 41 namespace V1_0 = hardware::broadcastradio::V1_0; 42 namespace V1_1 = hardware::broadcastradio::V1_1; 43 namespace utils = hardware::broadcastradio::utils; 44 45 using V1_0::Band; 46 using V1_0::BandConfig; 47 using V1_0::MetaData; 48 using V1_0::Result; 49 using V1_1::ITunerCallback; 50 using V1_1::ProgramInfo; 51 using V1_1::ProgramListResult; 52 using V1_1::ProgramSelector; 53 using V1_1::VendorKeyValue; 54 using utils::HalRevision; 55 56 static JavaVM *gvm = nullptr; 57 58 static struct { 59 struct { 60 jclass clazz; 61 jfieldID nativeContext; 62 jmethodID handleHwFailure; 63 jmethodID onError; 64 jmethodID onConfigurationChanged; 65 jmethodID onCurrentProgramInfoChanged; 66 jmethodID onTrafficAnnouncement; 67 jmethodID onEmergencyAnnouncement; 68 jmethodID onAntennaState; 69 jmethodID onBackgroundScanAvailabilityChange; 70 jmethodID onBackgroundScanComplete; 71 jmethodID onProgramListChanged; 72 } TunerCallback; 73 } gjni; 74 75 // from frameworks/base/core/java/android/hardware/radio/RadioTuner.java 76 enum class TunerError : jint { 77 HARDWARE_FAILURE = 0, 78 SERVER_DIED = 1, 79 CANCELLED = 2, 80 SCAN_TIMEOUT = 3, 81 CONFIG = 4, 82 BACKGROUND_SCAN_UNAVAILABLE = 5, 83 BACKGROUND_SCAN_FAILED = 6, 84 }; 85 86 static mutex gContextMutex; 87 88 class NativeCallback : public ITunerCallback { 89 mutex mMut; 90 91 jobject mJTuner; 92 jobject mJCallback; 93 NativeCallbackThread mCallbackThread; 94 HalRevision mHalRev; 95 96 Band mBand; 97 98 // Carries current program info data for 1.0 newMetadata callback. 99 V1_0::ProgramInfo mCurrentProgramInfo; 100 101 DISALLOW_COPY_AND_ASSIGN(NativeCallback); 102 103 public: 104 NativeCallback(JNIEnv *env, jobject jTuner, jobject jCallback, HalRevision halRev); 105 virtual ~NativeCallback(); 106 107 void detach(); 108 109 virtual Return<void> hardwareFailure(); 110 virtual Return<void> configChange(Result result, const BandConfig& config); 111 virtual Return<void> tuneComplete(Result result, const V1_0::ProgramInfo& info); 112 virtual Return<void> afSwitch(const V1_0::ProgramInfo& info); 113 virtual Return<void> antennaStateChange(bool connected); 114 virtual Return<void> trafficAnnouncement(bool active); 115 virtual Return<void> emergencyAnnouncement(bool active); 116 virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel, 117 const hidl_vec<MetaData>& metadata); 118 virtual Return<void> tuneComplete_1_1(Result result, const ProgramSelector& selector); 119 virtual Return<void> backgroundScanAvailable(bool isAvailable); 120 virtual Return<void> backgroundScanComplete(ProgramListResult result); 121 virtual Return<void> programListChanged(); 122 virtual Return<void> currentProgramInfoChanged(const ProgramInfo& info); 123 }; 124 125 struct TunerCallbackContext { 126 TunerCallbackContext() {} 127 128 sp<NativeCallback> mNativeCallback; 129 130 private: 131 DISALLOW_COPY_AND_ASSIGN(TunerCallbackContext); 132 }; 133 134 NativeCallback::NativeCallback(JNIEnv *env, jobject jTuner, jobject jCallback, HalRevision halRev) 135 : mCallbackThread(gvm), mHalRev(halRev) { 136 ALOGV("%s", __func__); 137 mJTuner = env->NewGlobalRef(jTuner); 138 mJCallback = env->NewGlobalRef(jCallback); 139 } 140 141 NativeCallback::~NativeCallback() { 142 ALOGV("%s", __func__); 143 144 // stop callback thread before dereferencing client callback 145 mCallbackThread.stop(); 146 147 JNIEnv *env = nullptr; 148 gvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4); 149 if (env != nullptr) { 150 env->DeleteGlobalRef(mJTuner); 151 env->DeleteGlobalRef(mJCallback); 152 } 153 } 154 155 void NativeCallback::detach() { 156 // stop callback thread to ignore further calls 157 mCallbackThread.stop(); 158 } 159 160 Return<void> NativeCallback::hardwareFailure() { 161 mCallbackThread.enqueue([this](JNIEnv *env) { 162 env->CallVoidMethod(mJCallback, gjni.TunerCallback.handleHwFailure); 163 }); 164 165 return Return<void>(); 166 } 167 168 Return<void> NativeCallback::configChange(Result result, const BandConfig& config) { 169 ALOGV("%s(%d)", __func__, result); 170 171 mCallbackThread.enqueue([result, config, this](JNIEnv *env) { 172 if (result == Result::OK) { 173 auto region = Tuner::getRegion(env, mJTuner); 174 auto jConfig = convert::BandConfigFromHal(env, config, region); 175 if (jConfig == nullptr) return; 176 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onConfigurationChanged, 177 jConfig.get()); 178 } else { 179 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, TunerError::CONFIG); 180 } 181 }); 182 183 return Return<void>(); 184 } 185 186 Return<void> NativeCallback::tuneComplete(Result result, const V1_0::ProgramInfo& info) { 187 ALOGV("%s(%d)", __func__, result); 188 189 if (mHalRev > HalRevision::V1_0) { 190 ALOGW("1.0 callback was ignored"); 191 return {}; 192 } 193 194 if (result == Result::OK) { 195 { 196 lock_guard<mutex> lk(mMut); 197 mCurrentProgramInfo = info; 198 } 199 200 // tuneComplete_1_1 implementation does not handle success case, see the implementation 201 mCallbackThread.enqueue([this, info](JNIEnv *env) { 202 auto jInfo = convert::ProgramInfoFromHal(env, info, mBand); 203 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged, 204 jInfo.get()); 205 }); 206 return {}; 207 } 208 209 auto selector = utils::make_selector(mBand, info.channel, info.subChannel); 210 return tuneComplete_1_1(result, selector); 211 } 212 213 Return<void> NativeCallback::tuneComplete_1_1(Result result, const ProgramSelector& selector) { 214 ALOGV("%s(%d)", __func__, result); 215 216 mCallbackThread.enqueue([result, this](JNIEnv *env) { 217 /* for HAL 1.1, onCurrentProgramInfoChanged will be called from currentProgramInfoChanged, 218 * so we don't need to handle success case here. 219 */ 220 if (result == Result::OK) return; 221 222 TunerError cause = TunerError::CANCELLED; 223 if (result == Result::TIMEOUT) cause = TunerError::SCAN_TIMEOUT; 224 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause); 225 }); 226 227 return Return<void>(); 228 } 229 230 Return<void> NativeCallback::afSwitch(const V1_0::ProgramInfo& info) { 231 ALOGV("%s", __func__); 232 return tuneComplete(Result::OK, info); 233 } 234 235 Return<void> NativeCallback::antennaStateChange(bool connected) { 236 ALOGV("%s(%d)", __func__, connected); 237 238 mCallbackThread.enqueue([this, connected](JNIEnv *env) { 239 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onAntennaState, connected); 240 }); 241 242 return Return<void>(); 243 } 244 245 Return<void> NativeCallback::trafficAnnouncement(bool active) { 246 ALOGV("%s(%d)", __func__, active); 247 248 mCallbackThread.enqueue([this, active](JNIEnv *env) { 249 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onTrafficAnnouncement, active); 250 }); 251 252 return Return<void>(); 253 } 254 255 Return<void> NativeCallback::emergencyAnnouncement(bool active) { 256 ALOGV("%s(%d)", __func__, active); 257 258 mCallbackThread.enqueue([this, active](JNIEnv *env) { 259 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onEmergencyAnnouncement, active); 260 }); 261 262 return Return<void>(); 263 } 264 265 Return<void> NativeCallback::newMetadata(uint32_t channel, uint32_t subChannel, 266 const hidl_vec<MetaData>& metadata) { 267 ALOGV("%s(%d, %d)", __func__, channel, subChannel); 268 269 if (mHalRev > HalRevision::V1_0) { 270 ALOGW("1.0 callback was ignored"); 271 return {}; 272 } 273 274 V1_0::ProgramInfo info; 275 { 276 lock_guard<mutex> lk(mMut); 277 info = mCurrentProgramInfo; 278 } 279 if (channel != info.channel || subChannel != info.subChannel) { 280 ALOGE("Channel mismatch on newMetadata callback (%d.%d != %d.%d)", 281 channel, subChannel, info.channel, info.subChannel); 282 return {}; 283 } 284 info.metadata = metadata; 285 286 mCallbackThread.enqueue([this, info](JNIEnv *env) { 287 auto jInfo = convert::ProgramInfoFromHal(env, info, mBand); 288 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged, 289 jInfo.get()); 290 }); 291 292 return {}; 293 } 294 295 Return<void> NativeCallback::backgroundScanAvailable(bool isAvailable) { 296 ALOGV("%s(%d)", __func__, isAvailable); 297 298 mCallbackThread.enqueue([this, isAvailable](JNIEnv *env) { 299 env->CallVoidMethod(mJCallback, 300 gjni.TunerCallback.onBackgroundScanAvailabilityChange, isAvailable); 301 }); 302 303 return Return<void>(); 304 } 305 306 Return<void> NativeCallback::backgroundScanComplete(ProgramListResult result) { 307 ALOGV("%s(%d)", __func__, result); 308 309 mCallbackThread.enqueue([this, result](JNIEnv *env) { 310 if (result == ProgramListResult::OK) { 311 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onBackgroundScanComplete); 312 } else { 313 auto cause = (result == ProgramListResult::UNAVAILABLE) ? 314 TunerError::BACKGROUND_SCAN_UNAVAILABLE : TunerError::BACKGROUND_SCAN_FAILED; 315 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause); 316 } 317 }); 318 319 return Return<void>(); 320 } 321 322 Return<void> NativeCallback::programListChanged() { 323 ALOGV("%s", __func__); 324 325 mCallbackThread.enqueue([this](JNIEnv *env) { 326 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramListChanged); 327 }); 328 329 return Return<void>(); 330 } 331 332 Return<void> NativeCallback::currentProgramInfoChanged(const ProgramInfo& info) { 333 ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str()); 334 335 mCallbackThread.enqueue([this, info](JNIEnv *env) { 336 auto jInfo = convert::ProgramInfoFromHal(env, info); 337 env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged, 338 jInfo.get()); 339 }); 340 341 return Return<void>(); 342 } 343 344 static TunerCallbackContext& getNativeContext(jlong nativeContextHandle) { 345 auto nativeContext = reinterpret_cast<TunerCallbackContext*>(nativeContextHandle); 346 LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized"); 347 return *nativeContext; 348 } 349 350 /** 351 * Always lock gContextMutex when using native context. 352 */ 353 static TunerCallbackContext& getNativeContext(JNIEnv *env, jobject jTunerCb) { 354 return getNativeContext(env->GetLongField(jTunerCb, gjni.TunerCallback.nativeContext)); 355 } 356 357 static jlong nativeInit(JNIEnv *env, jobject obj, jobject jTuner, jint jHalRev) { 358 ALOGV("%s", __func__); 359 lock_guard<mutex> lk(gContextMutex); 360 361 auto halRev = static_cast<HalRevision>(jHalRev); 362 363 auto ctx = new TunerCallbackContext(); 364 ctx->mNativeCallback = new NativeCallback(env, jTuner, obj, halRev); 365 366 static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer"); 367 return reinterpret_cast<jlong>(ctx); 368 } 369 370 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) { 371 ALOGV("%s", __func__); 372 lock_guard<mutex> lk(gContextMutex); 373 374 auto ctx = reinterpret_cast<TunerCallbackContext*>(nativeContext); 375 delete ctx; 376 } 377 378 static void nativeDetach(JNIEnv *env, jobject obj, jlong nativeContext) { 379 ALOGV("%s", __func__); 380 lock_guard<mutex> lk(gContextMutex); 381 auto& ctx = getNativeContext(nativeContext); 382 383 if (ctx.mNativeCallback == nullptr) return; 384 ctx.mNativeCallback->detach(); 385 ctx.mNativeCallback = nullptr; 386 } 387 388 sp<ITunerCallback> getNativeCallback(JNIEnv *env, jobject jTunerCallback) { 389 lock_guard<mutex> lk(gContextMutex); 390 auto& ctx = getNativeContext(env, jTunerCallback); 391 return ctx.mNativeCallback; 392 } 393 394 static const JNINativeMethod gTunerCallbackMethods[] = { 395 { "nativeInit", "(Lcom/android/server/broadcastradio/hal1/Tuner;I)J", (void*)nativeInit }, 396 { "nativeFinalize", "(J)V", (void*)nativeFinalize }, 397 { "nativeDetach", "(J)V", (void*)nativeDetach }, 398 }; 399 400 } // namespace TunerCallback 401 } // namespace BroadcastRadio 402 } // namespace server 403 404 void register_android_server_broadcastradio_TunerCallback(JavaVM *vm, JNIEnv *env) { 405 using namespace server::BroadcastRadio::TunerCallback; 406 407 gvm = vm; 408 409 auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/TunerCallback"); 410 gjni.TunerCallback.clazz = MakeGlobalRefOrDie(env, tunerCbClass); 411 gjni.TunerCallback.nativeContext = GetFieldIDOrDie(env, tunerCbClass, "mNativeContext", "J"); 412 gjni.TunerCallback.handleHwFailure = GetMethodIDOrDie(env, tunerCbClass, "handleHwFailure", "()V"); 413 gjni.TunerCallback.onError = GetMethodIDOrDie(env, tunerCbClass, "onError", "(I)V"); 414 gjni.TunerCallback.onConfigurationChanged = GetMethodIDOrDie(env, tunerCbClass, 415 "onConfigurationChanged", "(Landroid/hardware/radio/RadioManager$BandConfig;)V"); 416 gjni.TunerCallback.onCurrentProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass, 417 "onCurrentProgramInfoChanged", "(Landroid/hardware/radio/RadioManager$ProgramInfo;)V"); 418 gjni.TunerCallback.onTrafficAnnouncement = GetMethodIDOrDie(env, tunerCbClass, 419 "onTrafficAnnouncement", "(Z)V"); 420 gjni.TunerCallback.onEmergencyAnnouncement = GetMethodIDOrDie(env, tunerCbClass, 421 "onEmergencyAnnouncement", "(Z)V"); 422 gjni.TunerCallback.onAntennaState = GetMethodIDOrDie(env, tunerCbClass, 423 "onAntennaState", "(Z)V"); 424 gjni.TunerCallback.onBackgroundScanAvailabilityChange = GetMethodIDOrDie(env, tunerCbClass, 425 "onBackgroundScanAvailabilityChange", "(Z)V"); 426 gjni.TunerCallback.onBackgroundScanComplete = GetMethodIDOrDie(env, tunerCbClass, 427 "onBackgroundScanComplete", "()V"); 428 gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass, 429 "onProgramListChanged", "()V"); 430 431 auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/TunerCallback", 432 gTunerCallbackMethods, NELEM(gTunerCallbackMethods)); 433 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); 434 } 435 436 } // namespace android 437