Home | History | Annotate | Download | only in BroadcastRadio
      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 
    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 <>
    176 bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
    177     return __ThrowIfFailedHidl(env, hidlResult);
    178 }
    179 
    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 
    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 
    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 
    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 
    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 
    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>
    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>
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    413 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const RegionalBandConfig &config) {
    414     return BandDescriptorFromHal(env, config.bandConfig, config.region);
    415 }
    416 
    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 
    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 
    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 
    497 Direction DirectionToHal(bool directionDown) {
    498     return directionDown ? Direction::DOWN : Direction::UP;
    499 }
    500 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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