blob: e1ade4dc02da76408f02703d435cb9e03e60c104 [file] [log] [blame]
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -07001/**
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
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070017#define LOG_TAG "BroadcastRadioService.Tuner.jni"
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070018#define LOG_NDEBUG 0
19
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070020#include "Tuner.h"
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070021
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070022#include "convert.h"
23#include "TunerCallback.h"
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070024
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070025#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -070026#include <binder/IPCThreadState.h>
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070027#include <broadcastradio-utils/Utils.h>
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070028#include <core_jni_helpers.h>
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -070029#include <media/AudioSystem.h>
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070030#include <nativehelper/JNIHelp.h>
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070031#include <utils/Log.h>
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070032
33namespace android {
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070034namespace server {
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070035namespace BroadcastRadio {
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070036namespace Tuner {
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070037
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070038using std::lock_guard;
39using std::mutex;
40
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070041using hardware::Return;
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -070042using hardware::hidl_death_recipient;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070043using hardware::hidl_vec;
44
45namespace V1_0 = hardware::broadcastradio::V1_0;
46namespace V1_1 = hardware::broadcastradio::V1_1;
47
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070048using V1_0::Band;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070049using V1_0::BandConfig;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070050using V1_0::MetaData;
51using V1_0::Result;
52using V1_1::ITunerCallback;
53using V1_1::ProgramListResult;
54
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070055static mutex gContextMutex;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070056
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070057static struct {
58 struct {
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -070059 jclass clazz;
60 jmethodID cstor;
61 jmethodID add;
62 } ArrayList;
63 struct {
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070064 jfieldID nativeContext;
65 jfieldID region;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070066 jfieldID tunerCallback;
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -070067 } Tuner;
68} gjni;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070069
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -070070static const char* const kAudioDeviceName = "Radio tuner source";
71
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -070072class HalDeathRecipient : public hidl_death_recipient {
73 wp<V1_1::ITunerCallback> mTunerCallback;
74
75public:
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
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070081struct TunerContext {
82 TunerContext() {}
83
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -070084 bool mIsClosed = false;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -070085 HalRevision mHalRev;
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -070086 bool mWithAudio;
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -070087 bool mIsAudioConnected = false;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -070088 Band mBand;
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -070089 wp<V1_0::IBroadcastRadio> mHalModule;
90 wp<V1_1::IBroadcastRadio> mHalModule11;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -070091 sp<V1_0::ITuner> mHalTuner;
92 sp<V1_1::ITuner> mHalTuner11;
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -070093 sp<HalDeathRecipient> mHalDeathRecipient;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070094
95private:
96 DISALLOW_COPY_AND_ASSIGN(TunerContext);
97};
98
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070099static 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}
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700104
105/**
106 * Always lock gContextMutex when using native context.
107 */
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700108static TunerContext& getNativeContext(JNIEnv *env, JavaRef<jobject> const &jTuner) {
109 return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700110}
111
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700112static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio, jint band) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700113 ALOGV("%s", __func__);
114 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700115
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700116 auto ctx = new TunerContext();
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700117 ctx->mHalRev = static_cast<HalRevision>(halRev);
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700118 ctx->mWithAudio = withAudio;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700119 ctx->mBand = static_cast<Band>(band);
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700120
121 static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
122 return reinterpret_cast<jlong>(ctx);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700123}
124
125static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700126 ALOGV("%s", __func__);
127 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700128
129 auto ctx = reinterpret_cast<TunerContext*>(nativeContext);
130 delete ctx;
131}
132
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -0700133void 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
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700143// TODO(b/62713378): implement support for multiple tuners open at the same time
144static void notifyAudioService(TunerContext& ctx, bool connected) {
145 if (!ctx.mWithAudio) return;
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -0700146 if (ctx.mIsAudioConnected == connected) return;
147 ctx.mIsAudioConnected = connected;
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700148
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
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700157void assignHalInterfaces(JNIEnv *env, JavaRef<jobject> const &jTuner,
158 sp<V1_0::IBroadcastRadio> halModule, sp<V1_0::ITuner> halTuner) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700159 ALOGV("%s(%p)", __func__, halTuner.get());
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700160 ALOGE_IF(halTuner == nullptr, "HAL tuner is a nullptr");
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700161 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700162 auto& ctx = getNativeContext(env, jTuner);
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700163
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700164 if (ctx.mIsClosed) {
Tomasz Wasilczyk14752372017-06-21 11:58:21 -0700165 ALOGD("Tuner was closed during initialization");
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700166 // dropping the last reference will close HAL tuner
167 return;
168 }
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -0700169 if (ctx.mHalTuner != nullptr) {
170 ALOGE("HAL tuner is already set.");
171 return;
172 }
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700173
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700174 ctx.mHalModule = halModule;
175 ctx.mHalModule11 = V1_1::IBroadcastRadio::castFrom(halModule).withDefault(nullptr);
176
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700177 ctx.mHalTuner = halTuner;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700178 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");
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700181
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -0700182 ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner));
183 halTuner->linkToDeath(ctx.mHalDeathRecipient, 0);
184
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700185 notifyAudioService(ctx, true);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700186}
187
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700188static sp<V1_0::ITuner> getHalTuner(const TunerContext& ctx) {
189 auto tuner = ctx.mHalTuner;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700190 LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner is not open");
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700191 return tuner;
192}
193
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700194sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700195 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700196 return getHalTuner(getNativeContext(nativeContext));
197}
198
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700199sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700200 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700201 return getNativeContext(nativeContext).mHalTuner11;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700202}
203
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700204sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
205 return TunerCallback::getNativeCallback(env,
206 env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700207}
208
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -0700209Region getRegion(JNIEnv *env, jobject obj) {
210 return static_cast<Region>(env->GetIntField(obj, gjni.Tuner.region));
211}
212
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700213static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700214 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700215 auto& ctx = getNativeContext(nativeContext);
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700216
Tomasz Wasilczyke8f4b342017-06-19 10:48:16 -0700217 if (ctx.mIsClosed) return;
218 ctx.mIsClosed = true;
219
220 if (ctx.mHalTuner == nullptr) {
221 ALOGI("Tuner closed during initialization");
222 return;
223 }
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700224
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700225 ALOGI("Closing tuner %p", ctx.mHalTuner.get());
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -0700226
Tomasz Wasilczykb0b98fe2017-06-16 14:07:33 -0700227 notifyAudioService(ctx, false);
Tomasz Wasilczyk2e5a23c2017-06-19 14:47:03 -0700228
229 ctx.mHalTuner->unlinkToDeath(ctx.mHalDeathRecipient);
230 ctx.mHalDeathRecipient = nullptr;
231
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700232 ctx.mHalTuner11 = nullptr;
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700233 ctx.mHalTuner = nullptr;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700234}
235
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700236static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700237 ALOGV("%s", __func__);
238 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700239 auto& ctx = getNativeContext(nativeContext);
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700240
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700241 auto halTuner = getHalTuner(ctx);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700242 if (halTuner == nullptr) return;
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700243
244 Region region_unused;
245 BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused);
246
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700247 if (convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal))) return;
248
249 ctx.mBand = bandConfigHal.type;
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700250}
251
252static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext,
253 Region region) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700254 ALOGV("%s", __func__);
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700255 auto halTuner = getHalTuner(nativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700256 if (halTuner == nullptr) return nullptr;
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700257
258 BandConfig halConfig;
259 Result halResult;
260 auto hidlResult = halTuner->getConfiguration([&](Result result, const BandConfig& config) {
261 halResult = result;
262 halConfig = config;
263 });
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700264 if (convert::ThrowIfFailed(env, hidlResult, halResult)) {
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700265 return nullptr;
266 }
267
268 return convert::BandConfigFromHal(env, halConfig, region).release();
269}
270
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -0700271static 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
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700279static void nativeStep(JNIEnv *env, jobject obj, jlong nativeContext,
280 bool directionDown, bool skipSubChannel) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700281 ALOGV("%s", __func__);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700282 auto halTuner = getHalTuner(nativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700283 if (halTuner == nullptr) return;
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700284
285 auto dir = convert::DirectionToHal(directionDown);
286 convert::ThrowIfFailed(env, halTuner->step(dir, skipSubChannel));
287}
288
289static void nativeScan(JNIEnv *env, jobject obj, jlong nativeContext,
290 bool directionDown, bool skipSubChannel) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700291 ALOGV("%s", __func__);
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700292 auto halTuner = getHalTuner(nativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700293 if (halTuner == nullptr) return;
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700294
295 auto dir = convert::DirectionToHal(directionDown);
296 convert::ThrowIfFailed(env, halTuner->scan(dir, skipSubChannel));
297}
298
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700299static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700300 ALOGV("%s", __func__);
301 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700302 auto& ctx = getNativeContext(nativeContext);
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700303
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700304 auto halTuner10 = getHalTuner(ctx);
305 auto halTuner11 = ctx.mHalTuner11;
306 if (halTuner10 == nullptr) return;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700307
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700308 auto selector = convert::ProgramSelectorToHal(env, jSelector);
309 if (halTuner11 != nullptr) {
Tomasz Wasilczyk83162912017-08-01 10:52:58 -0700310 convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700311 } 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 }
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700320}
321
322static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700323 ALOGV("%s", __func__);
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700324 auto halTuner = getHalTuner(nativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700325 if (halTuner == nullptr) return;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700326
327 convert::ThrowIfFailed(env, halTuner->cancel());
328}
329
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -0700330static 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
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700341static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700342 ALOGV("%s", __func__);
343 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700344 auto& ctx = getNativeContext(nativeContext);
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700345
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700346 auto halTuner10 = getHalTuner(ctx);
347 auto halTuner11 = ctx.mHalTuner11;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700348 if (halTuner10 == nullptr) return nullptr;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700349
Tomasz Wasilczykbad4cd22017-05-15 13:40:56 -0700350 JavaRef<jobject> jInfo;
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700351 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;
Tomasz Wasilczykbad4cd22017-05-15 13:40:56 -0700357 if (result != Result::OK) return;
358 jInfo = convert::ProgramInfoFromHal(env, info);
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700359 });
360 } else {
361 hidlResult = halTuner10->getProgramInformation([&](Result result,
362 const V1_0::ProgramInfo& info) {
363 halResult = result;
Tomasz Wasilczykbad4cd22017-05-15 13:40:56 -0700364 if (result != Result::OK) return;
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700365 jInfo = convert::ProgramInfoFromHal(env, info, ctx.mBand);
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700366 });
367 }
368
Tomasz Wasilczykbad4cd22017-05-15 13:40:56 -0700369 if (jInfo != nullptr) return jInfo.release();
370 convert::ThrowIfFailed(env, hidlResult, halResult);
371 return nullptr;
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700372}
373
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700374static bool nativeStartBackgroundScan(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700375 ALOGV("%s", __func__);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700376 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
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700388static jobject nativeGetProgramList(JNIEnv *env, jobject obj, jlong nativeContext, jobject jVendorFilter) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700389 ALOGV("%s", __func__);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700390 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;
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700398 auto filter = convert::VendorInfoToHal(env, jVendorFilter);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700399 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
Tomasz Wasilczykcc0b4792017-06-16 10:29:27 -0700404 jList = make_javaref(env, env->NewObject(gjni.ArrayList.clazz, gjni.ArrayList.cstor));
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700405 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
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700416static jbyteArray nativeGetImage(JNIEnv *env, jobject obj, jlong nativeContext, jint id) {
417 ALOGV("%s(%x)", __func__, id);
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700418 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700419 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
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700454static bool nativeIsAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700455 ALOGV("%s", __func__);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700456 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
475static void nativeSetAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext, bool isForced) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700476 ALOGV("%s(%d)", __func__, isForced);
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700477 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
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700488static bool nativeIsAntennaConnected(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700489 ALOGV("%s", __func__);
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700490 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
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700503static const JNINativeMethod gTunerMethods[] = {
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700504 { "nativeInit", "(IZI)J", (void*)nativeInit },
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700505 { "nativeFinalize", "(J)V", (void*)nativeFinalize },
Tomasz Wasilczyk8b6db4f2017-05-01 09:28:36 -0700506 { "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 },
Tomasz Wasilczykb1a6fea2017-09-12 10:21:16 -0700511 { "nativeSetMuted", "(JZ)V", (void*)nativeSetMuted },
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700512 { "nativeStep", "(JZZ)V", (void*)nativeStep },
513 { "nativeScan", "(JZZ)V", (void*)nativeScan },
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700514 { "nativeTune", "(JLandroid/hardware/radio/ProgramSelector;)V", (void*)nativeTune },
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700515 { "nativeCancel", "(J)V", (void*)nativeCancel },
Tomasz Wasilczykc4cd8232017-07-14 10:46:15 -0700516 { "nativeCancelAnnouncement", "(J)V", (void*)nativeCancelAnnouncement },
Tomasz Wasilczyk37d986d2017-05-08 10:41:32 -0700517 { "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;",
518 (void*)nativeGetProgramInformation },
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700519 { "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan },
Tomasz Wasilczyk0f1776d2017-08-03 11:03:49 -0700520 { "nativeGetProgramList", "(JLjava/util/Map;)Ljava/util/List;",
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700521 (void*)nativeGetProgramList },
Tomasz Wasilczyk4482b142017-07-17 13:57:12 -0700522 { "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700523 { "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
524 { "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
Tomasz Wasilczyk39ac2142017-05-17 14:55:17 -0700525 { "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected },
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700526};
527
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700528} // namespace Tuner
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700529} // namespace BroadcastRadio
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700530} // namespace server
531
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700532void register_android_server_broadcastradio_Tuner(JavaVM *vm, JNIEnv *env) {
533 using namespace server::BroadcastRadio::Tuner;
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700534
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700535 register_android_server_broadcastradio_TunerCallback(vm, env);
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -0700536
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700537 auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner");
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -0700538 gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J");
539 gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I");
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700540 gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback",
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700541 "Lcom/android/server/broadcastradio/TunerCallback;");
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700542
Tomasz Wasilczykd3d53f62017-05-15 12:55:28 -0700543 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
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700548 auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/Tuner",
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700549 gTunerMethods, NELEM(gTunerMethods));
550 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
551}
552
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700553} // namespace android