Merge "broadcast radio JNI."
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 30a7e68..84568e4 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -140,6 +140,7 @@
android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
android_hardware_camera2_legacy_PerfMeasurement.cpp \
android_hardware_camera2_DngCreator.cpp \
+ android_hardware_Radio.cpp \
android_hardware_SensorManager.cpp \
android_hardware_SerialPort.cpp \
android_hardware_SoundTrigger.cpp \
@@ -236,10 +237,12 @@
libpdfium \
libimg_utils \
libnetd_client \
+ libradio \
libsoundtrigger \
libminikin \
libprocessgroup \
- libnativebridge
+ libnativebridge \
+ libradio_metadata
LOCAL_SHARED_LIBRARIES += \
libhwui \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ad52e3f..441af15 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -81,6 +81,7 @@
extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
extern int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv *env);
extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
+extern int register_android_hardware_Radio(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
extern int register_android_hardware_SerialPort(JNIEnv *env);
extern int register_android_hardware_SoundTrigger(JNIEnv *env);
@@ -1254,6 +1255,7 @@
REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
REG_JNI(register_android_hardware_camera2_DngCreator),
+ REG_JNI(register_android_hardware_Radio),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
REG_JNI(register_android_hardware_SoundTrigger),
diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp
new file mode 100644
index 0000000..b9dd77a
--- /dev/null
+++ b/core/jni/android_hardware_Radio.cpp
@@ -0,0 +1,936 @@
+/*
+**
+** Copyright 2015, 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_NDEBUG 0
+#define LOG_TAG "Radio-JNI"
+#include <utils/Log.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "core_jni_helpers.h"
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+#include <radio/RadioCallback.h>
+#include <radio/Radio.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
+
+using namespace android;
+
+static jclass gArrayListClass;
+static struct {
+ jmethodID add;
+} gArrayListMethods;
+
+static const char* const kRadioManagerClassPathName = "android/hardware/radio/RadioManager";
+static jclass gRadioManagerClass;
+
+static const char* const kRadioModuleClassPathName = "android/hardware/radio/RadioModule";
+static jclass gRadioModuleClass;
+static struct {
+ jfieldID mNativeContext;
+ jfieldID mId;
+} gModuleFields;
+static jmethodID gPostEventFromNative;
+
+static const char* const kModulePropertiesClassPathName =
+ "android/hardware/radio/RadioManager$ModuleProperties";
+static jclass gModulePropertiesClass;
+static jmethodID gModulePropertiesCstor;
+
+
+static const char* const kRadioBandDescriptorClassPathName =
+ "android/hardware/radio/RadioManager$BandDescriptor";
+static jclass gRadioBandDescriptorClass;
+static struct {
+ jfieldID mRegion;
+ jfieldID mType;
+ jfieldID mLowerLimit;
+ jfieldID mUpperLimit;
+ jfieldID mSpacing;
+} gRadioBandDescriptorFields;
+
+static const char* const kRadioFmBandDescriptorClassPathName =
+ "android/hardware/radio/RadioManager$FmBandDescriptor";
+static jclass gRadioFmBandDescriptorClass;
+static jmethodID gRadioFmBandDescriptorCstor;
+
+static const char* const kRadioAmBandDescriptorClassPathName =
+ "android/hardware/radio/RadioManager$AmBandDescriptor";
+static jclass gRadioAmBandDescriptorClass;
+static jmethodID gRadioAmBandDescriptorCstor;
+
+static const char* const kRadioBandConfigClassPathName =
+ "android/hardware/radio/RadioManager$BandConfig";
+static jclass gRadioBandConfigClass;
+static struct {
+ jfieldID mDescriptor;
+} gRadioBandConfigFields;
+
+
+static const char* const kRadioFmBandConfigClassPathName =
+ "android/hardware/radio/RadioManager$FmBandConfig";
+static jclass gRadioFmBandConfigClass;
+static jmethodID gRadioFmBandConfigCstor;
+static struct {
+ jfieldID mStereo;
+ jfieldID mRds;
+ jfieldID mTa;
+ jfieldID mAf;
+} gRadioFmBandConfigFields;
+
+static const char* const kRadioAmBandConfigClassPathName =
+ "android/hardware/radio/RadioManager$AmBandConfig";
+static jclass gRadioAmBandConfigClass;
+static jmethodID gRadioAmBandConfigCstor;
+static struct {
+ jfieldID mStereo;
+} gRadioAmBandConfigFields;
+
+
+static const char* const kRadioProgramInfoClassPathName =
+ "android/hardware/radio/RadioManager$ProgramInfo";
+static jclass gRadioProgramInfoClass;
+static jmethodID gRadioProgramInfoCstor;
+
+static const char* const kRadioMetadataClassPathName =
+ "android/hardware/radio/RadioMetadata";
+static jclass gRadioMetadataClass;
+static jmethodID gRadioMetadataCstor;
+static struct {
+ jmethodID putIntFromNative;
+ jmethodID putStringFromNative;
+ jmethodID putBitmapFromNative;
+} gRadioMetadataMethods;
+
+static Mutex gLock;
+
+enum {
+ RADIO_STATUS_OK = 0,
+ RADIO_STATUS_ERROR = INT_MIN,
+ RADIO_PERMISSION_DENIED = -1,
+ RADIO_STATUS_NO_INIT = -19,
+ RADIO_STATUS_BAD_VALUE = -22,
+ RADIO_STATUS_DEAD_OBJECT = -32,
+ RADIO_STATUS_INVALID_OPERATION = -38,
+ RADIO_STATUS_TIMED_OUT = -110,
+};
+
+
+// ----------------------------------------------------------------------------
+
+static sp<Radio> getRadio(JNIEnv* env, jobject thiz)
+{
+ Mutex::Autolock l(gLock);
+ Radio* const radio = (Radio*)env->GetLongField(thiz, gModuleFields.mNativeContext);
+ return sp<Radio>(radio);
+}
+
+static sp<Radio> setRadio(JNIEnv* env, jobject thiz, const sp<Radio>& module)
+{
+ Mutex::Autolock l(gLock);
+ sp<Radio> old = (Radio*)env->GetLongField(thiz, gModuleFields.mNativeContext);
+ if (module.get()) {
+ module->incStrong((void*)setRadio);
+ }
+ if (old != 0) {
+ old->decStrong((void*)setRadio);
+ }
+ env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get());
+ return old;
+}
+
+static jint convertBandDescriptorFromNative(JNIEnv *env,
+ jobject *jBandDescriptor,
+ const radio_band_config_t *nBandconfig)
+{
+ ALOGV("%s type %d region %d", __FUNCTION__, nBandconfig->band.type, nBandconfig->region);
+
+ if (nBandconfig->band.type == RADIO_BAND_FM ||
+ nBandconfig->band.type == RADIO_BAND_FM_HD) {
+ *jBandDescriptor = env->NewObject(gRadioFmBandDescriptorClass, gRadioFmBandDescriptorCstor,
+ nBandconfig->region, nBandconfig->band.type,
+ nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
+ nBandconfig->band.spacings[0],
+ nBandconfig->band.fm.stereo,
+ nBandconfig->band.fm.rds != RADIO_RDS_NONE,
+ nBandconfig->band.fm.ta,
+ nBandconfig->band.fm.af);
+ } else if (nBandconfig->band.type == RADIO_BAND_AM) {
+ *jBandDescriptor = env->NewObject(gRadioAmBandDescriptorClass, gRadioAmBandDescriptorCstor,
+ nBandconfig->region, nBandconfig->band.type,
+ nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
+ nBandconfig->band.spacings[0],
+ nBandconfig->band.am.stereo);
+ } else {
+ ALOGE("%s unknown band type %d", __FUNCTION__, nBandconfig->band.type);
+ return (jint)RADIO_STATUS_BAD_VALUE;
+ }
+
+ if (*jBandDescriptor == NULL) {
+ return (jint)RADIO_STATUS_NO_INIT;
+ }
+
+ return (jint)RADIO_STATUS_OK;
+}
+
+static jint convertBandConfigFromNative(JNIEnv *env,
+ jobject *jBandConfig,
+ const radio_band_config_t *nBandconfig)
+{
+ ALOGV("%s type %d region %d", __FUNCTION__, nBandconfig->band.type, nBandconfig->region);
+
+ if (nBandconfig->band.type == RADIO_BAND_FM ||
+ nBandconfig->band.type == RADIO_BAND_FM_HD) {
+ *jBandConfig = env->NewObject(gRadioFmBandConfigClass, gRadioFmBandConfigCstor,
+ nBandconfig->region, nBandconfig->band.type,
+ nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
+ nBandconfig->band.spacings[0],
+ nBandconfig->band.fm.stereo,
+ nBandconfig->band.fm.rds != RADIO_RDS_NONE,
+ nBandconfig->band.fm.ta,
+ nBandconfig->band.fm.af);
+ } else if (nBandconfig->band.type == RADIO_BAND_AM) {
+ *jBandConfig = env->NewObject(gRadioAmBandConfigClass, gRadioAmBandConfigCstor,
+ nBandconfig->region, nBandconfig->band.type,
+ nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
+ nBandconfig->band.spacings[0],
+ nBandconfig->band.am.stereo);
+ } else {
+ ALOGE("%s unknown band type %d", __FUNCTION__, nBandconfig->band.type);
+ return (jint)RADIO_STATUS_BAD_VALUE;
+ }
+
+ if (*jBandConfig == NULL) {
+ return (jint)RADIO_STATUS_NO_INIT;
+ }
+
+ return (jint)RADIO_STATUS_OK;
+}
+
+static jint convertMetadataFromNative(JNIEnv *env,
+ jobject *jMetadata,
+ const radio_metadata_t *nMetadata)
+{
+ ALOGV("%s", __FUNCTION__);
+ int count = radio_metadata_get_count(nMetadata);
+ if (count <= 0) {
+ return (jint)count;
+ }
+ *jMetadata = env->NewObject(gRadioMetadataClass, gRadioMetadataCstor);
+
+ jint jCount = 0;
+ jint jStatus = 0;
+ for (unsigned int i = 0; i < (unsigned int)count; i++) {
+ radio_metadata_key_t key;
+ radio_metadata_type_t type;
+ void *value;
+ unsigned int size;
+ if (radio_metadata_get_at_index(nMetadata, i , &key, &type, &value, &size) != 0) {
+ continue;
+ }
+ switch (type) {
+ case RADIO_METADATA_TYPE_INT: {
+ ALOGV("%s RADIO_METADATA_TYPE_INT %d", __FUNCTION__, key);
+ jStatus = env->CallIntMethod(*jMetadata,
+ gRadioMetadataMethods.putIntFromNative,
+ key, *(jint *)value);
+ if (jStatus == 0) {
+ jCount++;
+ }
+ } break;
+ case RADIO_METADATA_TYPE_TEXT: {
+ ALOGV("%s RADIO_METADATA_TYPE_TEXT %d", __FUNCTION__, key);
+ jstring jText = env->NewStringUTF((char *)value);
+ jStatus = env->CallIntMethod(*jMetadata,
+ gRadioMetadataMethods.putStringFromNative,
+ key, jText);
+ if (jStatus == 0) {
+ jCount++;
+ }
+ env->DeleteLocalRef(jText);
+ } break;
+ case RADIO_METADATA_TYPE_RAW: {
+ ALOGV("%s RADIO_METADATA_TYPE_RAW %d size %u", __FUNCTION__, key, size);
+ if (size == 0) {
+ break;
+ }
+ jbyteArray jData = env->NewByteArray(size);
+ if (jData == NULL) {
+ break;
+ }
+ env->SetByteArrayRegion(jData, 0, size, (jbyte *)value);
+ jStatus = env->CallIntMethod(*jMetadata,
+ gRadioMetadataMethods.putBitmapFromNative,
+ key, jData);
+ if (jStatus == 0) {
+ jCount++;
+ }
+ env->DeleteLocalRef(jData);
+ } break;
+ }
+ }
+ return jCount;
+}
+
+static jint convertProgramInfoFromNative(JNIEnv *env,
+ jobject *jProgramInfo,
+ const radio_program_info_t *nProgramInfo)
+{
+ ALOGV("%s", __FUNCTION__);
+ int jStatus;
+ jobject jMetadata = NULL;
+ if (nProgramInfo->metadata != NULL) {
+ ALOGV("%s metadata %p", __FUNCTION__, nProgramInfo->metadata);
+ jStatus = convertMetadataFromNative(env, &jMetadata, nProgramInfo->metadata);
+ if (jStatus < 0) {
+ return jStatus;
+ }
+ }
+
+ ALOGV("%s channel %d tuned %d", __FUNCTION__, nProgramInfo->channel, nProgramInfo->tuned);
+
+ *jProgramInfo = env->NewObject(gRadioProgramInfoClass, gRadioProgramInfoCstor,
+ nProgramInfo->channel, nProgramInfo->sub_channel,
+ nProgramInfo->tuned, nProgramInfo->stereo,
+ nProgramInfo->digital, nProgramInfo->signal_strength,
+ jMetadata);
+
+ env->DeleteLocalRef(jMetadata);
+ return (jint)RADIO_STATUS_OK;
+}
+
+
+static jint convertBandConfigToNative(JNIEnv *env,
+ radio_band_config_t *nBandconfig,
+ jobject jBandConfig)
+{
+ ALOGV("%s", __FUNCTION__);
+
+ jobject jDescriptor = env->GetObjectField(jBandConfig, gRadioBandConfigFields.mDescriptor);
+
+ if (jDescriptor == NULL) {
+ return (jint)RADIO_STATUS_NO_INIT;
+ }
+
+ nBandconfig->region =
+ (radio_region_t)env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mRegion);
+ nBandconfig->band.type =
+ (radio_band_t)env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mType);
+ nBandconfig->band.lower_limit =
+ env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mLowerLimit);
+ nBandconfig->band.upper_limit =
+ env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mUpperLimit);
+ nBandconfig->band.num_spacings = 1;
+ nBandconfig->band.spacings[0] =
+ env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mSpacing);
+
+ if (env->IsInstanceOf(jBandConfig, gRadioFmBandConfigClass)) {
+ nBandconfig->band.fm.deemphasis = radio_demephasis_for_region(nBandconfig->region);
+ nBandconfig->band.fm.stereo =
+ env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mStereo);
+ nBandconfig->band.fm.rds =
+ radio_rds_for_region(env->GetBooleanField(jBandConfig,
+ gRadioFmBandConfigFields.mRds),
+ nBandconfig->region);
+ nBandconfig->band.fm.ta = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mTa);
+ nBandconfig->band.fm.af = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mAf);
+ } else if (env->IsInstanceOf(jBandConfig, gRadioAmBandConfigClass)) {
+ nBandconfig->band.am.stereo =
+ env->GetBooleanField(jBandConfig, gRadioAmBandConfigFields.mStereo);
+ } else {
+ return (jint)RADIO_STATUS_BAD_VALUE;
+ }
+
+ return (jint)RADIO_STATUS_OK;
+}
+
+static jint
+android_hardware_Radio_listModules(JNIEnv *env, jobject clazz,
+ jobject jModules)
+{
+ ALOGV("%s", __FUNCTION__);
+
+ if (jModules == NULL) {
+ ALOGE("listModules NULL ArrayList");
+ return RADIO_STATUS_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jModules, gArrayListClass)) {
+ ALOGE("listModules not an arraylist");
+ return RADIO_STATUS_BAD_VALUE;
+ }
+
+ unsigned int numModules = 0;
+ radio_properties_t *nModules = NULL;
+
+ status_t status = Radio::listModules(nModules, &numModules);
+ if (status != NO_ERROR || numModules == 0) {
+ return (jint)status;
+ }
+
+ nModules = (radio_properties_t *)calloc(numModules, sizeof(radio_properties_t));
+
+ status = Radio::listModules(nModules, &numModules);
+ ALOGV("%s Radio::listModules status %d numModules %d", __FUNCTION__, status, numModules);
+
+ if (status != NO_ERROR) {
+ numModules = 0;
+ }
+
+ for (size_t i = 0; i < numModules; i++) {
+ if (nModules[i].num_bands == 0) {
+ continue;
+ }
+ ALOGV("%s module %zu id %d implementor %s product %s",
+ __FUNCTION__, i, nModules[i].handle, nModules[i].implementor,
+ nModules[i].product);
+
+
+ jobjectArray jBands = env->NewObjectArray(nModules[i].num_bands,
+ gRadioBandDescriptorClass, NULL);
+
+ for (size_t j = 0; j < nModules[i].num_bands; j++) {
+ jobject jBandDescriptor;
+ int jStatus =
+ convertBandDescriptorFromNative(env, &jBandDescriptor, &nModules[i].bands[j]);
+ if (jStatus != RADIO_STATUS_OK) {
+ continue;
+ }
+ env->SetObjectArrayElement(jBands, j, jBandDescriptor);
+ env->DeleteLocalRef(jBandDescriptor);
+ }
+
+ if (env->GetArrayLength(jBands) == 0) {
+ continue;
+ }
+ jstring jImplementor = env->NewStringUTF(nModules[i].implementor);
+ jstring jProduct = env->NewStringUTF(nModules[i].product);
+ jstring jVersion = env->NewStringUTF(nModules[i].version);
+ jstring jSerial = env->NewStringUTF(nModules[i].serial);
+ jobject jModule = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor,
+ nModules[i].handle, nModules[i].class_id,
+ jImplementor, jProduct, jVersion, jSerial,
+ nModules[i].num_tuners,
+ nModules[i].num_audio_sources,
+ nModules[i].supports_capture,
+ jBands);
+
+ env->DeleteLocalRef(jImplementor);
+ env->DeleteLocalRef(jProduct);
+ env->DeleteLocalRef(jVersion);
+ env->DeleteLocalRef(jSerial);
+ env->DeleteLocalRef(jBands);
+ if (jModule == NULL) {
+ continue;
+ }
+ env->CallBooleanMethod(jModules, gArrayListMethods.add, jModule);
+ }
+
+ free(nModules);
+ return (jint) status;
+}
+
+// ----------------------------------------------------------------------------
+
+class JNIRadioCallback: public RadioCallback
+{
+public:
+ JNIRadioCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
+ ~JNIRadioCallback();
+
+ virtual void onEvent(struct radio_event *event);
+
+private:
+ jclass mClass; // Reference to Radio class
+ jobject mObject; // Weak ref to Radio Java object to call on
+};
+
+JNIRadioCallback::JNIRadioCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+
+ // Hold onto the RadioModule class for use in calling the static method
+ // that posts events to the application thread.
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find class %s", kRadioModuleClassPathName);
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+
+ // We use a weak reference so the RadioModule object can be garbage collected.
+ // The reference is only used as a proxy for callbacks.
+ mObject = env->NewGlobalRef(weak_thiz);
+}
+
+JNIRadioCallback::~JNIRadioCallback()
+{
+ // remove global references
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ return;
+ }
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNIRadioCallback::onEvent(struct radio_event *event)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ return;
+ }
+
+ ALOGV("%s", __FUNCTION__);
+
+ jobject jObj = NULL;
+ jint jArg2 = 0;
+ jint jStatus = RADIO_STATUS_OK;
+ switch (event->type) {
+ case RADIO_EVENT_CONFIG:
+ jStatus = convertBandConfigFromNative(env, &jObj, &event->config);
+ break;
+ case RADIO_EVENT_TUNED:
+ case RADIO_EVENT_AF_SWITCH:
+ ALOGV("%s RADIO_EVENT_TUNED channel %d", __FUNCTION__, event->info.channel);
+ jStatus = convertProgramInfoFromNative(env, &jObj, &event->info);
+ break;
+ case RADIO_EVENT_METADATA:
+ jStatus = convertMetadataFromNative(env, &jObj, event->metadata);
+ if (jStatus >= 0) {
+ jStatus = RADIO_STATUS_OK;
+ }
+ break;
+ case RADIO_EVENT_ANTENNA:
+ case RADIO_EVENT_TA:
+ case RADIO_EVENT_CONTROL:
+ jArg2 = event->on ? 1 : 0;
+ break;
+ }
+
+ if (jStatus != RADIO_STATUS_OK) {
+ return;
+ }
+ env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+ event->type, event->status, jArg2, jObj);
+
+ env->DeleteLocalRef(jObj);
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ env->ExceptionClear();
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void
+android_hardware_Radio_setup(JNIEnv *env, jobject thiz,
+ jobject weak_this, jobject jConfig, jboolean withAudio)
+{
+ ALOGV("%s", __FUNCTION__);
+
+ setRadio(env, thiz, 0);
+
+ sp<JNIRadioCallback> callback = new JNIRadioCallback(env, thiz, weak_this);
+
+ radio_handle_t handle = (radio_handle_t)env->GetIntField(thiz, gModuleFields.mId);
+
+ struct radio_band_config nConfig;
+ struct radio_band_config *configPtr = NULL;
+ if (jConfig != NULL) {
+ jint jStatus = convertBandConfigToNative(env, &nConfig, jConfig);
+ if (jStatus != RADIO_STATUS_OK) {
+ return;
+ }
+ configPtr = &nConfig;
+ }
+ sp<Radio> module = Radio::attach(handle, configPtr, (bool)withAudio, callback);
+ if (module == 0) {
+ return;
+ }
+
+ setRadio(env, thiz, module);
+}
+
+static void
+android_hardware_Radio_close(JNIEnv *env, jobject thiz)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = setRadio(env, thiz, 0);
+ ALOGV("detach module %p", module.get());
+ if (module != 0) {
+ ALOGV("detach module->detach()");
+ module->detach();
+ }
+}
+
+static void
+android_hardware_Radio_finalize(JNIEnv *env, jobject thiz)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module != 0) {
+ ALOGW("Radio finalized without being detached");
+ }
+ android_hardware_Radio_close(env, thiz);
+}
+
+static jint
+android_hardware_Radio_setConfiguration(JNIEnv *env, jobject thiz, jobject jConfig)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+
+ if (!env->IsInstanceOf(jConfig, gRadioFmBandConfigClass) &&
+ !env->IsInstanceOf(jConfig, gRadioAmBandConfigClass)) {
+ return RADIO_STATUS_BAD_VALUE;
+ }
+
+ struct radio_band_config nConfig;
+ jint jStatus = convertBandConfigToNative(env, &nConfig, jConfig);
+ if (jStatus != RADIO_STATUS_OK) {
+ return jStatus;
+ }
+
+ status_t status = module->setConfiguration(&nConfig);
+ return (jint)status;
+}
+
+static jint
+android_hardware_Radio_getConfiguration(JNIEnv *env, jobject thiz, jobjectArray jConfigs)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ if (env->GetArrayLength(jConfigs) != 1) {
+ return (jint)RADIO_STATUS_BAD_VALUE;
+ }
+
+ struct radio_band_config nConfig;
+
+ status_t status = module->getConfiguration(&nConfig);
+ if (status != NO_ERROR) {
+ return (jint)status;
+ }
+ jobject jConfig;
+ int jStatus = convertBandConfigFromNative(env, &jConfig, &nConfig);
+ if (jStatus != RADIO_STATUS_OK) {
+ return jStatus;
+ }
+ env->SetObjectArrayElement(jConfigs, 0, jConfig);
+ env->DeleteLocalRef(jConfig);
+ return RADIO_STATUS_OK;
+}
+
+static jint
+android_hardware_Radio_setMute(JNIEnv *env, jobject thiz, jboolean mute)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ status_t status = module->setMute((bool)mute);
+ return (jint)status;
+}
+
+static jboolean
+android_hardware_Radio_getMute(JNIEnv *env, jobject thiz)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return true;
+ }
+ bool mute = true;
+ status_t status = module->getMute(&mute);
+ if (status != NO_ERROR) {
+ return true;
+ }
+ return (jboolean)mute;
+}
+
+static jint
+android_hardware_Radio_step(JNIEnv *env, jobject thiz, jint direction, jboolean skipSubChannel)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ status_t status = module->step((radio_direction_t)direction, (bool)skipSubChannel);
+ return (jint)status;
+}
+
+static jint
+android_hardware_Radio_scan(JNIEnv *env, jobject thiz, jint direction, jboolean skipSubChannel)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ status_t status = module->scan((radio_direction_t)direction, (bool)skipSubChannel);
+ return (jint)status;
+}
+
+static jint
+android_hardware_Radio_tune(JNIEnv *env, jobject thiz, jint channel, jint subChannel)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ status_t status = module->tune((unsigned int)channel, (unsigned int)subChannel);
+ return (jint)status;
+}
+
+static jint
+android_hardware_Radio_cancel(JNIEnv *env, jobject thiz)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ status_t status = module->cancel();
+ return (jint)status;
+}
+
+static jint
+android_hardware_Radio_getProgramInformation(JNIEnv *env, jobject thiz, jobjectArray jInfos)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return RADIO_STATUS_NO_INIT;
+ }
+ if (env->GetArrayLength(jInfos) != 1) {
+ return (jint)RADIO_STATUS_BAD_VALUE;
+ }
+
+ struct radio_program_info nInfo;
+ radio_metadata_allocate(&nInfo.metadata, 0, 0);
+ jobject jInfo = NULL;
+ int jStatus;
+
+ jStatus = (int)module->getProgramInformation(&nInfo);
+ if (jStatus != RADIO_STATUS_OK) {
+ goto exit;
+ }
+ jStatus = convertProgramInfoFromNative(env, &jInfo, &nInfo);
+ if (jStatus != RADIO_STATUS_OK) {
+ goto exit;
+ }
+ env->SetObjectArrayElement(jInfos, 0, jInfo);
+
+exit:
+ if (jInfo != NULL) {
+ env->DeleteLocalRef(jInfo);
+ }
+ radio_metadata_deallocate(nInfo.metadata);
+ return jStatus;
+}
+
+static jboolean
+android_hardware_Radio_isAntennaConnected(JNIEnv *env, jobject thiz)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return false;
+ }
+
+ struct radio_band_config nConfig;
+
+ status_t status = module->getConfiguration(&nConfig);
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ return (jboolean)nConfig.band.antenna_connected;
+}
+
+
+static jboolean
+android_hardware_Radio_hasControl(JNIEnv *env, jobject thiz)
+{
+ ALOGV("%s", __FUNCTION__);
+ sp<Radio> module = getRadio(env, thiz);
+ if (module == NULL) {
+ return false;
+ }
+
+ bool hasControl;
+ status_t status = module->hasControl(&hasControl);
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ return (jboolean)hasControl;
+}
+
+
+static JNINativeMethod gMethods[] = {
+ {"listModules",
+ "(Ljava/util/List;)I",
+ (void *)android_hardware_Radio_listModules},
+};
+
+static JNINativeMethod gModuleMethods[] = {
+ {"native_setup",
+ "(Ljava/lang/Object;Landroid/hardware/radio/RadioManager$BandConfig;Z)V",
+ (void *)android_hardware_Radio_setup},
+ {"native_finalize",
+ "()V",
+ (void *)android_hardware_Radio_finalize},
+ {"close",
+ "()V",
+ (void *)android_hardware_Radio_close},
+ {"setConfiguration",
+ "(Landroid/hardware/radio/RadioManager$BandConfig;)I",
+ (void *)android_hardware_Radio_setConfiguration},
+ {"getConfiguration",
+ "([Landroid/hardware/radio/RadioManager$BandConfig;)I",
+ (void *)android_hardware_Radio_getConfiguration},
+ {"setMute",
+ "(Z)I",
+ (void *)android_hardware_Radio_setMute},
+ {"getMute",
+ "()Z",
+ (void *)android_hardware_Radio_getMute},
+ {"step",
+ "(IZ)I",
+ (void *)android_hardware_Radio_step},
+ {"scan",
+ "(IZ)I",
+ (void *)android_hardware_Radio_scan},
+ {"tune",
+ "(II)I",
+ (void *)android_hardware_Radio_tune},
+ {"cancel",
+ "()I",
+ (void *)android_hardware_Radio_cancel},
+ {"getProgramInformation",
+ "([Landroid/hardware/radio/RadioManager$ProgramInfo;)I",
+ (void *)android_hardware_Radio_getProgramInformation},
+ {"isAntennaConnected",
+ "()Z",
+ (void *)android_hardware_Radio_isAntennaConnected},
+ {"hasControl",
+ "()Z",
+ (void *)android_hardware_Radio_hasControl},
+};
+
+int register_android_hardware_Radio(JNIEnv *env)
+{
+ jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+ gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+ gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+ jclass lClass = FindClassOrDie(env, kRadioManagerClassPathName);
+ gRadioManagerClass = MakeGlobalRefOrDie(env, lClass);
+
+ jclass moduleClass = FindClassOrDie(env, kRadioModuleClassPathName);
+ gRadioModuleClass = MakeGlobalRefOrDie(env, moduleClass);
+ gPostEventFromNative = GetStaticMethodIDOrDie(env, moduleClass, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ gModuleFields.mNativeContext = GetFieldIDOrDie(env, moduleClass, "mNativeContext", "J");
+ gModuleFields.mId = GetFieldIDOrDie(env, moduleClass, "mId", "I");
+
+ jclass modulePropertiesClass = FindClassOrDie(env, kModulePropertiesClassPathName);
+ gModulePropertiesClass = MakeGlobalRefOrDie(env, modulePropertiesClass);
+ gModulePropertiesCstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
+ "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;)V");
+
+ jclass bandDescriptorClass = FindClassOrDie(env, kRadioBandDescriptorClassPathName);
+ gRadioBandDescriptorClass = MakeGlobalRefOrDie(env, bandDescriptorClass);
+ gRadioBandDescriptorFields.mRegion = GetFieldIDOrDie(env, bandDescriptorClass, "mRegion", "I");
+ gRadioBandDescriptorFields.mType = GetFieldIDOrDie(env, bandDescriptorClass, "mType", "I");
+ gRadioBandDescriptorFields.mLowerLimit =
+ GetFieldIDOrDie(env, bandDescriptorClass, "mLowerLimit", "I");
+ gRadioBandDescriptorFields.mUpperLimit =
+ GetFieldIDOrDie(env, bandDescriptorClass, "mUpperLimit", "I");
+ gRadioBandDescriptorFields.mSpacing =
+ GetFieldIDOrDie(env, bandDescriptorClass, "mSpacing", "I");
+
+ jclass fmBandDescriptorClass = FindClassOrDie(env, kRadioFmBandDescriptorClassPathName);
+ gRadioFmBandDescriptorClass = MakeGlobalRefOrDie(env, fmBandDescriptorClass);
+ gRadioFmBandDescriptorCstor = GetMethodIDOrDie(env, fmBandDescriptorClass, "<init>",
+ "(IIIIIZZZZ)V");
+
+ jclass amBandDescriptorClass = FindClassOrDie(env, kRadioAmBandDescriptorClassPathName);
+ gRadioAmBandDescriptorClass = MakeGlobalRefOrDie(env, amBandDescriptorClass);
+ gRadioAmBandDescriptorCstor = GetMethodIDOrDie(env, amBandDescriptorClass, "<init>",
+ "(IIIIIZ)V");
+
+ jclass bandConfigClass = FindClassOrDie(env, kRadioBandConfigClassPathName);
+ gRadioBandConfigClass = MakeGlobalRefOrDie(env, bandConfigClass);
+ gRadioBandConfigFields.mDescriptor =
+ GetFieldIDOrDie(env, bandConfigClass, "mDescriptor",
+ "Landroid/hardware/radio/RadioManager$BandDescriptor;");
+
+ jclass fmBandConfigClass = FindClassOrDie(env, kRadioFmBandConfigClassPathName);
+ gRadioFmBandConfigClass = MakeGlobalRefOrDie(env, fmBandConfigClass);
+ gRadioFmBandConfigCstor = GetMethodIDOrDie(env, fmBandConfigClass, "<init>",
+ "(IIIIIZZZZ)V");
+ gRadioFmBandConfigFields.mStereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z");
+ gRadioFmBandConfigFields.mRds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z");
+ gRadioFmBandConfigFields.mTa = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z");
+ gRadioFmBandConfigFields.mAf = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z");
+
+
+ jclass amBandConfigClass = FindClassOrDie(env, kRadioAmBandConfigClassPathName);
+ gRadioAmBandConfigClass = MakeGlobalRefOrDie(env, amBandConfigClass);
+ gRadioAmBandConfigCstor = GetMethodIDOrDie(env, amBandConfigClass, "<init>",
+ "(IIIIIZ)V");
+ gRadioAmBandConfigFields.mStereo = GetFieldIDOrDie(env, amBandConfigClass, "mStereo", "Z");
+
+ jclass programInfoClass = FindClassOrDie(env, kRadioProgramInfoClassPathName);
+ gRadioProgramInfoClass = MakeGlobalRefOrDie(env, programInfoClass);
+ gRadioProgramInfoCstor = GetMethodIDOrDie(env, programInfoClass, "<init>",
+ "(IIZZZILandroid/hardware/radio/RadioMetadata;)V");
+
+ jclass metadataClass = FindClassOrDie(env, kRadioMetadataClassPathName);
+ gRadioMetadataClass = MakeGlobalRefOrDie(env, metadataClass);
+ gRadioMetadataCstor = GetMethodIDOrDie(env, metadataClass, "<init>", "()V");
+ gRadioMetadataMethods.putIntFromNative = GetMethodIDOrDie(env, metadataClass,
+ "putIntFromNative",
+ "(II)I");
+ gRadioMetadataMethods.putStringFromNative = GetMethodIDOrDie(env, metadataClass,
+ "putStringFromNative",
+ "(ILjava/lang/String;)I");
+ gRadioMetadataMethods.putBitmapFromNative = GetMethodIDOrDie(env, metadataClass,
+ "putBitmapFromNative",
+ "(I[B)I");
+
+
+ RegisterMethodsOrDie(env, kRadioManagerClassPathName, gMethods, NELEM(gMethods));
+
+ int ret = RegisterMethodsOrDie(env, kRadioModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
+
+ ALOGI("%s DONE", __FUNCTION__);
+
+ return ret;
+}