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> &registered) {
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