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.convert.jni"
18 #define LOG_NDEBUG 0
19 
20 #include "convert.h"
21 
22 #include "regions.h"
23 
24 #include <broadcastradio-utils-1x/Utils.h>
25 #include <core_jni_helpers.h>
26 #include <nativehelper/JNIHelp.h>
27 #include <utils/Log.h>
28 
29 namespace android {
30 namespace server {
31 namespace BroadcastRadio {
32 namespace convert {
33 
34 namespace utils = hardware::broadcastradio::utils;
35 
36 using hardware::Return;
37 using hardware::hidl_string;
38 using hardware::hidl_vec;
39 using regions::RegionalBandConfig;
40 
41 using V1_0::Band;
42 using V1_0::Deemphasis;
43 using V1_0::Direction;
44 using V1_0::MetadataType;
45 using V1_0::Result;
46 using V1_0::Rds;
47 using V1_1::ProgramIdentifier;
48 using V1_1::ProgramListResult;
49 using V1_1::ProgramSelector;
50 using V1_1::VendorKeyValue;
51 
52 // HAL 2.0 flags that have equivalent HAL 1.x fields
53 enum class ProgramInfoFlagsExt {
54     TUNED = 1 << 4,
55     STEREO = 1 << 5,
56 };
57 
58 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const RegionalBandConfig &config);
59 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region);
60 
61 static struct {
62     struct {
63         jfieldID descriptor;
64     } BandConfig;
65     struct {
66         jclass clazz;
67         jmethodID cstor;
68         jfieldID stereo;
69         jfieldID rds;
70         jfieldID ta;
71         jfieldID af;
72         jfieldID ea;
73     } FmBandConfig;
74     struct {
75         jclass clazz;
76         jmethodID cstor;
77         jfieldID stereo;
78     } AmBandConfig;
79 
80     struct {
81         jclass clazz;
82         jfieldID region;
83         jfieldID type;
84         jfieldID lowerLimit;
85         jfieldID upperLimit;
86         jfieldID spacing;
87     } BandDescriptor;
88     struct {
89         jclass clazz;
90         jmethodID cstor;
91     } FmBandDescriptor;
92     struct {
93         jclass clazz;
94         jmethodID cstor;
95     } AmBandDescriptor;
96 
97     struct {
98         jclass clazz;
99         jmethodID stringMapToNative;
100     } Convert;
101 
102     struct {
103         jclass clazz;
104         jmethodID cstor;
105     } HashMap;
106 
107     struct {
108         jmethodID get;
109         jmethodID size;
110     } List;
111 
112     struct {
113         jmethodID put;
114     } Map;
115 
116     struct {
117         jclass clazz;
118         jmethodID cstor;
119     } ModuleProperties;
120 
121     struct {
122         jclass clazz;
123         jmethodID cstor;
124     } ProgramInfo;
125 
126     struct {
127         jclass clazz;
128         jmethodID cstor;
129         jfieldID programType;
130         jfieldID primaryId;
131         jfieldID secondaryIds;
132         jfieldID vendorIds;
133 
134         struct {
135             jclass clazz;
136             jmethodID cstor;
137             jfieldID type;
138             jfieldID value;
139         } Identifier;
140     } ProgramSelector;
141 
142     struct {
143         jclass clazz;
144         jmethodID cstor;
145         jmethodID putIntFromNative;
146         jmethodID putStringFromNative;
147         jmethodID putBitmapFromNative;
148         jmethodID putClockFromNative;
149     } RadioMetadata;
150 
151     struct {
152         jclass clazz;
153         jmethodID cstor;
154     } RuntimeException;
155 
156     struct {
157         jclass clazz;
158         jmethodID cstor;
159     } ParcelableException;
160 
161     struct {
162         jclass clazz;
163     } String;
164 } gjni;
165 
CastToString(JNIEnv * env,jobject obj)166 static jstring CastToString(JNIEnv *env, jobject obj) {
167     if (env->IsInstanceOf(obj, gjni.String.clazz)) {
168         return static_cast<jstring>(obj);
169     } else {
170         ALOGE("Cast failed, object is not a string");
171         return nullptr;
172     }
173 }
174 
175 template <>
ThrowIfFailed(JNIEnv * env,const hardware::Return<void> & hidlResult)176 bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
177     return __ThrowIfFailedHidl(env, hidlResult);
178 }
179 
__ThrowIfFailedHidl(JNIEnv * env,const hardware::details::return_status & hidlResult)180 bool __ThrowIfFailedHidl(JNIEnv *env, const hardware::details::return_status &hidlResult) {
181     if (hidlResult.isOk()) return false;
182 
183     ThrowParcelableRuntimeException(env, "HIDL call failed: " + hidlResult.description());
184     return true;
185 }
186 
__ThrowIfFailed(JNIEnv * env,const Result halResult)187 bool __ThrowIfFailed(JNIEnv *env, const Result halResult) {
188     switch (halResult) {
189         case Result::OK:
190             return false;
191         case Result::NOT_INITIALIZED:
192             ThrowParcelableRuntimeException(env, "Result::NOT_INITIALIZED");
193             return true;
194         case Result::INVALID_ARGUMENTS:
195             jniThrowException(env, "java/lang/IllegalArgumentException",
196                     "Result::INVALID_ARGUMENTS");
197             return true;
198         case Result::INVALID_STATE:
199             jniThrowException(env, "java/lang/IllegalStateException", "Result::INVALID_STATE");
200             return true;
201         case Result::TIMEOUT:
202             ThrowParcelableRuntimeException(env, "Result::TIMEOUT (unexpected here)");
203             return true;
204         default:
205             ThrowParcelableRuntimeException(env, "Unknown failure, result: "
206                     + std::to_string(static_cast<int32_t>(halResult)));
207             return true;
208     }
209 }
210 
__ThrowIfFailed(JNIEnv * env,const ProgramListResult halResult)211 bool __ThrowIfFailed(JNIEnv *env, const ProgramListResult halResult) {
212     switch (halResult) {
213         case ProgramListResult::NOT_READY:
214             jniThrowException(env, "java/lang/IllegalStateException", "Scan is in progress");
215             return true;
216         case ProgramListResult::NOT_STARTED:
217             jniThrowException(env, "java/lang/IllegalStateException", "Scan has not been started");
218             return true;
219         case ProgramListResult::UNAVAILABLE:
220             ThrowParcelableRuntimeException(env,
221                     "ProgramListResult::UNAVAILABLE (unexpected here)");
222             return true;
223         default:
224             return __ThrowIfFailed(env, static_cast<Result>(halResult));
225     }
226 }
227 
ThrowParcelableRuntimeException(JNIEnv * env,const std::string & msg)228 void ThrowParcelableRuntimeException(JNIEnv *env, const std::string& msg) {
229     auto jMsg = make_javastr(env, msg);
230     auto runtimeExc = make_javaref(env, env->NewObject(gjni.RuntimeException.clazz,
231             gjni.RuntimeException.cstor, jMsg.get()));
232     auto parcelableExc = make_javaref(env, env->NewObject(gjni.ParcelableException.clazz,
233             gjni.ParcelableException.cstor, runtimeExc.get()));
234 
235     auto res = env->Throw(static_cast<jthrowable>(parcelableExc.get()));
236     ALOGE_IF(res != JNI_OK, "Couldn't throw parcelable runtime exception");
237 }
238 
ArrayFromHal(JNIEnv * env,const hidl_vec<uint32_t> & vec)239 static JavaRef<jintArray> ArrayFromHal(JNIEnv *env, const hidl_vec<uint32_t>& vec) {
240     auto jArr = make_javaref(env, env->NewIntArray(vec.size()));
241     auto jArrElements = env->GetIntArrayElements(jArr.get(), nullptr);
242     for (size_t i = 0; i < vec.size(); i++) {
243         jArrElements[i] = vec[i];
244     }
245     env->ReleaseIntArrayElements(jArr.get(), jArrElements, 0);
246     return jArr;
247 }
248 
ArrayFromHal(JNIEnv * env,const hidl_vec<uint64_t> & vec)249 static JavaRef<jlongArray> ArrayFromHal(JNIEnv *env, const hidl_vec<uint64_t>& vec) {
250     auto jArr = make_javaref(env, env->NewLongArray(vec.size()));
251     auto jArrElements = env->GetLongArrayElements(jArr.get(), nullptr);
252     for (size_t i = 0; i < vec.size(); i++) {
253         jArrElements[i] = vec[i];
254     }
255     env->ReleaseLongArrayElements(jArr.get(), jArrElements, 0);
256     return jArr;
257 }
258 
259 template <typename T>
ArrayFromHal(JNIEnv * env,const hidl_vec<T> & vec,jclass jElementClass,std::function<JavaRef<jobject> (JNIEnv *,const T &)> converter)260 static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
261         jclass jElementClass, std::function<JavaRef<jobject>(JNIEnv*, const T&)> converter) {
262     auto jArr = make_javaref(env, env->NewObjectArray(vec.size(), jElementClass, nullptr));
263     for (size_t i = 0; i < vec.size(); i++) {
264         auto jElement = converter(env, vec[i]);
265         env->SetObjectArrayElement(jArr.get(), i, jElement.get());
266     }
267     return jArr;
268 }
269 
270 template <typename T>
ArrayFromHal(JNIEnv * env,const hidl_vec<T> & vec,jclass jElementClass,JavaRef<jobject> (* converter)(JNIEnv *,const T &))271 static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
272         jclass jElementClass, JavaRef<jobject>(*converter)(JNIEnv*, const T&)) {
273     return ArrayFromHal(env, vec, jElementClass,
274             std::function<JavaRef<jobject>(JNIEnv*, const T&)>(converter));
275 }
276 
StringFromJava(JNIEnv * env,JavaRef<jstring> & jStr)277 static std::string StringFromJava(JNIEnv *env, JavaRef<jstring> &jStr) {
278     if (jStr == nullptr) return {};
279     auto cstr = env->GetStringUTFChars(jStr.get(), nullptr);
280     std::string str(cstr);
281     env->ReleaseStringUTFChars(jStr.get(), cstr);
282     return str;
283 }
284 
StringListToHal(JNIEnv * env,jobject jList)285 hidl_vec<hidl_string> StringListToHal(JNIEnv *env, jobject jList) {
286     auto len = (jList == nullptr) ? 0 : env->CallIntMethod(jList, gjni.List.size);
287     hidl_vec<hidl_string> list(len);
288 
289     for (decltype(len) i = 0; i < len; i++) {
290         auto jString = make_javaref(env, CastToString(env, env->CallObjectMethod(
291                 jList, gjni.List.get, i)));
292         list[i] = StringFromJava(env, jString);
293     }
294 
295     return list;
296 }
297 
VendorInfoFromHal(JNIEnv * env,const hidl_vec<VendorKeyValue> & info)298 JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hidl_vec<VendorKeyValue> &info) {
299     ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
300 
301     auto jInfo = make_javaref(env, env->NewObject(gjni.HashMap.clazz, gjni.HashMap.cstor));
302 
303     for (auto&& entry : info) {
304         auto jKey = make_javastr(env, entry.key);
305         auto jValue = make_javastr(env, entry.value);
306         env->CallObjectMethod(jInfo.get(), gjni.Map.put, jKey.get(), jValue.get());
307     }
308 
309     return jInfo;
310 }
311 
VendorInfoToHal(JNIEnv * env,jobject jInfo)312 hidl_vec<VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo) {
313     ALOGV("%s", __func__);
314 
315     auto jInfoArr = make_javaref(env, static_cast<jobjectArray>(env->CallStaticObjectMethod(
316             gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo)));
317     if (jInfoArr == nullptr) {
318         ALOGE("Converted array is null");
319         return {};
320     }
321 
322     auto len = env->GetArrayLength(jInfoArr.get());
323     hidl_vec<VendorKeyValue> vec;
324     vec.resize(len);
325 
326     for (jsize i = 0; i < len; i++) {
327         auto entry = make_javaref(env, static_cast<jobjectArray>(
328                 env->GetObjectArrayElement(jInfoArr.get(), i)));
329         auto jKey = make_javaref(env, static_cast<jstring>(
330                 env->GetObjectArrayElement(entry.get(), 0)));
331         auto jValue = make_javaref(env, static_cast<jstring>(
332                 env->GetObjectArrayElement(entry.get(), 1)));
333         auto key = StringFromJava(env, jKey);
334         auto value = StringFromJava(env, jValue);
335         vec[i] = { key, value };
336     }
337 
338     return vec;
339 }
340 
RdsForRegion(bool rds,Region region)341 static Rds RdsForRegion(bool rds, Region region) {
342     if (!rds) return Rds::NONE;
343 
344     switch(region) {
345         case Region::ITU_1:
346         case Region::OIRT:
347         case Region::JAPAN:
348         case Region::KOREA:
349             return Rds::WORLD;
350         case Region::ITU_2:
351             return Rds::US;
352         default:
353             ALOGE("Unexpected region: %d", region);
354             return Rds::NONE;
355     }
356 }
357 
DeemphasisForRegion(Region region)358 static Deemphasis DeemphasisForRegion(Region region) {
359     switch(region) {
360         case Region::KOREA:
361         case Region::ITU_2:
362             return Deemphasis::D75;
363         case Region::ITU_1:
364         case Region::OIRT:
365         case Region::JAPAN:
366             return Deemphasis::D50;
367         default:
368             ALOGE("Unexpected region: %d", region);
369             return Deemphasis::D50;
370     }
371 }
372 
ModulePropertiesFromHal(JNIEnv * env,const V1_0::Properties & prop10,const V1_1::Properties * prop11,jint moduleId,const std::string & serviceName)373 static JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &prop10,
374         const V1_1::Properties *prop11, jint moduleId, const std::string& serviceName) {
375     ALOGV("%s", __func__);
376     using namespace std::placeholders;
377 
378     auto jServiceName = make_javastr(env, serviceName);
379     auto jImplementor = make_javastr(env, prop10.implementor);
380     auto jProduct = make_javastr(env, prop10.product);
381     auto jVersion = make_javastr(env, prop10.version);
382     auto jSerial = make_javastr(env, prop10.serial);
383     constexpr bool isInitializationRequired = true;
384     bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
385     auto jVendorInfo = prop11 ? VendorInfoFromHal(env, prop11->vendorInfo) : nullptr;
386 
387     auto regionalBands = regions::mapRegions(prop10.bands);
388     auto jBands = ArrayFromHal<RegionalBandConfig>(env, regionalBands,
389             gjni.BandDescriptor.clazz, BandDescriptorFromHal);
390     auto jSupportedProgramTypes =
391             prop11 ? ArrayFromHal(env, prop11->supportedProgramTypes) : nullptr;
392     auto jSupportedIdentifierTypes =
393             prop11 ? ArrayFromHal(env, prop11->supportedIdentifierTypes) : nullptr;
394 
395     return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz,
396             gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
397             jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
398             prop10.numAudioSources, isInitializationRequired, prop10.supportsCapture, jBands.get(),
399             isBgScanSupported, jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(),
400             nullptr, jVendorInfo.get()));
401 }
402 
ModulePropertiesFromHal(JNIEnv * env,const V1_0::Properties & properties,jint moduleId,const std::string & serviceName)403 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
404         jint moduleId, const std::string& serviceName) {
405     return ModulePropertiesFromHal(env, properties, nullptr, moduleId, serviceName);
406 }
407 
ModulePropertiesFromHal(JNIEnv * env,const V1_1::Properties & properties,jint moduleId,const std::string & serviceName)408 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_1::Properties &properties,
409         jint moduleId, const std::string& serviceName) {
410     return ModulePropertiesFromHal(env, properties.base, &properties, moduleId, serviceName);
411 }
412 
BandDescriptorFromHal(JNIEnv * env,const RegionalBandConfig & config)413 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const RegionalBandConfig &config) {
414     return BandDescriptorFromHal(env, config.bandConfig, config.region);
415 }
416 
BandDescriptorFromHal(JNIEnv * env,const V1_0::BandConfig & config,Region region)417 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region) {
418     ALOGV("%s", __func__);
419 
420     jint spacing = config.spacings.size() > 0 ? config.spacings[0] : 0;
421     ALOGW_IF(config.spacings.size() > 1, "Multiple spacings - not a regional config");
422     ALOGW_IF(config.spacings.size() == 0, "No channel spacing specified");
423 
424     if (utils::isFm(config.type)) {
425         auto& fm = config.ext.fm;
426         return make_javaref(env, env->NewObject(
427                 gjni.FmBandDescriptor.clazz, gjni.FmBandDescriptor.cstor,
428                 region, config.type, config.lowerLimit, config.upperLimit, spacing,
429                 fm.stereo, fm.rds != Rds::NONE, fm.ta, fm.af, fm.ea));
430     } else if (utils::isAm(config.type)) {
431         auto& am = config.ext.am;
432         return make_javaref(env, env->NewObject(
433                 gjni.AmBandDescriptor.clazz, gjni.AmBandDescriptor.cstor,
434                 region, config.type, config.lowerLimit, config.upperLimit, spacing, am.stereo));
435     } else {
436         ALOGE("Unsupported band type: %d", config.type);
437         return nullptr;
438     }
439 }
440 
BandConfigFromHal(JNIEnv * env,const V1_0::BandConfig & config,Region region)441 JavaRef<jobject> BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region) {
442     ALOGV("%s", __func__);
443 
444     auto descriptor = BandDescriptorFromHal(env, config, region);
445     if (descriptor == nullptr) return nullptr;
446 
447     if (utils::isFm(config.type)) {
448         return make_javaref(env, env->NewObject(
449                 gjni.FmBandConfig.clazz, gjni.FmBandConfig.cstor, descriptor.get()));
450     } else if (utils::isAm(config.type)) {
451         return make_javaref(env, env->NewObject(
452                 gjni.AmBandConfig.clazz, gjni.AmBandConfig.cstor, descriptor.get()));
453     } else {
454         ALOGE("Unsupported band type: %d", config.type);
455         return nullptr;
456     }
457 }
458 
BandConfigToHal(JNIEnv * env,jobject jConfig,Region & region)459 V1_0::BandConfig BandConfigToHal(JNIEnv *env, jobject jConfig, Region &region) {
460     ALOGV("%s", __func__);
461     auto jDescriptor = env->GetObjectField(jConfig, gjni.BandConfig.descriptor);
462     if (jDescriptor == nullptr) {
463         ALOGE("Descriptor is missing");
464         return {};
465     }
466 
467     region = static_cast<Region>(env->GetIntField(jDescriptor, gjni.BandDescriptor.region));
468 
469     V1_0::BandConfig config = {};
470     config.type = static_cast<Band>(env->GetIntField(jDescriptor, gjni.BandDescriptor.type));
471     config.antennaConnected = false;  // just don't set it
472     config.lowerLimit = env->GetIntField(jDescriptor, gjni.BandDescriptor.lowerLimit);
473     config.upperLimit = env->GetIntField(jDescriptor, gjni.BandDescriptor.upperLimit);
474     config.spacings = hidl_vec<uint32_t>({
475         static_cast<uint32_t>(env->GetIntField(jDescriptor, gjni.BandDescriptor.spacing))
476     });
477 
478     if (env->IsInstanceOf(jConfig, gjni.FmBandConfig.clazz)) {
479         auto& fm = config.ext.fm;
480         fm.deemphasis = DeemphasisForRegion(region);
481         fm.stereo = env->GetBooleanField(jConfig, gjni.FmBandConfig.stereo);
482         fm.rds = RdsForRegion(env->GetBooleanField(jConfig, gjni.FmBandConfig.rds), region);
483         fm.ta = env->GetBooleanField(jConfig, gjni.FmBandConfig.ta);
484         fm.af = env->GetBooleanField(jConfig, gjni.FmBandConfig.af);
485         fm.ea = env->GetBooleanField(jConfig, gjni.FmBandConfig.ea);
486     } else if (env->IsInstanceOf(jConfig, gjni.AmBandConfig.clazz)) {
487         auto& am = config.ext.am;
488         am.stereo = env->GetBooleanField(jConfig, gjni.AmBandConfig.stereo);
489     } else {
490         ALOGE("Unexpected band config type");
491         return {};
492     }
493 
494     return config;
495 }
496 
DirectionToHal(bool directionDown)497 Direction DirectionToHal(bool directionDown) {
498     return directionDown ? Direction::DOWN : Direction::UP;
499 }
500 
MetadataFromHal(JNIEnv * env,const hidl_vec<V1_0::MetaData> & metadata)501 JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hidl_vec<V1_0::MetaData> &metadata) {
502     ALOGV("%s", __func__);
503     if (metadata.size() == 0) return nullptr;
504 
505     auto jMetadata = make_javaref(env, env->NewObject(
506             gjni.RadioMetadata.clazz, gjni.RadioMetadata.cstor));
507 
508     for (auto& item : metadata) {
509         jint key = static_cast<jint>(item.key);
510         jint status = 0;
511         switch (item.type) {
512             case MetadataType::INT:
513                 status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putIntFromNative,
514                         key, item.intValue);
515                 break;
516             case MetadataType::TEXT: {
517                 auto value = make_javastr(env, item.stringValue);
518                 status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putStringFromNative,
519                         key, value.get());
520                 break;
521             }
522             case MetadataType::RAW: {
523                 auto len = item.rawValue.size();
524                 if (len == 0) break;
525                 auto value = make_javaref(env, env->NewByteArray(len));
526                 if (value == nullptr) {
527                     ALOGE("Failed to allocate byte array of len %zu", len);
528                     break;
529                 }
530                 env->SetByteArrayRegion(value.get(), 0, len,
531                         reinterpret_cast<const jbyte*>(item.rawValue.data()));
532                 status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putBitmapFromNative,
533                         key, value.get());
534                 break;
535             }
536             case MetadataType::CLOCK:
537                 status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putClockFromNative,
538                         key, item.clockValue.utcSecondsSinceEpoch,
539                         item.clockValue.timezoneOffsetInMinutes);
540                 break;
541             default:
542                 ALOGW("invalid metadata type %d", item.type);
543         }
544         ALOGE_IF(status != 0, "Failed inserting metadata %d (of type %d)", key, item.type);
545     }
546 
547     return jMetadata;
548 }
549 
ProgramIdentifierFromHal(JNIEnv * env,const ProgramIdentifier & id)550 static JavaRef<jobject> ProgramIdentifierFromHal(JNIEnv *env, const ProgramIdentifier &id) {
551     ALOGV("%s", __func__);
552     return make_javaref(env, env->NewObject(gjni.ProgramSelector.Identifier.clazz,
553             gjni.ProgramSelector.Identifier.cstor, id.type, id.value));
554 }
555 
ProgramSelectorFromHal(JNIEnv * env,const ProgramSelector & selector)556 static JavaRef<jobject> ProgramSelectorFromHal(JNIEnv *env, const ProgramSelector &selector) {
557     ALOGV("%s", __func__);
558     auto jPrimary = ProgramIdentifierFromHal(env, selector.primaryId);
559     auto jSecondary = ArrayFromHal(env, selector.secondaryIds,
560             gjni.ProgramSelector.Identifier.clazz, ProgramIdentifierFromHal);
561     auto jVendor = ArrayFromHal(env, selector.vendorIds);
562 
563     return make_javaref(env, env->NewObject(gjni.ProgramSelector.clazz, gjni.ProgramSelector.cstor,
564             selector.programType, jPrimary.get(), jSecondary.get(), jVendor.get()));
565 }
566 
ProgramIdentifierToHal(JNIEnv * env,jobject jId)567 static ProgramIdentifier ProgramIdentifierToHal(JNIEnv *env, jobject jId) {
568     ALOGV("%s", __func__);
569 
570     ProgramIdentifier id = {};
571     id.type = env->GetIntField(jId, gjni.ProgramSelector.Identifier.type);
572     id.value = env->GetLongField(jId, gjni.ProgramSelector.Identifier.value);
573     return id;
574 }
575 
ProgramSelectorToHal(JNIEnv * env,jobject jSelector)576 ProgramSelector ProgramSelectorToHal(JNIEnv *env, jobject jSelector) {
577     ALOGV("%s", __func__);
578 
579     ProgramSelector selector = {};
580 
581     selector.programType = env->GetIntField(jSelector, gjni.ProgramSelector.programType);
582 
583     auto jPrimary = env->GetObjectField(jSelector, gjni.ProgramSelector.primaryId);
584     auto jSecondary = reinterpret_cast<jobjectArray>(
585             env->GetObjectField(jSelector, gjni.ProgramSelector.secondaryIds));
586     auto jVendor = reinterpret_cast<jlongArray>(
587             env->GetObjectField(jSelector, gjni.ProgramSelector.vendorIds));
588 
589     if (jPrimary == nullptr || jSecondary == nullptr || jVendor == nullptr) {
590         ALOGE("ProgramSelector object is incomplete");
591         return {};
592     }
593 
594     selector.primaryId = ProgramIdentifierToHal(env, jPrimary);
595     auto count = env->GetArrayLength(jSecondary);
596     selector.secondaryIds.resize(count);
597     for (jsize i = 0; i < count; i++) {
598         auto jId = env->GetObjectArrayElement(jSecondary, i);
599         selector.secondaryIds[i] = ProgramIdentifierToHal(env, jId);
600     }
601 
602     count = env->GetArrayLength(jVendor);
603     selector.vendorIds.resize(count);
604     auto jVendorElements = env->GetLongArrayElements(jVendor, nullptr);
605     for (jint i = 0; i < count; i++) {
606         selector.vendorIds[i] = jVendorElements[i];
607     }
608     env->ReleaseLongArrayElements(jVendor, jVendorElements, 0);
609 
610     return selector;
611 }
612 
ProgramInfoFromHal(JNIEnv * env,const V1_0::ProgramInfo & info10,const V1_1::ProgramInfo * info11,const ProgramSelector & selector)613 static JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info10,
614         const V1_1::ProgramInfo *info11, const ProgramSelector &selector) {
615     ALOGV("%s", __func__);
616 
617     auto jMetadata = MetadataFromHal(env, info10.metadata);
618     auto jVendorInfo = info11 ? VendorInfoFromHal(env, info11->vendorInfo) : nullptr;
619     auto jSelector = ProgramSelectorFromHal(env, selector);
620 
621     jint flags = info11 ? info11->flags : 0;
622     if (info10.tuned) flags |= static_cast<jint>(ProgramInfoFlagsExt::TUNED);
623     if (info10.stereo) flags |= static_cast<jint>(ProgramInfoFlagsExt::STEREO);
624     // info10.digital is dropped, because it has no equivalent in the new APIs
625 
626     return make_javaref(env, env->NewObject(gjni.ProgramInfo.clazz, gjni.ProgramInfo.cstor,
627             jSelector.get(), nullptr, nullptr, nullptr, flags, info10.signalStrength,
628             jMetadata.get(), jVendorInfo.get()));
629 }
630 
ProgramInfoFromHal(JNIEnv * env,const V1_0::ProgramInfo & info,V1_0::Band band)631 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info, V1_0::Band band) {
632     auto selector = utils::make_selector(band, info.channel, info.subChannel);
633     return ProgramInfoFromHal(env, info, nullptr, selector);
634 }
635 
ProgramInfoFromHal(JNIEnv * env,const V1_1::ProgramInfo & info)636 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info) {
637     return ProgramInfoFromHal(env, info.base, &info, info.selector);
638 }
639 
640 } // namespace convert
641 } // namespace BroadcastRadio
642 } // namespace server
643 
register_android_server_broadcastradio_convert(JNIEnv * env)644 void register_android_server_broadcastradio_convert(JNIEnv *env) {
645     using namespace server::BroadcastRadio::convert;
646 
647     auto bandConfigClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$BandConfig");
648     gjni.BandConfig.descriptor = GetFieldIDOrDie(env, bandConfigClass,
649             "mDescriptor", "Landroid/hardware/radio/RadioManager$BandDescriptor;");
650 
651     auto fmBandConfigClass = FindClassOrDie(env,
652             "android/hardware/radio/RadioManager$FmBandConfig");
653     gjni.FmBandConfig.clazz = MakeGlobalRefOrDie(env, fmBandConfigClass);
654     gjni.FmBandConfig.cstor = GetMethodIDOrDie(env, fmBandConfigClass,
655             "<init>", "(Landroid/hardware/radio/RadioManager$FmBandDescriptor;)V");
656     gjni.FmBandConfig.stereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z");
657     gjni.FmBandConfig.rds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z");
658     gjni.FmBandConfig.ta = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z");
659     gjni.FmBandConfig.af = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z");
660     gjni.FmBandConfig.ea = GetFieldIDOrDie(env, fmBandConfigClass, "mEa", "Z");
661 
662     auto amBandConfigClass = FindClassOrDie(env,
663             "android/hardware/radio/RadioManager$AmBandConfig");
664     gjni.AmBandConfig.clazz = MakeGlobalRefOrDie(env, amBandConfigClass);
665     gjni.AmBandConfig.cstor = GetMethodIDOrDie(env, amBandConfigClass,
666             "<init>", "(Landroid/hardware/radio/RadioManager$AmBandDescriptor;)V");
667     gjni.AmBandConfig.stereo = GetFieldIDOrDie(env, amBandConfigClass, "mStereo", "Z");
668 
669     auto bandDescriptorClass = FindClassOrDie(env,
670             "android/hardware/radio/RadioManager$BandDescriptor");
671     gjni.BandDescriptor.clazz = MakeGlobalRefOrDie(env, bandDescriptorClass);
672     gjni.BandDescriptor.region = GetFieldIDOrDie(env, bandDescriptorClass, "mRegion", "I");
673     gjni.BandDescriptor.type = GetFieldIDOrDie(env, bandDescriptorClass, "mType", "I");
674     gjni.BandDescriptor.lowerLimit = GetFieldIDOrDie(env, bandDescriptorClass, "mLowerLimit", "I");
675     gjni.BandDescriptor.upperLimit = GetFieldIDOrDie(env, bandDescriptorClass, "mUpperLimit", "I");
676     gjni.BandDescriptor.spacing = GetFieldIDOrDie(env, bandDescriptorClass, "mSpacing", "I");
677 
678     auto fmBandDescriptorClass = FindClassOrDie(env,
679             "android/hardware/radio/RadioManager$FmBandDescriptor");
680     gjni.FmBandDescriptor.clazz = MakeGlobalRefOrDie(env, fmBandDescriptorClass);
681     gjni.FmBandDescriptor.cstor = GetMethodIDOrDie(env, fmBandDescriptorClass,
682             "<init>", "(IIIIIZZZZZ)V");
683 
684     auto amBandDescriptorClass = FindClassOrDie(env,
685             "android/hardware/radio/RadioManager$AmBandDescriptor");
686     gjni.AmBandDescriptor.clazz = MakeGlobalRefOrDie(env, amBandDescriptorClass);
687     gjni.AmBandDescriptor.cstor = GetMethodIDOrDie(env, amBandDescriptorClass,
688             "<init>", "(IIIIIZ)V");
689 
690     auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Convert");
691     gjni.Convert.clazz = MakeGlobalRefOrDie(env, convertClass);
692     gjni.Convert.stringMapToNative = GetStaticMethodIDOrDie(env, convertClass, "stringMapToNative",
693             "(Ljava/util/Map;)[[Ljava/lang/String;");
694 
695     auto hashMapClass = FindClassOrDie(env, "java/util/HashMap");
696     gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass);
697     gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "<init>", "()V");
698 
699     auto listClass = FindClassOrDie(env, "java/util/List");
700     gjni.List.get = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
701     gjni.List.size = GetMethodIDOrDie(env, listClass, "size", "()I");
702 
703     auto mapClass = FindClassOrDie(env, "java/util/Map");
704     gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put",
705             "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
706 
707     auto modulePropertiesClass = FindClassOrDie(env,
708             "android/hardware/radio/RadioManager$ModuleProperties");
709     gjni.ModuleProperties.clazz = MakeGlobalRefOrDie(env, modulePropertiesClass);
710     gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
711             "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
712             "Ljava/lang/String;IIZZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
713             "[I[ILjava/util/Map;Ljava/util/Map;)V");
714 
715     auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
716     gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass);
717     gjni.ProgramInfo.cstor = GetMethodIDOrDie(env, programInfoClass, "<init>", "("
718             "Landroid/hardware/radio/ProgramSelector;"
719             "Landroid/hardware/radio/ProgramSelector$Identifier;"
720             "Landroid/hardware/radio/ProgramSelector$Identifier;"
721             "Ljava/util/Collection;"  // relatedContent
722             "II"  // flags, signalQuality
723             "Landroid/hardware/radio/RadioMetadata;"
724             "Ljava/util/Map;"  // vendorInfo
725             ")V");
726 
727     auto programSelectorClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector");
728     gjni.ProgramSelector.clazz = MakeGlobalRefOrDie(env, programSelectorClass);
729     gjni.ProgramSelector.cstor = GetMethodIDOrDie(env, programSelectorClass, "<init>",
730             "(ILandroid/hardware/radio/ProgramSelector$Identifier;"
731             "[Landroid/hardware/radio/ProgramSelector$Identifier;[J)V");
732     gjni.ProgramSelector.programType = GetFieldIDOrDie(env, programSelectorClass,
733             "mProgramType", "I");
734     gjni.ProgramSelector.primaryId = GetFieldIDOrDie(env, programSelectorClass,
735             "mPrimaryId", "Landroid/hardware/radio/ProgramSelector$Identifier;");
736     gjni.ProgramSelector.secondaryIds = GetFieldIDOrDie(env, programSelectorClass,
737             "mSecondaryIds", "[Landroid/hardware/radio/ProgramSelector$Identifier;");
738     gjni.ProgramSelector.vendorIds = GetFieldIDOrDie(env, programSelectorClass,
739             "mVendorIds", "[J");
740 
741     auto progSelIdClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector$Identifier");
742     gjni.ProgramSelector.Identifier.clazz = MakeGlobalRefOrDie(env, progSelIdClass);
743     gjni.ProgramSelector.Identifier.cstor = GetMethodIDOrDie(env, progSelIdClass,
744             "<init>", "(IJ)V");
745     gjni.ProgramSelector.Identifier.type = GetFieldIDOrDie(env, progSelIdClass,
746             "mType", "I");
747     gjni.ProgramSelector.Identifier.value = GetFieldIDOrDie(env, progSelIdClass,
748             "mValue", "J");
749 
750     auto radioMetadataClass = FindClassOrDie(env, "android/hardware/radio/RadioMetadata");
751     gjni.RadioMetadata.clazz = MakeGlobalRefOrDie(env, radioMetadataClass);
752     gjni.RadioMetadata.cstor = GetMethodIDOrDie(env, radioMetadataClass, "<init>", "()V");
753     gjni.RadioMetadata.putIntFromNative = GetMethodIDOrDie(env, radioMetadataClass,
754             "putIntFromNative", "(II)I");
755     gjni.RadioMetadata.putStringFromNative = GetMethodIDOrDie(env, radioMetadataClass,
756             "putStringFromNative", "(ILjava/lang/String;)I");
757     gjni.RadioMetadata.putBitmapFromNative = GetMethodIDOrDie(env, radioMetadataClass,
758             "putBitmapFromNative", "(I[B)I");
759     gjni.RadioMetadata.putClockFromNative = GetMethodIDOrDie(env, radioMetadataClass,
760             "putClockFromNative", "(IJI)I");
761 
762     auto runtimeExcClass = FindClassOrDie(env, "java/lang/RuntimeException");
763     gjni.RuntimeException.clazz = MakeGlobalRefOrDie(env, runtimeExcClass);
764     gjni.RuntimeException.cstor = GetMethodIDOrDie(env, runtimeExcClass, "<init>",
765             "(Ljava/lang/String;)V");
766 
767     auto parcelableExcClass = FindClassOrDie(env, "android/os/ParcelableException");
768     gjni.ParcelableException.clazz = MakeGlobalRefOrDie(env, parcelableExcClass);
769     gjni.ParcelableException.cstor = GetMethodIDOrDie(env, parcelableExcClass, "<init>",
770             "(Ljava/lang/Throwable;)V");
771 
772     auto stringClass = FindClassOrDie(env, "java/lang/String");
773     gjni.String.clazz = MakeGlobalRefOrDie(env, stringClass);
774 }
775 
776 } // namespace android
777