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 ®ion) {
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