blob: 176ae81a15e4295472e0fd0e25a357668a5c7d49 [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.jni"
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070018#define LOG_NDEBUG 0
19
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070020#include "BroadcastRadioService.h"
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070021
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070022#include "Tuner.h"
23#include "convert.h"
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070024
Tomasz Wasilczyk23837932017-05-05 08:42:10 -070025#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -070026#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070027#include <android/hidl/manager/1.0/IServiceManager.h>
Tomasz Wasilczyk8e67a332017-12-04 09:54:56 -080028#include <broadcastradio-utils-1x/Utils.h>
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070029#include <core_jni_helpers.h>
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070030#include <hidl/ServiceManagement.h>
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070031#include <nativehelper/JNIHelp.h>
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070032#include <utils/Log.h>
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070033
34namespace android {
Tomasz Wasilczyk21348172017-04-20 14:02:42 -070035namespace server {
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -070036namespace BroadcastRadio {
37namespace BroadcastRadioService {
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070038
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070039using std::lock_guard;
40using std::mutex;
41
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070042using hardware::Return;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070043using hardware::hidl_string;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070044using hardware::hidl_vec;
45
46namespace V1_0 = hardware::broadcastradio::V1_0;
47namespace V1_1 = hardware::broadcastradio::V1_1;
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -070048namespace V1_2 = hardware::broadcastradio::V1_2;
49namespace utils = hardware::broadcastradio::utils;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070050
51using V1_0::BandConfig;
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -070052using V1_0::Class;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070053using V1_0::ITuner;
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -070054using V1_0::MetaData;
55using V1_0::ProgramInfo;
56using V1_0::Result;
57using utils::HalRevision;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070058
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -070059static mutex gContextMutex;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070060
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070061static struct {
62 struct {
63 jclass clazz;
64 jmethodID cstor;
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070065 jmethodID add;
66 } ArrayList;
67 struct {
68 jclass clazz;
69 jmethodID cstor;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070070 } Tuner;
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -070071} gjni;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070072
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -070073struct Module {
74 sp<V1_0::IBroadcastRadio> radioModule;
75 HalRevision halRev;
76};
77
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070078struct ServiceContext {
79 ServiceContext() {}
80
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -070081 std::vector<Module> mModules;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070082
83private:
84 DISALLOW_COPY_AND_ASSIGN(ServiceContext);
85};
86
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -070087const std::vector<Class> gAllClasses = {
88 Class::AM_FM,
89 Class::SAT,
90 Class::DT,
91};
92
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -070093
94/**
95 * Always lock gContextMutex when using native context.
96 */
97static ServiceContext& getNativeContext(jlong nativeContextHandle) {
98 auto nativeContext = reinterpret_cast<ServiceContext*>(nativeContextHandle);
99 LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
100 return *nativeContext;
101}
102
103static jlong nativeInit(JNIEnv *env, jobject obj) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700104 ALOGV("%s", __func__);
105 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700106
107 auto nativeContext = new ServiceContext();
108 static_assert(sizeof(jlong) >= sizeof(nativeContext), "jlong is smaller than a pointer");
109 return reinterpret_cast<jlong>(nativeContext);
110}
111
112static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
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
116 auto ctx = reinterpret_cast<ServiceContext*>(nativeContext);
117 delete ctx;
118}
119
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700120static jobject nativeLoadModules(JNIEnv *env, jobject obj, jlong nativeContext) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700121 ALOGV("%s", __func__);
122 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700123 auto& ctx = getNativeContext(nativeContext);
124
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700125 // Get list of registered HIDL HAL implementations.
126 auto manager = hardware::defaultServiceManager();
127 hidl_vec<hidl_string> services;
128 if (manager == nullptr) {
129 ALOGE("Can't reach service manager, using default service implementation only");
130 services = std::vector<hidl_string>({ "default" });
131 } else {
132 manager->listByInterface(V1_0::IBroadcastRadioFactory::descriptor,
133 [&services](const hidl_vec<hidl_string> &registered) {
134 services = registered;
135 });
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700136 }
137
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700138 // Scan provided list for actually implemented modules.
139 ctx.mModules.clear();
140 auto jModules = make_javaref(env, env->NewObject(gjni.ArrayList.clazz, gjni.ArrayList.cstor));
141 for (auto&& serviceName : services) {
142 ALOGV("checking service: %s", serviceName.c_str());
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700143
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700144 auto factory = V1_0::IBroadcastRadioFactory::getService(serviceName);
145 if (factory == nullptr) {
146 ALOGE("can't load service %s", serviceName.c_str());
147 continue;
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700148 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700149
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -0700150 auto halRev = HalRevision::V1_0;
151 auto halMinor = 0;
152 if (V1_2::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr) != nullptr) {
153 halRev = HalRevision::V1_2;
154 halMinor = 2;
155 } else if (V1_1::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr)
156 != nullptr) {
157 halRev = HalRevision::V1_1;
158 halMinor = 1;
159 }
160
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700161 // Second level of scanning - that's unfortunate.
162 for (auto&& clazz : gAllClasses) {
163 sp<V1_0::IBroadcastRadio> module10 = nullptr;
164 sp<V1_1::IBroadcastRadio> module11 = nullptr;
165 factory->connectModule(clazz, [&](Result res, const sp<V1_0::IBroadcastRadio>& module) {
166 if (res == Result::OK) {
167 module10 = module;
168 module11 = V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr);
169 } else if (res != Result::INVALID_ARGUMENTS) {
170 ALOGE("couldn't load %s:%s module",
171 serviceName.c_str(), V1_0::toString(clazz).c_str());
172 }
173 });
174 if (module10 == nullptr) continue;
175
176 auto idx = ctx.mModules.size();
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -0700177 ctx.mModules.push_back({module10, halRev});
178 ALOGI("loaded broadcast radio module %zu: %s:%s (HAL 1.%d)",
179 idx, serviceName.c_str(), V1_0::toString(clazz).c_str(), halMinor);
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700180
181 JavaRef<jobject> jModule = nullptr;
182 Result halResult = Result::OK;
183 Return<void> hidlResult;
184 if (module11 != nullptr) {
185 hidlResult = module11->getProperties_1_1([&](const V1_1::Properties& properties) {
186 jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
187 });
188 } else {
189 hidlResult = module10->getProperties([&](Result result,
190 const V1_0::Properties& properties) {
191 halResult = result;
192 if (result != Result::OK) return;
193 jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
194 });
195 }
196 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr;
197
198 env->CallBooleanMethod(jModules.get(), gjni.ArrayList.add, jModule.get());
199 }
200 }
201
202 return jModules.release();
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700203}
204
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700205static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,
206 jobject bandConfig, bool withAudio, jobject callback) {
Tomasz Wasilczyke3e8f902017-07-26 09:11:05 -0700207 ALOGV("%s", __func__);
208 lock_guard<mutex> lk(gContextMutex);
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700209 auto& ctx = getNativeContext(nativeContext);
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700210
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700211 if (callback == nullptr) {
212 ALOGE("Callback is empty");
213 return nullptr;
214 }
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700215
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700216 if (moduleId < 0 || static_cast<size_t>(moduleId) >= ctx.mModules.size()) {
217 ALOGE("Invalid module ID: %d", moduleId);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700218 return nullptr;
219 }
220
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -0700221 ALOGI("Opening tuner %d", moduleId);
222 auto module = ctx.mModules[moduleId];
Tomasz Wasilczyk23837932017-05-05 08:42:10 -0700223
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -0700224 Region region;
225 BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
226
Tomasz Wasilczykcc0b4792017-06-16 10:29:27 -0700227 auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -0700228 callback, module.halRev, region, withAudio, bandConfigHal.type));
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700229 if (tuner == nullptr) {
230 ALOGE("Unable to create new tuner object.");
231 return nullptr;
232 }
233
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700234 auto tunerCb = Tuner::getNativeCallback(env, tuner);
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700235 Result halResult;
236 sp<ITuner> halTuner = nullptr;
237
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -0700238 auto hidlResult = module.radioModule->openTuner(bandConfigHal, withAudio, tunerCb,
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700239 [&](Result result, const sp<ITuner>& tuner) {
240 halResult = result;
241 halTuner = tuner;
242 });
243 if (!hidlResult.isOk() || halResult != Result::OK || halTuner == nullptr) {
244 ALOGE("Couldn't open tuner");
245 ALOGE_IF(hidlResult.isOk(), "halResult = %d", halResult);
246 ALOGE_IF(!hidlResult.isOk(), "hidlResult = %s", hidlResult.description().c_str());
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700247 return nullptr;
248 }
249
Tomasz Wasilczyk20eef7d2017-09-14 09:42:54 -0700250 Tuner::assignHalInterfaces(env, tuner, module.radioModule, halTuner);
Tomasz Wasilczyk14752372017-06-21 11:58:21 -0700251 ALOGD("Opened tuner %p", halTuner.get());
Tomasz Wasilczyk3b4465e2018-01-14 21:47:44 -0800252
253 bool isConnected = true;
254 halTuner->getConfiguration([&](Result result, const BandConfig& config) {
255 if (result == Result::OK) isConnected = config.antennaConnected;
256 });
257 if (!isConnected) {
258 tunerCb->antennaStateChange(false);
259 }
260
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700261 return tuner.release();
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700262}
263
264static const JNINativeMethod gRadioServiceMethods[] = {
265 { "nativeInit", "()J", (void*)nativeInit },
266 { "nativeFinalize", "(J)V", (void*)nativeFinalize },
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700267 { "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules },
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700268 { "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z"
Tomasz Wasilczykdf013262017-12-13 11:47:20 -0800269 "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/hal1/Tuner;",
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700270 (void*)nativeOpenTuner },
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700271};
272
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700273} // namespace BroadcastRadioService
274} // namespace BroadcastRadio
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700275} // namespace server
276
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700277void register_android_server_broadcastradio_BroadcastRadioService(JNIEnv *env) {
278 using namespace server::BroadcastRadio::BroadcastRadioService;
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700279
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700280 register_android_server_broadcastradio_convert(env);
Tomasz Wasilczyk31c8df02017-04-26 14:47:08 -0700281
Tomasz Wasilczykdf013262017-12-13 11:47:20 -0800282 auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
Tomasz Wasilczykf13b8412017-05-09 11:54:35 -0700283 gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
284 gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
Tomasz Wasilczyk8cfb0e82017-07-12 13:59:20 -0700285 "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700286
Tomasz Wasilczykd15c9df2017-06-13 10:10:36 -0700287 auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
288 gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
289 gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
290 gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
291
Tomasz Wasilczyk6b4b6462017-07-19 10:52:28 -0700292 auto res = jniRegisterNativeMethods(env,
Tomasz Wasilczykdf013262017-12-13 11:47:20 -0800293 "com/android/server/broadcastradio/hal1/BroadcastRadioService",
Tomasz Wasilczykd7c21d32017-04-17 17:02:06 -0700294 gRadioServiceMethods, NELEM(gRadioServiceMethods));
295 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
296}
297
Tomasz Wasilczyk21348172017-04-20 14:02:42 -0700298} // namespace android