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