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.jni"
18 #define LOG_NDEBUG 0
19
20 #include "BroadcastRadioService.h"
21
22 #include "Tuner.h"
23 #include "convert.h"
24
25 #include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
26 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
27 #include <android/hidl/manager/1.2/IServiceManager.h>
28 #include <broadcastradio-utils-1x/Utils.h>
29 #include <core_jni_helpers.h>
30 #include <hidl/ServiceManagement.h>
31 #include <nativehelper/JNIHelp.h>
32 #include <utils/Log.h>
33
34 namespace android {
35 namespace server {
36 namespace BroadcastRadio {
37 namespace BroadcastRadioService {
38
39 using std::lock_guard;
40 using std::mutex;
41
42 using hardware::Return;
43 using hardware::hidl_string;
44 using hardware::hidl_vec;
45
46 namespace V1_0 = hardware::broadcastradio::V1_0;
47 namespace V1_1 = hardware::broadcastradio::V1_1;
48 namespace utils = hardware::broadcastradio::utils;
49
50 using V1_0::BandConfig;
51 using V1_0::Class;
52 using V1_0::ITuner;
53 using V1_0::MetaData;
54 using V1_0::ProgramInfo;
55 using V1_0::Result;
56 using utils::HalRevision;
57
58 static mutex gContextMutex;
59
60 static struct {
61 struct {
62 jclass clazz;
63 jmethodID cstor;
64 jmethodID add;
65 } ArrayList;
66 struct {
67 jclass clazz;
68 jmethodID cstor;
69 } Tuner;
70 } gjni;
71
72 struct Module {
73 sp<V1_0::IBroadcastRadio> radioModule;
74 HalRevision halRev;
75 std::vector<hardware::broadcastradio::V1_0::BandConfig> bands;
76 };
77
78 struct ServiceContext {
ServiceContextandroid::server::BroadcastRadio::BroadcastRadioService::ServiceContext79 ServiceContext() {}
80
81 std::vector<Module> mModules;
82
83 private:
84 DISALLOW_COPY_AND_ASSIGN(ServiceContext);
85 };
86
87 const std::vector<Class> gAllClasses = {
88 Class::AM_FM,
89 Class::SAT,
90 Class::DT,
91 };
92
93
94 /**
95 * Always lock gContextMutex when using native context.
96 */
getNativeContext(jlong nativeContextHandle)97 static 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
nativeInit(JNIEnv * env,jobject obj)103 static jlong nativeInit(JNIEnv *env, jobject obj) {
104 ALOGV("%s", __func__);
105 lock_guard<mutex> lk(gContextMutex);
106
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
nativeFinalize(JNIEnv * env,jobject obj,jlong nativeContext)112 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
113 ALOGV("%s", __func__);
114 lock_guard<mutex> lk(gContextMutex);
115
116 auto ctx = reinterpret_cast<ServiceContext*>(nativeContext);
117 delete ctx;
118 }
119
nativeLoadModules(JNIEnv * env,jobject obj,jlong nativeContext)120 static jobject nativeLoadModules(JNIEnv *env, jobject obj, jlong nativeContext) {
121 ALOGV("%s", __func__);
122 lock_guard<mutex> lk(gContextMutex);
123 auto& ctx = getNativeContext(nativeContext);
124
125 // Get list of registered HIDL HAL implementations.
126 auto manager = hardware::defaultServiceManager1_2();
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->listManifestByInterface(V1_0::IBroadcastRadioFactory::descriptor,
133 [&services](const hidl_vec<hidl_string> ®istered) {
134 services = registered;
135 });
136 }
137
138 // 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());
143
144 auto factory = V1_0::IBroadcastRadioFactory::getService(serviceName);
145 if (factory == nullptr) {
146 ALOGE("can't load service %s", serviceName.c_str());
147 continue;
148 }
149
150 auto halRev = HalRevision::V1_0;
151 auto halMinor = 0;
152 if (V1_1::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr) != nullptr) {
153 halRev = HalRevision::V1_1;
154 halMinor = 1;
155 }
156
157 // Second level of scanning - that's unfortunate.
158 for (auto&& clazz : gAllClasses) {
159 sp<V1_0::IBroadcastRadio> module10 = nullptr;
160 sp<V1_1::IBroadcastRadio> module11 = nullptr;
161 factory->connectModule(clazz, [&](Result res, const sp<V1_0::IBroadcastRadio>& module) {
162 if (res == Result::OK) {
163 module10 = module;
164 module11 = V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr);
165 } else if (res != Result::INVALID_ARGUMENTS) {
166 ALOGE("couldn't load %s:%s module",
167 serviceName.c_str(), V1_0::toString(clazz).c_str());
168 }
169 });
170 if (module10 == nullptr) continue;
171
172 auto idx = ctx.mModules.size();
173 ctx.mModules.push_back({module10, halRev, {}});
174 auto& nModule = ctx.mModules[idx];
175 ALOGI("loaded broadcast radio module %zu: %s:%s (HAL 1.%d)",
176 idx, serviceName.c_str(), V1_0::toString(clazz).c_str(), halMinor);
177
178 JavaRef<jobject> jModule = nullptr;
179 Result halResult = Result::OK;
180 Return<void> hidlResult;
181 if (module11 != nullptr) {
182 hidlResult = module11->getProperties_1_1([&](const V1_1::Properties& properties) {
183 nModule.bands = properties.base.bands;
184 jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
185 });
186 } else {
187 hidlResult = module10->getProperties([&](Result result,
188 const V1_0::Properties& properties) {
189 halResult = result;
190 if (result != Result::OK) return;
191 nModule.bands = properties.bands;
192 jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
193 });
194 }
195 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr;
196
197 env->CallBooleanMethod(jModules.get(), gjni.ArrayList.add, jModule.get());
198 }
199 }
200
201 return jModules.release();
202 }
203
nativeOpenTuner(JNIEnv * env,jobject obj,long nativeContext,jint moduleId,jobject bandConfig,bool withAudio,jobject callback)204 static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,
205 jobject bandConfig, bool withAudio, jobject callback) {
206 ALOGV("%s", __func__);
207 lock_guard<mutex> lk(gContextMutex);
208 auto& ctx = getNativeContext(nativeContext);
209
210 if (callback == nullptr) {
211 ALOGE("Callback is empty");
212 return nullptr;
213 }
214
215 if (moduleId < 0 || static_cast<size_t>(moduleId) >= ctx.mModules.size()) {
216 ALOGE("Invalid module ID: %d", moduleId);
217 return nullptr;
218 }
219
220 ALOGI("Opening tuner %d", moduleId);
221 auto module = ctx.mModules[moduleId];
222
223 Region region;
224 BandConfig bandConfigHal;
225 if (bandConfig != nullptr) {
226 bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
227 } else {
228 region = Region::INVALID;
229 if (module.bands.size() == 0) {
230 ALOGE("No bands defined");
231 return nullptr;
232 }
233 bandConfigHal = module.bands[0];
234
235 /* Prefer FM to workaround possible program list fetching limitation
236 * (if tuner scans only configured band for programs). */
237 auto fmIt = std::find_if(module.bands.begin(), module.bands.end(),
238 [](const BandConfig & band) { return utils::isFm(band.type); });
239 if (fmIt != module.bands.end()) bandConfigHal = *fmIt;
240
241 if (bandConfigHal.spacings.size() > 1) {
242 bandConfigHal.spacings = hidl_vec<uint32_t>({ *std::min_element(
243 bandConfigHal.spacings.begin(), bandConfigHal.spacings.end()) });
244 }
245 }
246
247 auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
248 callback, module.halRev, region, withAudio, bandConfigHal.type));
249 if (tuner == nullptr) {
250 ALOGE("Unable to create new tuner object.");
251 return nullptr;
252 }
253
254 auto tunerCb = Tuner::getNativeCallback(env, tuner);
255 Result halResult;
256 sp<ITuner> halTuner = nullptr;
257
258 auto hidlResult = module.radioModule->openTuner(bandConfigHal, withAudio, tunerCb,
259 [&](Result result, const sp<ITuner>& tuner) {
260 halResult = result;
261 halTuner = tuner;
262 });
263 if (!hidlResult.isOk() || halResult != Result::OK || halTuner == nullptr) {
264 ALOGE("Couldn't open tuner");
265 ALOGE_IF(hidlResult.isOk(), "halResult = %d", halResult);
266 ALOGE_IF(!hidlResult.isOk(), "hidlResult = %s", hidlResult.description().c_str());
267 return nullptr;
268 }
269
270 Tuner::assignHalInterfaces(env, tuner, module.radioModule, halTuner);
271 ALOGD("Opened tuner %p", halTuner.get());
272
273 bool isConnected = true;
274 halTuner->getConfiguration([&](Result result, const BandConfig& config) {
275 if (result == Result::OK) isConnected = config.antennaConnected;
276 });
277 if (!isConnected) {
278 tunerCb->antennaStateChange(false);
279 }
280
281 return tuner.release();
282 }
283
284 static const JNINativeMethod gRadioServiceMethods[] = {
285 { "nativeInit", "()J", (void*)nativeInit },
286 { "nativeFinalize", "(J)V", (void*)nativeFinalize },
287 { "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules },
288 { "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z"
289 "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/hal1/Tuner;",
290 (void*)nativeOpenTuner },
291 };
292
293 } // namespace BroadcastRadioService
294 } // namespace BroadcastRadio
295 } // namespace server
296
register_android_server_broadcastradio_BroadcastRadioService(JNIEnv * env)297 void register_android_server_broadcastradio_BroadcastRadioService(JNIEnv *env) {
298 using namespace server::BroadcastRadio::BroadcastRadioService;
299
300 register_android_server_broadcastradio_convert(env);
301
302 auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
303 gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
304 gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
305 "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
306
307 auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
308 gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
309 gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
310 gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
311
312 auto res = jniRegisterNativeMethods(env,
313 "com/android/server/broadcastradio/hal1/BroadcastRadioService",
314 gRadioServiceMethods, NELEM(gRadioServiceMethods));
315 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
316 }
317
318 } // namespace android
319