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