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.0/IServiceManager.h>
28 #include <core_jni_helpers.h>
29 #include <hidl/ServiceManagement.h>
30 #include <nativehelper/JNIHelp.h>
31 #include <utils/Log.h>
32
33 namespace android {
34 namespace server {
35 namespace BroadcastRadio {
36 namespace BroadcastRadioService {
37
38 using std::lock_guard;
39 using std::mutex;
40
41 using hardware::Return;
42 using hardware::hidl_string;
43 using hardware::hidl_vec;
44
45 namespace V1_0 = hardware::broadcastradio::V1_0;
46 namespace V1_1 = hardware::broadcastradio::V1_1;
47
48 using V1_0::Class;
49 using V1_0::Result;
50
51 using V1_0::BandConfig;
52 using V1_0::ProgramInfo;
53 using V1_0::MetaData;
54 using V1_0::ITuner;
55
56 static mutex gContextMutex;
57
58 static struct {
59 struct {
60 jclass clazz;
61 jmethodID cstor;
62 jmethodID add;
63 } ArrayList;
64 struct {
65 jclass clazz;
66 jmethodID cstor;
67 } Tuner;
68 } gjni;
69
70 struct ServiceContext {
ServiceContextandroid::server::BroadcastRadio::BroadcastRadioService::ServiceContext71 ServiceContext() {}
72
73 std::vector<sp<V1_0::IBroadcastRadio>> mModules;
74
75 private:
76 DISALLOW_COPY_AND_ASSIGN(ServiceContext);
77 };
78
79 const std::vector<Class> gAllClasses = {
80 Class::AM_FM,
81 Class::SAT,
82 Class::DT,
83 };
84
85
86 /**
87 * Always lock gContextMutex when using native context.
88 */
getNativeContext(jlong nativeContextHandle)89 static ServiceContext& getNativeContext(jlong nativeContextHandle) {
90 auto nativeContext = reinterpret_cast<ServiceContext*>(nativeContextHandle);
91 LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
92 return *nativeContext;
93 }
94
nativeInit(JNIEnv * env,jobject obj)95 static jlong nativeInit(JNIEnv *env, jobject obj) {
96 ALOGV("%s", __func__);
97 lock_guard<mutex> lk(gContextMutex);
98
99 auto nativeContext = new ServiceContext();
100 static_assert(sizeof(jlong) >= sizeof(nativeContext), "jlong is smaller than a pointer");
101 return reinterpret_cast<jlong>(nativeContext);
102 }
103
nativeFinalize(JNIEnv * env,jobject obj,jlong nativeContext)104 static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
105 ALOGV("%s", __func__);
106 lock_guard<mutex> lk(gContextMutex);
107
108 auto ctx = reinterpret_cast<ServiceContext*>(nativeContext);
109 delete ctx;
110 }
111
nativeLoadModules(JNIEnv * env,jobject obj,jlong nativeContext)112 static jobject nativeLoadModules(JNIEnv *env, jobject obj, jlong nativeContext) {
113 ALOGV("%s", __func__);
114 lock_guard<mutex> lk(gContextMutex);
115 auto& ctx = getNativeContext(nativeContext);
116
117 // Get list of registered HIDL HAL implementations.
118 auto manager = hardware::defaultServiceManager();
119 hidl_vec<hidl_string> services;
120 if (manager == nullptr) {
121 ALOGE("Can't reach service manager, using default service implementation only");
122 services = std::vector<hidl_string>({ "default" });
123 } else {
124 manager->listByInterface(V1_0::IBroadcastRadioFactory::descriptor,
125 [&services](const hidl_vec<hidl_string> ®istered) {
126 services = registered;
127 });
128 }
129
130 // Scan provided list for actually implemented modules.
131 ctx.mModules.clear();
132 auto jModules = make_javaref(env, env->NewObject(gjni.ArrayList.clazz, gjni.ArrayList.cstor));
133 for (auto&& serviceName : services) {
134 ALOGV("checking service: %s", serviceName.c_str());
135
136 auto factory = V1_0::IBroadcastRadioFactory::getService(serviceName);
137 if (factory == nullptr) {
138 ALOGE("can't load service %s", serviceName.c_str());
139 continue;
140 }
141
142 // Second level of scanning - that's unfortunate.
143 for (auto&& clazz : gAllClasses) {
144 sp<V1_0::IBroadcastRadio> module10 = nullptr;
145 sp<V1_1::IBroadcastRadio> module11 = nullptr;
146 factory->connectModule(clazz, [&](Result res, const sp<V1_0::IBroadcastRadio>& module) {
147 if (res == Result::OK) {
148 module10 = module;
149 module11 = V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr);
150 } else if (res != Result::INVALID_ARGUMENTS) {
151 ALOGE("couldn't load %s:%s module",
152 serviceName.c_str(), V1_0::toString(clazz).c_str());
153 }
154 });
155 if (module10 == nullptr) continue;
156
157 auto idx = ctx.mModules.size();
158 ctx.mModules.push_back(module10);
159 ALOGI("loaded broadcast radio module %zu: %s:%s",
160 idx, serviceName.c_str(), V1_0::toString(clazz).c_str());
161
162 JavaRef<jobject> jModule = nullptr;
163 Result halResult = Result::OK;
164 Return<void> hidlResult;
165 if (module11 != nullptr) {
166 hidlResult = module11->getProperties_1_1([&](const V1_1::Properties& properties) {
167 jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
168 });
169 } else {
170 hidlResult = module10->getProperties([&](Result result,
171 const V1_0::Properties& properties) {
172 halResult = result;
173 if (result != Result::OK) return;
174 jModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
175 });
176 }
177 if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr;
178
179 env->CallBooleanMethod(jModules.get(), gjni.ArrayList.add, jModule.get());
180 }
181 }
182
183 return jModules.release();
184 }
185
nativeOpenTuner(JNIEnv * env,jobject obj,long nativeContext,jint moduleId,jobject bandConfig,bool withAudio,jobject callback)186 static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,
187 jobject bandConfig, bool withAudio, jobject callback) {
188 ALOGV("%s", __func__);
189 lock_guard<mutex> lk(gContextMutex);
190 auto& ctx = getNativeContext(nativeContext);
191
192 if (callback == nullptr) {
193 ALOGE("Callback is empty");
194 return nullptr;
195 }
196
197 if (moduleId < 0 || static_cast<size_t>(moduleId) >= ctx.mModules.size()) {
198 ALOGE("Invalid module ID: %d", moduleId);
199 return nullptr;
200 }
201 auto module = ctx.mModules[moduleId];
202
203 HalRevision halRev;
204 if (V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr) != nullptr) {
205 ALOGI("Opening tuner %d with broadcast radio HAL 1.1", moduleId);
206 halRev = HalRevision::V1_1;
207 } else {
208 ALOGI("Opening tuner %d with broadcast radio HAL 1.0", moduleId);
209 halRev = HalRevision::V1_0;
210 }
211
212 Region region;
213 BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
214
215 auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
216 callback, halRev, region, withAudio, bandConfigHal.type));
217 if (tuner == nullptr) {
218 ALOGE("Unable to create new tuner object.");
219 return nullptr;
220 }
221
222 auto tunerCb = Tuner::getNativeCallback(env, tuner);
223 Result halResult;
224 sp<ITuner> halTuner = nullptr;
225
226 auto hidlResult = module->openTuner(bandConfigHal, withAudio, tunerCb,
227 [&](Result result, const sp<ITuner>& tuner) {
228 halResult = result;
229 halTuner = tuner;
230 });
231 if (!hidlResult.isOk() || halResult != Result::OK || halTuner == nullptr) {
232 ALOGE("Couldn't open tuner");
233 ALOGE_IF(hidlResult.isOk(), "halResult = %d", halResult);
234 ALOGE_IF(!hidlResult.isOk(), "hidlResult = %s", hidlResult.description().c_str());
235 return nullptr;
236 }
237
238 Tuner::assignHalInterfaces(env, tuner, module, halTuner);
239 ALOGD("Opened tuner %p", halTuner.get());
240 return tuner.release();
241 }
242
243 static const JNINativeMethod gRadioServiceMethods[] = {
244 { "nativeInit", "()J", (void*)nativeInit },
245 { "nativeFinalize", "(J)V", (void*)nativeFinalize },
246 { "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules },
247 { "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z"
248 "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/Tuner;",
249 (void*)nativeOpenTuner },
250 };
251
252 } // namespace BroadcastRadioService
253 } // namespace BroadcastRadio
254 } // namespace server
255
register_android_server_broadcastradio_BroadcastRadioService(JNIEnv * env)256 void register_android_server_broadcastradio_BroadcastRadioService(JNIEnv *env) {
257 using namespace server::BroadcastRadio::BroadcastRadioService;
258
259 register_android_server_broadcastradio_convert(env);
260
261 auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner");
262 gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
263 gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
264 "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
265
266 auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
267 gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
268 gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
269 gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
270
271 auto res = jniRegisterNativeMethods(env,
272 "com/android/server/broadcastradio/BroadcastRadioService",
273 gRadioServiceMethods, NELEM(gRadioServiceMethods));
274 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
275 }
276
277 } // namespace android
278