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