1/*
2**
3** Copyright 2015, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18//#define LOG_NDEBUG 0
19#define LOG_TAG "Radio-JNI"
20#include <utils/Log.h>
21
22#include "jni.h"
23#include "JNIHelp.h"
24#include "core_jni_helpers.h"
25#include <system/radio.h>
26#include <system/radio_metadata.h>
27#include <radio/RadioCallback.h>
28#include <radio/Radio.h>
29#include <utils/RefBase.h>
30#include <utils/Vector.h>
31#include <binder/IMemory.h>
32#include <binder/MemoryDealer.h>
33
34using namespace android;
35
36static jclass gArrayListClass;
37static struct {
38    jmethodID    add;
39} gArrayListMethods;
40
41static const char* const kRadioManagerClassPathName = "android/hardware/radio/RadioManager";
42static jclass gRadioManagerClass;
43
44static const char* const kRadioModuleClassPathName = "android/hardware/radio/RadioModule";
45static jclass gRadioModuleClass;
46static struct {
47    jfieldID    mNativeContext;
48    jfieldID    mId;
49} gModuleFields;
50static jmethodID gPostEventFromNative;
51
52static const char* const kModulePropertiesClassPathName =
53                                     "android/hardware/radio/RadioManager$ModuleProperties";
54static jclass gModulePropertiesClass;
55static jmethodID gModulePropertiesCstor;
56
57
58static const char* const kRadioBandDescriptorClassPathName =
59                             "android/hardware/radio/RadioManager$BandDescriptor";
60static jclass gRadioBandDescriptorClass;
61static struct {
62    jfieldID mRegion;
63    jfieldID mType;
64    jfieldID mLowerLimit;
65    jfieldID mUpperLimit;
66    jfieldID mSpacing;
67} gRadioBandDescriptorFields;
68
69static const char* const kRadioFmBandDescriptorClassPathName =
70                             "android/hardware/radio/RadioManager$FmBandDescriptor";
71static jclass gRadioFmBandDescriptorClass;
72static jmethodID gRadioFmBandDescriptorCstor;
73
74static const char* const kRadioAmBandDescriptorClassPathName =
75                             "android/hardware/radio/RadioManager$AmBandDescriptor";
76static jclass gRadioAmBandDescriptorClass;
77static jmethodID gRadioAmBandDescriptorCstor;
78
79static const char* const kRadioBandConfigClassPathName =
80                             "android/hardware/radio/RadioManager$BandConfig";
81static jclass gRadioBandConfigClass;
82static struct {
83    jfieldID mDescriptor;
84} gRadioBandConfigFields;
85
86
87static const char* const kRadioFmBandConfigClassPathName =
88                             "android/hardware/radio/RadioManager$FmBandConfig";
89static jclass gRadioFmBandConfigClass;
90static jmethodID gRadioFmBandConfigCstor;
91static struct {
92    jfieldID mStereo;
93    jfieldID mRds;
94    jfieldID mTa;
95    jfieldID mAf;
96    jfieldID mEa;
97} gRadioFmBandConfigFields;
98
99static const char* const kRadioAmBandConfigClassPathName =
100                             "android/hardware/radio/RadioManager$AmBandConfig";
101static jclass gRadioAmBandConfigClass;
102static jmethodID gRadioAmBandConfigCstor;
103static struct {
104    jfieldID mStereo;
105} gRadioAmBandConfigFields;
106
107
108static const char* const kRadioProgramInfoClassPathName =
109                             "android/hardware/radio/RadioManager$ProgramInfo";
110static jclass gRadioProgramInfoClass;
111static jmethodID gRadioProgramInfoCstor;
112
113static const char* const kRadioMetadataClassPathName =
114                             "android/hardware/radio/RadioMetadata";
115static jclass gRadioMetadataClass;
116static jmethodID gRadioMetadataCstor;
117static struct {
118    jmethodID putIntFromNative;
119    jmethodID putStringFromNative;
120    jmethodID putBitmapFromNative;
121    jmethodID putClockFromNative;
122} gRadioMetadataMethods;
123
124static Mutex gLock;
125
126enum {
127    RADIO_STATUS_OK = 0,
128    RADIO_STATUS_ERROR = INT_MIN,
129    RADIO_PERMISSION_DENIED = -1,
130    RADIO_STATUS_NO_INIT = -19,
131    RADIO_STATUS_BAD_VALUE = -22,
132    RADIO_STATUS_DEAD_OBJECT = -32,
133    RADIO_STATUS_INVALID_OPERATION = -38,
134    RADIO_STATUS_TIMED_OUT = -110,
135};
136
137
138// ----------------------------------------------------------------------------
139
140static sp<Radio> getRadio(JNIEnv* env, jobject thiz)
141{
142    Mutex::Autolock l(gLock);
143    Radio* const radio = (Radio*)env->GetLongField(thiz, gModuleFields.mNativeContext);
144    return sp<Radio>(radio);
145}
146
147static sp<Radio> setRadio(JNIEnv* env, jobject thiz, const sp<Radio>& module)
148{
149    Mutex::Autolock l(gLock);
150    sp<Radio> old = (Radio*)env->GetLongField(thiz, gModuleFields.mNativeContext);
151    if (module.get()) {
152        module->incStrong((void*)setRadio);
153    }
154    if (old != 0) {
155        old->decStrong((void*)setRadio);
156    }
157    env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get());
158    return old;
159}
160
161static jint convertBandDescriptorFromNative(JNIEnv *env,
162                                           jobject *jBandDescriptor,
163                                           const radio_band_config_t *nBandconfig)
164{
165    ALOGV("%s type %d region %d", __FUNCTION__, nBandconfig->band.type, nBandconfig->region);
166
167    if (nBandconfig->band.type == RADIO_BAND_FM ||
168            nBandconfig->band.type == RADIO_BAND_FM_HD) {
169        *jBandDescriptor = env->NewObject(gRadioFmBandDescriptorClass, gRadioFmBandDescriptorCstor,
170                                      nBandconfig->region, nBandconfig->band.type,
171                                      nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
172                                      nBandconfig->band.spacings[0],
173                                      nBandconfig->band.fm.stereo,
174                                      nBandconfig->band.fm.rds != RADIO_RDS_NONE,
175                                      nBandconfig->band.fm.ta,
176                                      nBandconfig->band.fm.af,
177                                      nBandconfig->band.fm.ea);
178    } else if (nBandconfig->band.type == RADIO_BAND_AM) {
179        *jBandDescriptor = env->NewObject(gRadioAmBandDescriptorClass, gRadioAmBandDescriptorCstor,
180                                      nBandconfig->region, nBandconfig->band.type,
181                                      nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
182                                      nBandconfig->band.spacings[0],
183                                      nBandconfig->band.am.stereo);
184    } else {
185        ALOGE("%s unknown band type %d", __FUNCTION__, nBandconfig->band.type);
186        return (jint)RADIO_STATUS_BAD_VALUE;
187    }
188
189    if (*jBandDescriptor == NULL) {
190        return (jint)RADIO_STATUS_NO_INIT;
191    }
192
193    return (jint)RADIO_STATUS_OK;
194}
195
196static jint convertBandConfigFromNative(JNIEnv *env,
197                                           jobject *jBandConfig,
198                                           const radio_band_config_t *nBandconfig)
199{
200    ALOGV("%s type %d region %d", __FUNCTION__, nBandconfig->band.type, nBandconfig->region);
201
202    if (nBandconfig->band.type == RADIO_BAND_FM ||
203            nBandconfig->band.type == RADIO_BAND_FM_HD) {
204        *jBandConfig = env->NewObject(gRadioFmBandConfigClass, gRadioFmBandConfigCstor,
205                                      nBandconfig->region, nBandconfig->band.type,
206                                      nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
207                                      nBandconfig->band.spacings[0],
208                                      nBandconfig->band.fm.stereo,
209                                      nBandconfig->band.fm.rds != RADIO_RDS_NONE,
210                                      nBandconfig->band.fm.ta,
211                                      nBandconfig->band.fm.af,
212                                      nBandconfig->band.fm.ea);
213    } else if (nBandconfig->band.type == RADIO_BAND_AM) {
214        *jBandConfig = env->NewObject(gRadioAmBandConfigClass, gRadioAmBandConfigCstor,
215                                      nBandconfig->region, nBandconfig->band.type,
216                                      nBandconfig->band.lower_limit, nBandconfig->band.upper_limit,
217                                      nBandconfig->band.spacings[0],
218                                      nBandconfig->band.am.stereo);
219    } else {
220        ALOGE("%s unknown band type %d", __FUNCTION__, nBandconfig->band.type);
221        return (jint)RADIO_STATUS_BAD_VALUE;
222    }
223
224    if (*jBandConfig == NULL) {
225        return (jint)RADIO_STATUS_NO_INIT;
226    }
227
228    return (jint)RADIO_STATUS_OK;
229}
230
231static jint convertMetadataFromNative(JNIEnv *env,
232                                           jobject *jMetadata,
233                                           const radio_metadata_t *nMetadata)
234{
235    ALOGV("%s", __FUNCTION__);
236    int count = radio_metadata_get_count(nMetadata);
237    if (count <= 0) {
238        return (jint)count;
239    }
240    *jMetadata = env->NewObject(gRadioMetadataClass, gRadioMetadataCstor);
241
242    jint jCount = 0;
243    jint jStatus = 0;
244    for (unsigned int i = 0; i < (unsigned int)count; i++) {
245        radio_metadata_key_t key;
246        radio_metadata_type_t type;
247        void *value;
248        unsigned int size;
249        if (radio_metadata_get_at_index(nMetadata, i , &key, &type, &value, &size) != 0) {
250            continue;
251        }
252        switch (type) {
253            case RADIO_METADATA_TYPE_INT: {
254                ALOGV("%s RADIO_METADATA_TYPE_INT %d", __FUNCTION__, key);
255                jStatus = env->CallIntMethod(*jMetadata,
256                                   gRadioMetadataMethods.putIntFromNative,
257                                   key, *(jint *)value);
258                if (jStatus == 0) {
259                    jCount++;
260                }
261            } break;
262            case RADIO_METADATA_TYPE_TEXT: {
263                ALOGV("%s RADIO_METADATA_TYPE_TEXT %d", __FUNCTION__, key);
264                jstring jText = env->NewStringUTF((char *)value);
265                jStatus = env->CallIntMethod(*jMetadata,
266                                   gRadioMetadataMethods.putStringFromNative,
267                                   key, jText);
268                if (jStatus == 0) {
269                    jCount++;
270                }
271                env->DeleteLocalRef(jText);
272            } break;
273            case RADIO_METADATA_TYPE_RAW: {
274                ALOGV("%s RADIO_METADATA_TYPE_RAW %d size %u", __FUNCTION__, key, size);
275                if (size == 0) {
276                    break;
277                }
278                jbyteArray jData = env->NewByteArray(size);
279                if (jData == NULL) {
280                    break;
281                }
282                env->SetByteArrayRegion(jData, 0, size, (jbyte *)value);
283                jStatus = env->CallIntMethod(*jMetadata,
284                                   gRadioMetadataMethods.putBitmapFromNative,
285                                   key, jData);
286                if (jStatus == 0) {
287                    jCount++;
288                }
289                env->DeleteLocalRef(jData);
290            } break;
291            case RADIO_METADATA_TYPE_CLOCK: {
292                  ALOGV("%s RADIO_METADATA_TYPE_CLOCK %d", __FUNCTION__, key);
293                  radio_metadata_clock_t *clock = (radio_metadata_clock_t *) value;
294                  jStatus =
295                      env->CallIntMethod(*jMetadata,
296                                         gRadioMetadataMethods.putClockFromNative,
297                                         key, (jint) clock->utc_seconds_since_epoch,
298                                         (jint) clock->timezone_offset_in_minutes);
299                  if (jStatus == 0) {
300                      jCount++;
301                  }
302            } break;
303        }
304    }
305    return jCount;
306}
307
308static jint convertProgramInfoFromNative(JNIEnv *env,
309                                           jobject *jProgramInfo,
310                                           const radio_program_info_t *nProgramInfo)
311{
312    ALOGV("%s", __FUNCTION__);
313    int jStatus;
314    jobject jMetadata = NULL;
315    if (nProgramInfo->metadata != NULL) {
316        ALOGV("%s metadata %p", __FUNCTION__, nProgramInfo->metadata);
317        jStatus = convertMetadataFromNative(env, &jMetadata, nProgramInfo->metadata);
318        if (jStatus < 0) {
319            return jStatus;
320        }
321    }
322
323    ALOGV("%s channel %d tuned %d", __FUNCTION__, nProgramInfo->channel, nProgramInfo->tuned);
324
325    *jProgramInfo = env->NewObject(gRadioProgramInfoClass, gRadioProgramInfoCstor,
326                                  nProgramInfo->channel, nProgramInfo->sub_channel,
327                                  nProgramInfo->tuned, nProgramInfo->stereo,
328                                  nProgramInfo->digital, nProgramInfo->signal_strength,
329                                  jMetadata);
330
331    env->DeleteLocalRef(jMetadata);
332    return (jint)RADIO_STATUS_OK;
333}
334
335
336static jint convertBandConfigToNative(JNIEnv *env,
337                                      radio_band_config_t *nBandconfig,
338                                      jobject jBandConfig)
339{
340    ALOGV("%s", __FUNCTION__);
341
342    jobject jDescriptor = env->GetObjectField(jBandConfig, gRadioBandConfigFields.mDescriptor);
343
344    if (jDescriptor == NULL) {
345        return (jint)RADIO_STATUS_NO_INIT;
346    }
347
348    nBandconfig->region =
349            (radio_region_t)env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mRegion);
350    nBandconfig->band.type =
351            (radio_band_t)env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mType);
352    nBandconfig->band.lower_limit =
353            env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mLowerLimit);
354    nBandconfig->band.upper_limit =
355            env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mUpperLimit);
356    nBandconfig->band.num_spacings = 1;
357    nBandconfig->band.spacings[0] =
358            env->GetIntField(jDescriptor, gRadioBandDescriptorFields.mSpacing);
359
360    if (env->IsInstanceOf(jBandConfig, gRadioFmBandConfigClass)) {
361        nBandconfig->band.fm.deemphasis = radio_demephasis_for_region(nBandconfig->region);
362        nBandconfig->band.fm.stereo =
363                env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mStereo);
364        nBandconfig->band.fm.rds =
365                radio_rds_for_region(env->GetBooleanField(jBandConfig,
366                                                          gRadioFmBandConfigFields.mRds),
367                                     nBandconfig->region);
368        nBandconfig->band.fm.ta = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mTa);
369        nBandconfig->band.fm.af = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mAf);
370        nBandconfig->band.fm.ea = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mEa);
371    } else if (env->IsInstanceOf(jBandConfig, gRadioAmBandConfigClass)) {
372        nBandconfig->band.am.stereo =
373                env->GetBooleanField(jBandConfig, gRadioAmBandConfigFields.mStereo);
374    } else {
375        return (jint)RADIO_STATUS_BAD_VALUE;
376    }
377
378    return (jint)RADIO_STATUS_OK;
379}
380
381static jint
382android_hardware_Radio_listModules(JNIEnv *env, jobject clazz,
383                                          jobject jModules)
384{
385    ALOGV("%s", __FUNCTION__);
386
387    if (jModules == NULL) {
388        ALOGE("listModules NULL ArrayList");
389        return RADIO_STATUS_BAD_VALUE;
390    }
391    if (!env->IsInstanceOf(jModules, gArrayListClass)) {
392        ALOGE("listModules not an arraylist");
393        return RADIO_STATUS_BAD_VALUE;
394    }
395
396    unsigned int numModules = 0;
397    radio_properties_t *nModules = NULL;
398
399    status_t status = Radio::listModules(nModules, &numModules);
400    if (status != NO_ERROR || numModules == 0) {
401        return (jint)status;
402    }
403
404    nModules = (radio_properties_t *)calloc(numModules, sizeof(radio_properties_t));
405
406    status = Radio::listModules(nModules, &numModules);
407    ALOGV("%s Radio::listModules status %d numModules %d", __FUNCTION__, status, numModules);
408
409    if (status != NO_ERROR) {
410        numModules = 0;
411    }
412
413    for (size_t i = 0; i < numModules; i++) {
414        if (nModules[i].num_bands == 0) {
415            continue;
416        }
417        ALOGV("%s module %zu id %d implementor %s product %s",
418              __FUNCTION__, i, nModules[i].handle, nModules[i].implementor,
419              nModules[i].product);
420
421
422        jobjectArray jBands = env->NewObjectArray(nModules[i].num_bands,
423                                                  gRadioBandDescriptorClass, NULL);
424
425        for (size_t j = 0; j < nModules[i].num_bands; j++) {
426            jobject jBandDescriptor;
427            int jStatus =
428                    convertBandDescriptorFromNative(env, &jBandDescriptor, &nModules[i].bands[j]);
429            if (jStatus != RADIO_STATUS_OK) {
430                continue;
431            }
432            env->SetObjectArrayElement(jBands, j, jBandDescriptor);
433            env->DeleteLocalRef(jBandDescriptor);
434        }
435
436        if (env->GetArrayLength(jBands) == 0) {
437            continue;
438        }
439        jstring jImplementor = env->NewStringUTF(nModules[i].implementor);
440        jstring jProduct = env->NewStringUTF(nModules[i].product);
441        jstring jVersion = env->NewStringUTF(nModules[i].version);
442        jstring jSerial = env->NewStringUTF(nModules[i].serial);
443        jobject jModule = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor,
444                                               nModules[i].handle, nModules[i].class_id,
445                                               jImplementor, jProduct, jVersion, jSerial,
446                                               nModules[i].num_tuners,
447                                               nModules[i].num_audio_sources,
448                                               nModules[i].supports_capture,
449                                               jBands);
450
451        env->DeleteLocalRef(jImplementor);
452        env->DeleteLocalRef(jProduct);
453        env->DeleteLocalRef(jVersion);
454        env->DeleteLocalRef(jSerial);
455        env->DeleteLocalRef(jBands);
456        if (jModule == NULL) {
457            continue;
458        }
459        env->CallBooleanMethod(jModules, gArrayListMethods.add, jModule);
460    }
461
462    free(nModules);
463    return (jint) status;
464}
465
466// ----------------------------------------------------------------------------
467
468class JNIRadioCallback: public RadioCallback
469{
470public:
471    JNIRadioCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
472    ~JNIRadioCallback();
473
474    virtual void onEvent(struct radio_event *event);
475
476private:
477    jclass      mClass;     // Reference to Radio class
478    jobject     mObject;    // Weak ref to Radio Java object to call on
479};
480
481JNIRadioCallback::JNIRadioCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
482{
483
484    // Hold onto the RadioModule class for use in calling the static method
485    // that posts events to the application thread.
486    jclass clazz = env->GetObjectClass(thiz);
487    if (clazz == NULL) {
488        ALOGE("Can't find class %s", kRadioModuleClassPathName);
489        return;
490    }
491    mClass = (jclass)env->NewGlobalRef(clazz);
492
493    // We use a weak reference so the RadioModule object can be garbage collected.
494    // The reference is only used as a proxy for callbacks.
495    mObject  = env->NewGlobalRef(weak_thiz);
496}
497
498JNIRadioCallback::~JNIRadioCallback()
499{
500    // remove global references
501    JNIEnv *env = AndroidRuntime::getJNIEnv();
502    if (env == NULL) {
503        return;
504    }
505    env->DeleteGlobalRef(mObject);
506    env->DeleteGlobalRef(mClass);
507}
508
509void JNIRadioCallback::onEvent(struct radio_event *event)
510{
511    JNIEnv *env = AndroidRuntime::getJNIEnv();
512    if (env == NULL) {
513        return;
514    }
515
516    ALOGV("%s", __FUNCTION__);
517
518    jobject jObj = NULL;
519    jint jArg2 = 0;
520    jint jStatus = RADIO_STATUS_OK;
521    switch (event->type) {
522        case RADIO_EVENT_CONFIG:
523            jStatus = convertBandConfigFromNative(env, &jObj, &event->config);
524            break;
525        case RADIO_EVENT_TUNED:
526        case RADIO_EVENT_AF_SWITCH:
527            ALOGV("%s RADIO_EVENT_TUNED channel %d", __FUNCTION__, event->info.channel);
528            jStatus = convertProgramInfoFromNative(env, &jObj, &event->info);
529            break;
530        case RADIO_EVENT_METADATA:
531            jStatus = convertMetadataFromNative(env, &jObj, event->metadata);
532            if (jStatus >= 0) {
533                jStatus = RADIO_STATUS_OK;
534            }
535            break;
536        case RADIO_EVENT_ANTENNA:
537        case RADIO_EVENT_TA:
538        case RADIO_EVENT_EA:
539        case RADIO_EVENT_CONTROL:
540            jArg2 = event->on ? 1 : 0;
541            break;
542    }
543
544    if (jStatus != RADIO_STATUS_OK) {
545        return;
546    }
547    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
548                              event->type, event->status, jArg2, jObj);
549
550    env->DeleteLocalRef(jObj);
551    if (env->ExceptionCheck()) {
552        ALOGW("An exception occurred while notifying an event.");
553        env->ExceptionClear();
554    }
555}
556
557// ----------------------------------------------------------------------------
558
559static void
560android_hardware_Radio_setup(JNIEnv *env, jobject thiz,
561                             jobject weak_this, jobject jConfig, jboolean withAudio)
562{
563    ALOGV("%s", __FUNCTION__);
564
565    setRadio(env, thiz, 0);
566
567    sp<JNIRadioCallback> callback = new JNIRadioCallback(env, thiz, weak_this);
568
569    radio_handle_t handle = (radio_handle_t)env->GetIntField(thiz, gModuleFields.mId);
570
571    struct radio_band_config nConfig;
572    struct radio_band_config *configPtr = NULL;
573    if (jConfig != NULL) {
574        jint jStatus = convertBandConfigToNative(env, &nConfig, jConfig);
575        if (jStatus != RADIO_STATUS_OK) {
576            return;
577        }
578        configPtr = &nConfig;
579    }
580    sp<Radio> module = Radio::attach(handle, configPtr, (bool)withAudio, callback);
581    if (module == 0) {
582        return;
583    }
584
585    setRadio(env, thiz, module);
586}
587
588static void
589android_hardware_Radio_close(JNIEnv *env, jobject thiz)
590{
591    ALOGV("%s", __FUNCTION__);
592    sp<Radio> module = setRadio(env, thiz, 0);
593    ALOGV("detach module %p", module.get());
594    if (module != 0) {
595        ALOGV("detach module->detach()");
596        module->detach();
597    }
598}
599
600static void
601android_hardware_Radio_finalize(JNIEnv *env, jobject thiz)
602{
603    ALOGV("%s", __FUNCTION__);
604    sp<Radio> module = getRadio(env, thiz);
605    if (module != 0) {
606        ALOGW("Radio finalized without being detached");
607    }
608    android_hardware_Radio_close(env, thiz);
609}
610
611static jint
612android_hardware_Radio_setConfiguration(JNIEnv *env, jobject thiz, jobject jConfig)
613{
614    ALOGV("%s", __FUNCTION__);
615    sp<Radio> module = getRadio(env, thiz);
616    if (module == NULL) {
617        return RADIO_STATUS_NO_INIT;
618    }
619
620    if (!env->IsInstanceOf(jConfig, gRadioFmBandConfigClass) &&
621            !env->IsInstanceOf(jConfig, gRadioAmBandConfigClass)) {
622        return RADIO_STATUS_BAD_VALUE;
623    }
624
625    struct radio_band_config nConfig;
626    jint jStatus = convertBandConfigToNative(env, &nConfig, jConfig);
627    if (jStatus != RADIO_STATUS_OK) {
628        return jStatus;
629    }
630
631    status_t status = module->setConfiguration(&nConfig);
632    return (jint)status;
633}
634
635static jint
636android_hardware_Radio_getConfiguration(JNIEnv *env, jobject thiz, jobjectArray jConfigs)
637{
638    ALOGV("%s", __FUNCTION__);
639    sp<Radio> module = getRadio(env, thiz);
640    if (module == NULL) {
641        return RADIO_STATUS_NO_INIT;
642    }
643    if (env->GetArrayLength(jConfigs) != 1) {
644        return (jint)RADIO_STATUS_BAD_VALUE;
645    }
646
647    struct radio_band_config nConfig;
648
649    status_t status = module->getConfiguration(&nConfig);
650    if (status != NO_ERROR) {
651        return (jint)status;
652    }
653    jobject jConfig;
654    int jStatus = convertBandConfigFromNative(env, &jConfig, &nConfig);
655    if (jStatus != RADIO_STATUS_OK) {
656        return jStatus;
657    }
658    env->SetObjectArrayElement(jConfigs, 0, jConfig);
659    env->DeleteLocalRef(jConfig);
660    return RADIO_STATUS_OK;
661}
662
663static jint
664android_hardware_Radio_setMute(JNIEnv *env, jobject thiz, jboolean mute)
665{
666    ALOGV("%s", __FUNCTION__);
667    sp<Radio> module = getRadio(env, thiz);
668    if (module == NULL) {
669        return RADIO_STATUS_NO_INIT;
670    }
671    status_t status = module->setMute((bool)mute);
672    return (jint)status;
673}
674
675static jboolean
676android_hardware_Radio_getMute(JNIEnv *env, jobject thiz)
677{
678    ALOGV("%s", __FUNCTION__);
679    sp<Radio> module = getRadio(env, thiz);
680    if (module == NULL) {
681        return true;
682    }
683    bool mute = true;
684    status_t status = module->getMute(&mute);
685    if (status != NO_ERROR) {
686        return true;
687    }
688    return (jboolean)mute;
689}
690
691static jint
692android_hardware_Radio_step(JNIEnv *env, jobject thiz, jint direction, jboolean skipSubChannel)
693{
694    ALOGV("%s", __FUNCTION__);
695    sp<Radio> module = getRadio(env, thiz);
696    if (module == NULL) {
697        return RADIO_STATUS_NO_INIT;
698    }
699    status_t status = module->step((radio_direction_t)direction, (bool)skipSubChannel);
700    return (jint)status;
701}
702
703static jint
704android_hardware_Radio_scan(JNIEnv *env, jobject thiz, jint direction, jboolean skipSubChannel)
705{
706    ALOGV("%s", __FUNCTION__);
707    sp<Radio> module = getRadio(env, thiz);
708    if (module == NULL) {
709        return RADIO_STATUS_NO_INIT;
710    }
711    status_t status = module->scan((radio_direction_t)direction, (bool)skipSubChannel);
712    return (jint)status;
713}
714
715static jint
716android_hardware_Radio_tune(JNIEnv *env, jobject thiz, jint channel, jint subChannel)
717{
718    ALOGV("%s", __FUNCTION__);
719    sp<Radio> module = getRadio(env, thiz);
720    if (module == NULL) {
721        return RADIO_STATUS_NO_INIT;
722    }
723    status_t status = module->tune((unsigned int)channel, (unsigned int)subChannel);
724    return (jint)status;
725}
726
727static jint
728android_hardware_Radio_cancel(JNIEnv *env, jobject thiz)
729{
730    ALOGV("%s", __FUNCTION__);
731    sp<Radio> module = getRadio(env, thiz);
732    if (module == NULL) {
733        return RADIO_STATUS_NO_INIT;
734    }
735    status_t status = module->cancel();
736    return (jint)status;
737}
738
739static jint
740android_hardware_Radio_getProgramInformation(JNIEnv *env, jobject thiz, jobjectArray jInfos)
741{
742    ALOGV("%s", __FUNCTION__);
743    sp<Radio> module = getRadio(env, thiz);
744    if (module == NULL) {
745        return RADIO_STATUS_NO_INIT;
746    }
747    if (env->GetArrayLength(jInfos) != 1) {
748        return (jint)RADIO_STATUS_BAD_VALUE;
749    }
750
751    struct radio_program_info nInfo;
752    radio_metadata_allocate(&nInfo.metadata, 0, 0);
753    jobject jInfo = NULL;
754    int jStatus;
755
756    jStatus = (int)module->getProgramInformation(&nInfo);
757    if (jStatus != RADIO_STATUS_OK) {
758        goto exit;
759    }
760    jStatus = convertProgramInfoFromNative(env, &jInfo, &nInfo);
761    if (jStatus != RADIO_STATUS_OK) {
762        goto exit;
763    }
764    env->SetObjectArrayElement(jInfos, 0, jInfo);
765
766exit:
767    if (jInfo != NULL) {
768        env->DeleteLocalRef(jInfo);
769    }
770    radio_metadata_deallocate(nInfo.metadata);
771    return jStatus;
772}
773
774static jboolean
775android_hardware_Radio_isAntennaConnected(JNIEnv *env, jobject thiz)
776{
777    ALOGV("%s", __FUNCTION__);
778    sp<Radio> module = getRadio(env, thiz);
779    if (module == NULL) {
780        return false;
781    }
782
783    struct radio_band_config nConfig;
784
785    status_t status = module->getConfiguration(&nConfig);
786    if (status != NO_ERROR) {
787        return false;
788    }
789
790    return (jboolean)nConfig.band.antenna_connected;
791}
792
793
794static jboolean
795android_hardware_Radio_hasControl(JNIEnv *env, jobject thiz)
796{
797    ALOGV("%s", __FUNCTION__);
798    sp<Radio> module = getRadio(env, thiz);
799    if (module == NULL) {
800        return false;
801    }
802
803    bool hasControl;
804    status_t status = module->hasControl(&hasControl);
805    if (status != NO_ERROR) {
806        return false;
807    }
808
809    return (jboolean)hasControl;
810}
811
812
813static JNINativeMethod gMethods[] = {
814    {"listModules",
815        "(Ljava/util/List;)I",
816        (void *)android_hardware_Radio_listModules},
817};
818
819static JNINativeMethod gModuleMethods[] = {
820    {"native_setup",
821        "(Ljava/lang/Object;Landroid/hardware/radio/RadioManager$BandConfig;Z)V",
822        (void *)android_hardware_Radio_setup},
823    {"native_finalize",
824        "()V",
825        (void *)android_hardware_Radio_finalize},
826    {"close",
827        "()V",
828        (void *)android_hardware_Radio_close},
829    {"setConfiguration",
830        "(Landroid/hardware/radio/RadioManager$BandConfig;)I",
831        (void *)android_hardware_Radio_setConfiguration},
832    {"getConfiguration",
833        "([Landroid/hardware/radio/RadioManager$BandConfig;)I",
834        (void *)android_hardware_Radio_getConfiguration},
835    {"setMute",
836        "(Z)I",
837        (void *)android_hardware_Radio_setMute},
838    {"getMute",
839        "()Z",
840        (void *)android_hardware_Radio_getMute},
841    {"step",
842        "(IZ)I",
843        (void *)android_hardware_Radio_step},
844    {"scan",
845        "(IZ)I",
846        (void *)android_hardware_Radio_scan},
847    {"tune",
848        "(II)I",
849        (void *)android_hardware_Radio_tune},
850    {"cancel",
851        "()I",
852        (void *)android_hardware_Radio_cancel},
853    {"getProgramInformation",
854        "([Landroid/hardware/radio/RadioManager$ProgramInfo;)I",
855        (void *)android_hardware_Radio_getProgramInformation},
856    {"isAntennaConnected",
857        "()Z",
858        (void *)android_hardware_Radio_isAntennaConnected},
859    {"hasControl",
860        "()Z",
861        (void *)android_hardware_Radio_hasControl},
862};
863
864int register_android_hardware_Radio(JNIEnv *env)
865{
866    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
867    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
868    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
869
870    jclass lClass = FindClassOrDie(env, kRadioManagerClassPathName);
871    gRadioManagerClass = MakeGlobalRefOrDie(env, lClass);
872
873    jclass moduleClass = FindClassOrDie(env, kRadioModuleClassPathName);
874    gRadioModuleClass = MakeGlobalRefOrDie(env, moduleClass);
875    gPostEventFromNative = GetStaticMethodIDOrDie(env, moduleClass, "postEventFromNative",
876                                                  "(Ljava/lang/Object;IIILjava/lang/Object;)V");
877    gModuleFields.mNativeContext = GetFieldIDOrDie(env, moduleClass, "mNativeContext", "J");
878    gModuleFields.mId = GetFieldIDOrDie(env, moduleClass, "mId", "I");
879
880    jclass modulePropertiesClass = FindClassOrDie(env, kModulePropertiesClassPathName);
881    gModulePropertiesClass = MakeGlobalRefOrDie(env, modulePropertiesClass);
882    gModulePropertiesCstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
883            "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;)V");
884
885    jclass bandDescriptorClass = FindClassOrDie(env, kRadioBandDescriptorClassPathName);
886    gRadioBandDescriptorClass = MakeGlobalRefOrDie(env, bandDescriptorClass);
887    gRadioBandDescriptorFields.mRegion = GetFieldIDOrDie(env, bandDescriptorClass, "mRegion", "I");
888    gRadioBandDescriptorFields.mType = GetFieldIDOrDie(env, bandDescriptorClass, "mType", "I");
889    gRadioBandDescriptorFields.mLowerLimit =
890            GetFieldIDOrDie(env, bandDescriptorClass, "mLowerLimit", "I");
891    gRadioBandDescriptorFields.mUpperLimit =
892            GetFieldIDOrDie(env, bandDescriptorClass, "mUpperLimit", "I");
893    gRadioBandDescriptorFields.mSpacing =
894            GetFieldIDOrDie(env, bandDescriptorClass, "mSpacing", "I");
895
896    jclass fmBandDescriptorClass = FindClassOrDie(env, kRadioFmBandDescriptorClassPathName);
897    gRadioFmBandDescriptorClass = MakeGlobalRefOrDie(env, fmBandDescriptorClass);
898    gRadioFmBandDescriptorCstor = GetMethodIDOrDie(env, fmBandDescriptorClass, "<init>",
899            "(IIIIIZZZZZ)V");
900
901    jclass amBandDescriptorClass = FindClassOrDie(env, kRadioAmBandDescriptorClassPathName);
902    gRadioAmBandDescriptorClass = MakeGlobalRefOrDie(env, amBandDescriptorClass);
903    gRadioAmBandDescriptorCstor = GetMethodIDOrDie(env, amBandDescriptorClass, "<init>",
904            "(IIIIIZ)V");
905
906    jclass bandConfigClass = FindClassOrDie(env, kRadioBandConfigClassPathName);
907    gRadioBandConfigClass = MakeGlobalRefOrDie(env, bandConfigClass);
908    gRadioBandConfigFields.mDescriptor =
909            GetFieldIDOrDie(env, bandConfigClass, "mDescriptor",
910                            "Landroid/hardware/radio/RadioManager$BandDescriptor;");
911
912    jclass fmBandConfigClass = FindClassOrDie(env, kRadioFmBandConfigClassPathName);
913    gRadioFmBandConfigClass = MakeGlobalRefOrDie(env, fmBandConfigClass);
914    gRadioFmBandConfigCstor = GetMethodIDOrDie(env, fmBandConfigClass, "<init>",
915            "(IIIIIZZZZZ)V");
916    gRadioFmBandConfigFields.mStereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z");
917    gRadioFmBandConfigFields.mRds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z");
918    gRadioFmBandConfigFields.mTa = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z");
919    gRadioFmBandConfigFields.mAf = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z");
920    gRadioFmBandConfigFields.mEa =
921        GetFieldIDOrDie(env, fmBandConfigClass, "mEa", "Z");
922
923
924    jclass amBandConfigClass = FindClassOrDie(env, kRadioAmBandConfigClassPathName);
925    gRadioAmBandConfigClass = MakeGlobalRefOrDie(env, amBandConfigClass);
926    gRadioAmBandConfigCstor = GetMethodIDOrDie(env, amBandConfigClass, "<init>",
927            "(IIIIIZ)V");
928    gRadioAmBandConfigFields.mStereo = GetFieldIDOrDie(env, amBandConfigClass, "mStereo", "Z");
929
930    jclass programInfoClass = FindClassOrDie(env, kRadioProgramInfoClassPathName);
931    gRadioProgramInfoClass = MakeGlobalRefOrDie(env, programInfoClass);
932    gRadioProgramInfoCstor = GetMethodIDOrDie(env, programInfoClass, "<init>",
933            "(IIZZZILandroid/hardware/radio/RadioMetadata;)V");
934
935    jclass metadataClass = FindClassOrDie(env, kRadioMetadataClassPathName);
936    gRadioMetadataClass = MakeGlobalRefOrDie(env, metadataClass);
937    gRadioMetadataCstor = GetMethodIDOrDie(env, metadataClass, "<init>", "()V");
938    gRadioMetadataMethods.putIntFromNative = GetMethodIDOrDie(env, metadataClass,
939                                                              "putIntFromNative",
940                                                              "(II)I");
941    gRadioMetadataMethods.putStringFromNative = GetMethodIDOrDie(env, metadataClass,
942                                                                 "putStringFromNative",
943                                                                 "(ILjava/lang/String;)I");
944    gRadioMetadataMethods.putBitmapFromNative = GetMethodIDOrDie(env, metadataClass,
945                                                                 "putBitmapFromNative",
946                                                                 "(I[B)I");
947    gRadioMetadataMethods.putClockFromNative = GetMethodIDOrDie(env, metadataClass,
948                                                                "putClockFromNative",
949                                                                "(IJI)I");
950
951
952    RegisterMethodsOrDie(env, kRadioManagerClassPathName, gMethods, NELEM(gMethods));
953
954    int ret = RegisterMethodsOrDie(env, kRadioModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
955
956    ALOGI("%s DONE", __FUNCTION__);
957
958    return ret;
959}
960