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