/** * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "BroadcastRadioService.convert.jni" #define LOG_NDEBUG 0 #include "convert.h" #include "regions.h" #include #include #include #include namespace android { namespace server { namespace BroadcastRadio { namespace convert { namespace utils = hardware::broadcastradio::utils; using hardware::Return; using hardware::hidl_string; using hardware::hidl_vec; using regions::RegionalBandConfig; using V1_0::Band; using V1_0::Deemphasis; using V1_0::Direction; using V1_0::MetadataType; using V1_0::Result; using V1_0::Rds; using V1_1::ProgramIdentifier; using V1_1::ProgramListResult; using V1_1::ProgramSelector; using V1_1::VendorKeyValue; // HAL 2.0 flags that have equivalent HAL 1.x fields enum class ProgramInfoFlagsExt { TUNED = 1 << 4, STEREO = 1 << 5, }; static JavaRef BandDescriptorFromHal(JNIEnv *env, const RegionalBandConfig &config); static JavaRef BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region); static struct { struct { jfieldID descriptor; } BandConfig; struct { jclass clazz; jmethodID cstor; jfieldID stereo; jfieldID rds; jfieldID ta; jfieldID af; jfieldID ea; } FmBandConfig; struct { jclass clazz; jmethodID cstor; jfieldID stereo; } AmBandConfig; struct { jclass clazz; jfieldID region; jfieldID type; jfieldID lowerLimit; jfieldID upperLimit; jfieldID spacing; } BandDescriptor; struct { jclass clazz; jmethodID cstor; } FmBandDescriptor; struct { jclass clazz; jmethodID cstor; } AmBandDescriptor; struct { jclass clazz; jmethodID stringMapToNative; } Convert; struct { jclass clazz; jmethodID cstor; } HashMap; struct { jmethodID get; jmethodID size; } List; struct { jmethodID put; } Map; struct { jclass clazz; jmethodID cstor; } ModuleProperties; struct { jclass clazz; jmethodID cstor; } ProgramInfo; struct { jclass clazz; jmethodID cstor; jfieldID programType; jfieldID primaryId; jfieldID secondaryIds; jfieldID vendorIds; struct { jclass clazz; jmethodID cstor; jfieldID type; jfieldID value; } Identifier; } ProgramSelector; struct { jclass clazz; jmethodID cstor; jmethodID putIntFromNative; jmethodID putStringFromNative; jmethodID putBitmapFromNative; jmethodID putClockFromNative; } RadioMetadata; struct { jclass clazz; jmethodID cstor; } RuntimeException; struct { jclass clazz; jmethodID cstor; } ParcelableException; struct { jclass clazz; } String; } gjni; static jstring CastToString(JNIEnv *env, jobject obj) { if (env->IsInstanceOf(obj, gjni.String.clazz)) { return static_cast(obj); } else { ALOGE("Cast failed, object is not a string"); return nullptr; } } template <> bool ThrowIfFailed(JNIEnv *env, const hardware::Return &hidlResult) { return __ThrowIfFailedHidl(env, hidlResult); } bool __ThrowIfFailedHidl(JNIEnv *env, const hardware::details::return_status &hidlResult) { if (hidlResult.isOk()) return false; ThrowParcelableRuntimeException(env, "HIDL call failed: " + hidlResult.description()); return true; } bool __ThrowIfFailed(JNIEnv *env, const Result halResult) { switch (halResult) { case Result::OK: return false; case Result::NOT_INITIALIZED: ThrowParcelableRuntimeException(env, "Result::NOT_INITIALIZED"); return true; case Result::INVALID_ARGUMENTS: jniThrowException(env, "java/lang/IllegalArgumentException", "Result::INVALID_ARGUMENTS"); return true; case Result::INVALID_STATE: jniThrowException(env, "java/lang/IllegalStateException", "Result::INVALID_STATE"); return true; case Result::TIMEOUT: ThrowParcelableRuntimeException(env, "Result::TIMEOUT (unexpected here)"); return true; default: ThrowParcelableRuntimeException(env, "Unknown failure, result: " + std::to_string(static_cast(halResult))); return true; } } bool __ThrowIfFailed(JNIEnv *env, const ProgramListResult halResult) { switch (halResult) { case ProgramListResult::NOT_READY: jniThrowException(env, "java/lang/IllegalStateException", "Scan is in progress"); return true; case ProgramListResult::NOT_STARTED: jniThrowException(env, "java/lang/IllegalStateException", "Scan has not been started"); return true; case ProgramListResult::UNAVAILABLE: ThrowParcelableRuntimeException(env, "ProgramListResult::UNAVAILABLE (unexpected here)"); return true; default: return __ThrowIfFailed(env, static_cast(halResult)); } } void ThrowParcelableRuntimeException(JNIEnv *env, const std::string& msg) { auto jMsg = make_javastr(env, msg); auto runtimeExc = make_javaref(env, env->NewObject(gjni.RuntimeException.clazz, gjni.RuntimeException.cstor, jMsg.get())); auto parcelableExc = make_javaref(env, env->NewObject(gjni.ParcelableException.clazz, gjni.ParcelableException.cstor, runtimeExc.get())); auto res = env->Throw(static_cast(parcelableExc.get())); ALOGE_IF(res != JNI_OK, "Couldn't throw parcelable runtime exception"); } static JavaRef ArrayFromHal(JNIEnv *env, const hidl_vec& vec) { auto jArr = make_javaref(env, env->NewIntArray(vec.size())); auto jArrElements = env->GetIntArrayElements(jArr.get(), nullptr); for (size_t i = 0; i < vec.size(); i++) { jArrElements[i] = vec[i]; } env->ReleaseIntArrayElements(jArr.get(), jArrElements, 0); return jArr; } static JavaRef ArrayFromHal(JNIEnv *env, const hidl_vec& vec) { auto jArr = make_javaref(env, env->NewLongArray(vec.size())); auto jArrElements = env->GetLongArrayElements(jArr.get(), nullptr); for (size_t i = 0; i < vec.size(); i++) { jArrElements[i] = vec[i]; } env->ReleaseLongArrayElements(jArr.get(), jArrElements, 0); return jArr; } template static JavaRef ArrayFromHal(JNIEnv *env, const hidl_vec& vec, jclass jElementClass, std::function(JNIEnv*, const T&)> converter) { auto jArr = make_javaref(env, env->NewObjectArray(vec.size(), jElementClass, nullptr)); for (size_t i = 0; i < vec.size(); i++) { auto jElement = converter(env, vec[i]); env->SetObjectArrayElement(jArr.get(), i, jElement.get()); } return jArr; } template static JavaRef ArrayFromHal(JNIEnv *env, const hidl_vec& vec, jclass jElementClass, JavaRef(*converter)(JNIEnv*, const T&)) { return ArrayFromHal(env, vec, jElementClass, std::function(JNIEnv*, const T&)>(converter)); } static std::string StringFromJava(JNIEnv *env, JavaRef &jStr) { if (jStr == nullptr) return {}; auto cstr = env->GetStringUTFChars(jStr.get(), nullptr); std::string str(cstr); env->ReleaseStringUTFChars(jStr.get(), cstr); return str; } hidl_vec StringListToHal(JNIEnv *env, jobject jList) { auto len = (jList == nullptr) ? 0 : env->CallIntMethod(jList, gjni.List.size); hidl_vec list(len); for (decltype(len) i = 0; i < len; i++) { auto jString = make_javaref(env, CastToString(env, env->CallObjectMethod( jList, gjni.List.get, i))); list[i] = StringFromJava(env, jString); } return list; } JavaRef VendorInfoFromHal(JNIEnv *env, const hidl_vec &info) { ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str()); auto jInfo = make_javaref(env, env->NewObject(gjni.HashMap.clazz, gjni.HashMap.cstor)); for (auto&& entry : info) { auto jKey = make_javastr(env, entry.key); auto jValue = make_javastr(env, entry.value); env->CallObjectMethod(jInfo.get(), gjni.Map.put, jKey.get(), jValue.get()); } return jInfo; } hidl_vec VendorInfoToHal(JNIEnv *env, jobject jInfo) { ALOGV("%s", __func__); auto jInfoArr = make_javaref(env, static_cast(env->CallStaticObjectMethod( gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo))); if (jInfoArr == nullptr) { ALOGE("Converted array is null"); return {}; } auto len = env->GetArrayLength(jInfoArr.get()); hidl_vec vec; vec.resize(len); for (jsize i = 0; i < len; i++) { auto entry = make_javaref(env, static_cast( env->GetObjectArrayElement(jInfoArr.get(), i))); auto jKey = make_javaref(env, static_cast( env->GetObjectArrayElement(entry.get(), 0))); auto jValue = make_javaref(env, static_cast( env->GetObjectArrayElement(entry.get(), 1))); auto key = StringFromJava(env, jKey); auto value = StringFromJava(env, jValue); vec[i] = { key, value }; } return vec; } static Rds RdsForRegion(bool rds, Region region) { if (!rds) return Rds::NONE; switch(region) { case Region::ITU_1: case Region::OIRT: case Region::JAPAN: case Region::KOREA: return Rds::WORLD; case Region::ITU_2: return Rds::US; default: ALOGE("Unexpected region: %d", static_cast(region)); return Rds::NONE; } } static Deemphasis DeemphasisForRegion(Region region) { switch(region) { case Region::KOREA: case Region::ITU_2: return Deemphasis::D75; case Region::ITU_1: case Region::OIRT: case Region::JAPAN: return Deemphasis::D50; default: ALOGE("Unexpected region: %d", static_cast(region)); return Deemphasis::D50; } } static JavaRef ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &prop10, const V1_1::Properties *prop11, jint moduleId, const std::string& serviceName) { ALOGV("%s", __func__); using namespace std::placeholders; auto jServiceName = make_javastr(env, serviceName); auto jImplementor = make_javastr(env, prop10.implementor); auto jProduct = make_javastr(env, prop10.product); auto jVersion = make_javastr(env, prop10.version); auto jSerial = make_javastr(env, prop10.serial); constexpr bool isInitializationRequired = true; bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false; auto jVendorInfo = prop11 ? VendorInfoFromHal(env, prop11->vendorInfo) : nullptr; auto regionalBands = regions::mapRegions(prop10.bands); auto jBands = ArrayFromHal(env, regionalBands, gjni.BandDescriptor.clazz, BandDescriptorFromHal); auto jSupportedProgramTypes = prop11 ? ArrayFromHal(env, prop11->supportedProgramTypes) : nullptr; auto jSupportedIdentifierTypes = prop11 ? ArrayFromHal(env, prop11->supportedIdentifierTypes) : nullptr; return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz, gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId, jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners, prop10.numAudioSources, isInitializationRequired, prop10.supportsCapture, jBands.get(), isBgScanSupported, jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr, jVendorInfo.get())); } JavaRef ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties, jint moduleId, const std::string& serviceName) { return ModulePropertiesFromHal(env, properties, nullptr, moduleId, serviceName); } JavaRef ModulePropertiesFromHal(JNIEnv *env, const V1_1::Properties &properties, jint moduleId, const std::string& serviceName) { return ModulePropertiesFromHal(env, properties.base, &properties, moduleId, serviceName); } static JavaRef BandDescriptorFromHal(JNIEnv *env, const RegionalBandConfig &config) { return BandDescriptorFromHal(env, config.bandConfig, config.region); } static JavaRef BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region) { ALOGV("%s", __func__); jint spacing = config.spacings.size() > 0 ? config.spacings[0] : 0; ALOGW_IF(config.spacings.size() > 1, "Multiple spacings - not a regional config"); ALOGW_IF(config.spacings.size() == 0, "No channel spacing specified"); if (utils::isFm(config.type)) { auto& fm = config.ext.fm; return make_javaref(env, env->NewObject( gjni.FmBandDescriptor.clazz, gjni.FmBandDescriptor.cstor, region, config.type, config.lowerLimit, config.upperLimit, spacing, fm.stereo, fm.rds != Rds::NONE, fm.ta, fm.af, fm.ea)); } else if (utils::isAm(config.type)) { auto& am = config.ext.am; return make_javaref(env, env->NewObject( gjni.AmBandDescriptor.clazz, gjni.AmBandDescriptor.cstor, region, config.type, config.lowerLimit, config.upperLimit, spacing, am.stereo)); } else { ALOGE("Unsupported band type: %d", static_cast(config.type)); return nullptr; } } JavaRef BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region) { ALOGV("%s", __func__); auto descriptor = BandDescriptorFromHal(env, config, region); if (descriptor == nullptr) return nullptr; if (utils::isFm(config.type)) { return make_javaref(env, env->NewObject( gjni.FmBandConfig.clazz, gjni.FmBandConfig.cstor, descriptor.get())); } else if (utils::isAm(config.type)) { return make_javaref(env, env->NewObject( gjni.AmBandConfig.clazz, gjni.AmBandConfig.cstor, descriptor.get())); } else { ALOGE("Unsupported band type: %d", static_cast(config.type)); return nullptr; } } V1_0::BandConfig BandConfigToHal(JNIEnv *env, jobject jConfig, Region ®ion) { ALOGV("%s", __func__); auto jDescriptor = env->GetObjectField(jConfig, gjni.BandConfig.descriptor); if (jDescriptor == nullptr) { ALOGE("Descriptor is missing"); return {}; } region = static_cast(env->GetIntField(jDescriptor, gjni.BandDescriptor.region)); V1_0::BandConfig config = {}; config.type = static_cast(env->GetIntField(jDescriptor, gjni.BandDescriptor.type)); config.antennaConnected = false; // just don't set it config.lowerLimit = env->GetIntField(jDescriptor, gjni.BandDescriptor.lowerLimit); config.upperLimit = env->GetIntField(jDescriptor, gjni.BandDescriptor.upperLimit); config.spacings = hidl_vec({ static_cast(env->GetIntField(jDescriptor, gjni.BandDescriptor.spacing)) }); if (env->IsInstanceOf(jConfig, gjni.FmBandConfig.clazz)) { auto& fm = config.ext.fm; fm.deemphasis = DeemphasisForRegion(region); fm.stereo = env->GetBooleanField(jConfig, gjni.FmBandConfig.stereo); fm.rds = RdsForRegion(env->GetBooleanField(jConfig, gjni.FmBandConfig.rds), region); fm.ta = env->GetBooleanField(jConfig, gjni.FmBandConfig.ta); fm.af = env->GetBooleanField(jConfig, gjni.FmBandConfig.af); fm.ea = env->GetBooleanField(jConfig, gjni.FmBandConfig.ea); } else if (env->IsInstanceOf(jConfig, gjni.AmBandConfig.clazz)) { auto& am = config.ext.am; am.stereo = env->GetBooleanField(jConfig, gjni.AmBandConfig.stereo); } else { ALOGE("Unexpected band config type"); return {}; } return config; } Direction DirectionToHal(bool directionDown) { return directionDown ? Direction::DOWN : Direction::UP; } JavaRef MetadataFromHal(JNIEnv *env, const hidl_vec &metadata) { ALOGV("%s", __func__); if (metadata.size() == 0) return nullptr; auto jMetadata = make_javaref(env, env->NewObject( gjni.RadioMetadata.clazz, gjni.RadioMetadata.cstor)); for (auto& item : metadata) { jint key = static_cast(item.key); jint status = 0; switch (item.type) { case MetadataType::INT: status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putIntFromNative, key, item.intValue); break; case MetadataType::TEXT: { auto value = make_javastr(env, item.stringValue); status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putStringFromNative, key, value.get()); break; } case MetadataType::RAW: { auto len = item.rawValue.size(); if (len == 0) break; auto value = make_javaref(env, env->NewByteArray(len)); if (value == nullptr) { ALOGE("Failed to allocate byte array of len %zu", len); break; } env->SetByteArrayRegion(value.get(), 0, len, reinterpret_cast(item.rawValue.data())); status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putBitmapFromNative, key, value.get()); break; } case MetadataType::CLOCK: status = env->CallIntMethod(jMetadata.get(), gjni.RadioMetadata.putClockFromNative, key, item.clockValue.utcSecondsSinceEpoch, item.clockValue.timezoneOffsetInMinutes); break; default: ALOGW("invalid metadata type %d", static_cast(item.type)); } ALOGE_IF(status != 0, "Failed inserting metadata %d (of type %d)", key, static_cast(item.type)); } return jMetadata; } static JavaRef ProgramIdentifierFromHal(JNIEnv *env, const ProgramIdentifier &id) { ALOGV("%s", __func__); return make_javaref(env, env->NewObject(gjni.ProgramSelector.Identifier.clazz, gjni.ProgramSelector.Identifier.cstor, id.type, id.value)); } static JavaRef ProgramSelectorFromHal(JNIEnv *env, const ProgramSelector &selector) { ALOGV("%s", __func__); auto jPrimary = ProgramIdentifierFromHal(env, selector.primaryId); auto jSecondary = ArrayFromHal(env, selector.secondaryIds, gjni.ProgramSelector.Identifier.clazz, ProgramIdentifierFromHal); auto jVendor = ArrayFromHal(env, selector.vendorIds); return make_javaref(env, env->NewObject(gjni.ProgramSelector.clazz, gjni.ProgramSelector.cstor, selector.programType, jPrimary.get(), jSecondary.get(), jVendor.get())); } static ProgramIdentifier ProgramIdentifierToHal(JNIEnv *env, jobject jId) { ALOGV("%s", __func__); ProgramIdentifier id = {}; id.type = env->GetIntField(jId, gjni.ProgramSelector.Identifier.type); id.value = env->GetLongField(jId, gjni.ProgramSelector.Identifier.value); return id; } ProgramSelector ProgramSelectorToHal(JNIEnv *env, jobject jSelector) { ALOGV("%s", __func__); ProgramSelector selector = {}; selector.programType = env->GetIntField(jSelector, gjni.ProgramSelector.programType); auto jPrimary = env->GetObjectField(jSelector, gjni.ProgramSelector.primaryId); auto jSecondary = reinterpret_cast( env->GetObjectField(jSelector, gjni.ProgramSelector.secondaryIds)); auto jVendor = reinterpret_cast( env->GetObjectField(jSelector, gjni.ProgramSelector.vendorIds)); if (jPrimary == nullptr || jSecondary == nullptr || jVendor == nullptr) { ALOGE("ProgramSelector object is incomplete"); return {}; } selector.primaryId = ProgramIdentifierToHal(env, jPrimary); auto count = env->GetArrayLength(jSecondary); selector.secondaryIds.resize(count); for (jsize i = 0; i < count; i++) { auto jId = env->GetObjectArrayElement(jSecondary, i); selector.secondaryIds[i] = ProgramIdentifierToHal(env, jId); } count = env->GetArrayLength(jVendor); selector.vendorIds.resize(count); auto jVendorElements = env->GetLongArrayElements(jVendor, nullptr); for (jint i = 0; i < count; i++) { selector.vendorIds[i] = jVendorElements[i]; } env->ReleaseLongArrayElements(jVendor, jVendorElements, 0); return selector; } static JavaRef ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info10, const V1_1::ProgramInfo *info11, const ProgramSelector &selector) { ALOGV("%s", __func__); auto jMetadata = MetadataFromHal(env, info10.metadata); auto jVendorInfo = info11 ? VendorInfoFromHal(env, info11->vendorInfo) : nullptr; auto jSelector = ProgramSelectorFromHal(env, selector); jint flags = info11 ? info11->flags : 0; if (info10.tuned) flags |= static_cast(ProgramInfoFlagsExt::TUNED); if (info10.stereo) flags |= static_cast(ProgramInfoFlagsExt::STEREO); // info10.digital is dropped, because it has no equivalent in the new APIs return make_javaref(env, env->NewObject(gjni.ProgramInfo.clazz, gjni.ProgramInfo.cstor, jSelector.get(), nullptr, nullptr, nullptr, flags, info10.signalStrength, jMetadata.get(), jVendorInfo.get())); } JavaRef ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info, V1_0::Band band) { auto selector = utils::make_selector(band, info.channel, info.subChannel); return ProgramInfoFromHal(env, info, nullptr, selector); } JavaRef ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info) { return ProgramInfoFromHal(env, info.base, &info, info.selector); } } // namespace convert } // namespace BroadcastRadio } // namespace server void register_android_server_broadcastradio_convert(JNIEnv *env) { using namespace server::BroadcastRadio::convert; auto bandConfigClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$BandConfig"); gjni.BandConfig.descriptor = GetFieldIDOrDie(env, bandConfigClass, "mDescriptor", "Landroid/hardware/radio/RadioManager$BandDescriptor;"); auto fmBandConfigClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$FmBandConfig"); gjni.FmBandConfig.clazz = MakeGlobalRefOrDie(env, fmBandConfigClass); gjni.FmBandConfig.cstor = GetMethodIDOrDie(env, fmBandConfigClass, "", "(Landroid/hardware/radio/RadioManager$FmBandDescriptor;)V"); gjni.FmBandConfig.stereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z"); gjni.FmBandConfig.rds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z"); gjni.FmBandConfig.ta = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z"); gjni.FmBandConfig.af = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z"); gjni.FmBandConfig.ea = GetFieldIDOrDie(env, fmBandConfigClass, "mEa", "Z"); auto amBandConfigClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$AmBandConfig"); gjni.AmBandConfig.clazz = MakeGlobalRefOrDie(env, amBandConfigClass); gjni.AmBandConfig.cstor = GetMethodIDOrDie(env, amBandConfigClass, "", "(Landroid/hardware/radio/RadioManager$AmBandDescriptor;)V"); gjni.AmBandConfig.stereo = GetFieldIDOrDie(env, amBandConfigClass, "mStereo", "Z"); auto bandDescriptorClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$BandDescriptor"); gjni.BandDescriptor.clazz = MakeGlobalRefOrDie(env, bandDescriptorClass); gjni.BandDescriptor.region = GetFieldIDOrDie(env, bandDescriptorClass, "mRegion", "I"); gjni.BandDescriptor.type = GetFieldIDOrDie(env, bandDescriptorClass, "mType", "I"); gjni.BandDescriptor.lowerLimit = GetFieldIDOrDie(env, bandDescriptorClass, "mLowerLimit", "I"); gjni.BandDescriptor.upperLimit = GetFieldIDOrDie(env, bandDescriptorClass, "mUpperLimit", "I"); gjni.BandDescriptor.spacing = GetFieldIDOrDie(env, bandDescriptorClass, "mSpacing", "I"); auto fmBandDescriptorClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$FmBandDescriptor"); gjni.FmBandDescriptor.clazz = MakeGlobalRefOrDie(env, fmBandDescriptorClass); gjni.FmBandDescriptor.cstor = GetMethodIDOrDie(env, fmBandDescriptorClass, "", "(IIIIIZZZZZ)V"); auto amBandDescriptorClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$AmBandDescriptor"); gjni.AmBandDescriptor.clazz = MakeGlobalRefOrDie(env, amBandDescriptorClass); gjni.AmBandDescriptor.cstor = GetMethodIDOrDie(env, amBandDescriptorClass, "", "(IIIIIZ)V"); auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Convert"); gjni.Convert.clazz = MakeGlobalRefOrDie(env, convertClass); gjni.Convert.stringMapToNative = GetStaticMethodIDOrDie(env, convertClass, "stringMapToNative", "(Ljava/util/Map;)[[Ljava/lang/String;"); auto hashMapClass = FindClassOrDie(env, "java/util/HashMap"); gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass); gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "", "()V"); auto listClass = FindClassOrDie(env, "java/util/List"); gjni.List.get = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;"); gjni.List.size = GetMethodIDOrDie(env, listClass, "size", "()I"); auto mapClass = FindClassOrDie(env, "java/util/Map"); gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); auto modulePropertiesClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ModuleProperties"); gjni.ModuleProperties.clazz = MakeGlobalRefOrDie(env, modulePropertiesClass); gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;IIZZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z" "[I[ILjava/util/Map;Ljava/util/Map;)V"); auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo"); gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass); gjni.ProgramInfo.cstor = GetMethodIDOrDie(env, programInfoClass, "", "(" "Landroid/hardware/radio/ProgramSelector;" "Landroid/hardware/radio/ProgramSelector$Identifier;" "Landroid/hardware/radio/ProgramSelector$Identifier;" "Ljava/util/Collection;" // relatedContent "II" // flags, signalQuality "Landroid/hardware/radio/RadioMetadata;" "Ljava/util/Map;" // vendorInfo ")V"); auto programSelectorClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector"); gjni.ProgramSelector.clazz = MakeGlobalRefOrDie(env, programSelectorClass); gjni.ProgramSelector.cstor = GetMethodIDOrDie(env, programSelectorClass, "", "(ILandroid/hardware/radio/ProgramSelector$Identifier;" "[Landroid/hardware/radio/ProgramSelector$Identifier;[J)V"); gjni.ProgramSelector.programType = GetFieldIDOrDie(env, programSelectorClass, "mProgramType", "I"); gjni.ProgramSelector.primaryId = GetFieldIDOrDie(env, programSelectorClass, "mPrimaryId", "Landroid/hardware/radio/ProgramSelector$Identifier;"); gjni.ProgramSelector.secondaryIds = GetFieldIDOrDie(env, programSelectorClass, "mSecondaryIds", "[Landroid/hardware/radio/ProgramSelector$Identifier;"); gjni.ProgramSelector.vendorIds = GetFieldIDOrDie(env, programSelectorClass, "mVendorIds", "[J"); auto progSelIdClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector$Identifier"); gjni.ProgramSelector.Identifier.clazz = MakeGlobalRefOrDie(env, progSelIdClass); gjni.ProgramSelector.Identifier.cstor = GetMethodIDOrDie(env, progSelIdClass, "", "(IJ)V"); gjni.ProgramSelector.Identifier.type = GetFieldIDOrDie(env, progSelIdClass, "mType", "I"); gjni.ProgramSelector.Identifier.value = GetFieldIDOrDie(env, progSelIdClass, "mValue", "J"); auto radioMetadataClass = FindClassOrDie(env, "android/hardware/radio/RadioMetadata"); gjni.RadioMetadata.clazz = MakeGlobalRefOrDie(env, radioMetadataClass); gjni.RadioMetadata.cstor = GetMethodIDOrDie(env, radioMetadataClass, "", "()V"); gjni.RadioMetadata.putIntFromNative = GetMethodIDOrDie(env, radioMetadataClass, "putIntFromNative", "(II)I"); gjni.RadioMetadata.putStringFromNative = GetMethodIDOrDie(env, radioMetadataClass, "putStringFromNative", "(ILjava/lang/String;)I"); gjni.RadioMetadata.putBitmapFromNative = GetMethodIDOrDie(env, radioMetadataClass, "putBitmapFromNative", "(I[B)I"); gjni.RadioMetadata.putClockFromNative = GetMethodIDOrDie(env, radioMetadataClass, "putClockFromNative", "(IJI)I"); auto runtimeExcClass = FindClassOrDie(env, "java/lang/RuntimeException"); gjni.RuntimeException.clazz = MakeGlobalRefOrDie(env, runtimeExcClass); gjni.RuntimeException.cstor = GetMethodIDOrDie(env, runtimeExcClass, "", "(Ljava/lang/String;)V"); auto parcelableExcClass = FindClassOrDie(env, "android/os/ParcelableException"); gjni.ParcelableException.clazz = MakeGlobalRefOrDie(env, parcelableExcClass); gjni.ParcelableException.cstor = GetMethodIDOrDie(env, parcelableExcClass, "", "(Ljava/lang/Throwable;)V"); auto stringClass = FindClassOrDie(env, "java/lang/String"); gjni.String.clazz = MakeGlobalRefOrDie(env, stringClass); } } // namespace android