Home | History | Annotate | Download | only in BroadcastRadio
      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