• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 using namespace android;
35 
36 static jclass gArrayListClass;
37 static struct {
38     jmethodID    add;
39 } gArrayListMethods;
40 
41 static const char* const kRadioManagerClassPathName = "android/hardware/radio/RadioManager";
42 static jclass gRadioManagerClass;
43 
44 static const char* const kRadioModuleClassPathName = "android/hardware/radio/RadioModule";
45 static jclass gRadioModuleClass;
46 static struct {
47     jfieldID    mNativeContext;
48     jfieldID    mId;
49 } gModuleFields;
50 static jmethodID gPostEventFromNative;
51 
52 static const char* const kModulePropertiesClassPathName =
53                                      "android/hardware/radio/RadioManager$ModuleProperties";
54 static jclass gModulePropertiesClass;
55 static jmethodID gModulePropertiesCstor;
56 
57 
58 static const char* const kRadioBandDescriptorClassPathName =
59                              "android/hardware/radio/RadioManager$BandDescriptor";
60 static jclass gRadioBandDescriptorClass;
61 static struct {
62     jfieldID mRegion;
63     jfieldID mType;
64     jfieldID mLowerLimit;
65     jfieldID mUpperLimit;
66     jfieldID mSpacing;
67 } gRadioBandDescriptorFields;
68 
69 static const char* const kRadioFmBandDescriptorClassPathName =
70                              "android/hardware/radio/RadioManager$FmBandDescriptor";
71 static jclass gRadioFmBandDescriptorClass;
72 static jmethodID gRadioFmBandDescriptorCstor;
73 
74 static const char* const kRadioAmBandDescriptorClassPathName =
75                              "android/hardware/radio/RadioManager$AmBandDescriptor";
76 static jclass gRadioAmBandDescriptorClass;
77 static jmethodID gRadioAmBandDescriptorCstor;
78 
79 static const char* const kRadioBandConfigClassPathName =
80                              "android/hardware/radio/RadioManager$BandConfig";
81 static jclass gRadioBandConfigClass;
82 static struct {
83     jfieldID mDescriptor;
84 } gRadioBandConfigFields;
85 
86 
87 static const char* const kRadioFmBandConfigClassPathName =
88                              "android/hardware/radio/RadioManager$FmBandConfig";
89 static jclass gRadioFmBandConfigClass;
90 static jmethodID gRadioFmBandConfigCstor;
91 static struct {
92     jfieldID mStereo;
93     jfieldID mRds;
94     jfieldID mTa;
95     jfieldID mAf;
96     jfieldID mEa;
97 } gRadioFmBandConfigFields;
98 
99 static const char* const kRadioAmBandConfigClassPathName =
100                              "android/hardware/radio/RadioManager$AmBandConfig";
101 static jclass gRadioAmBandConfigClass;
102 static jmethodID gRadioAmBandConfigCstor;
103 static struct {
104     jfieldID mStereo;
105 } gRadioAmBandConfigFields;
106 
107 
108 static const char* const kRadioProgramInfoClassPathName =
109                              "android/hardware/radio/RadioManager$ProgramInfo";
110 static jclass gRadioProgramInfoClass;
111 static jmethodID gRadioProgramInfoCstor;
112 
113 static const char* const kRadioMetadataClassPathName =
114                              "android/hardware/radio/RadioMetadata";
115 static jclass gRadioMetadataClass;
116 static jmethodID gRadioMetadataCstor;
117 static struct {
118     jmethodID putIntFromNative;
119     jmethodID putStringFromNative;
120     jmethodID putBitmapFromNative;
121     jmethodID putClockFromNative;
122 } gRadioMetadataMethods;
123 
124 static Mutex gLock;
125 
126 enum {
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 
getRadio(JNIEnv * env,jobject thiz)140 static 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 
setRadio(JNIEnv * env,jobject thiz,const sp<Radio> & module)147 static 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 
convertBandDescriptorFromNative(JNIEnv * env,jobject * jBandDescriptor,const radio_band_config_t * nBandconfig)161 static 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 
convertBandConfigFromNative(JNIEnv * env,jobject * jBandConfig,const radio_band_config_t * nBandconfig)196 static 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 
convertMetadataFromNative(JNIEnv * env,jobject * jMetadata,const radio_metadata_t * nMetadata)231 static 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 
convertProgramInfoFromNative(JNIEnv * env,jobject * jProgramInfo,const radio_program_info_t * nProgramInfo)309 static 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 
convertBandConfigToNative(JNIEnv * env,radio_band_config_t * nBandconfig,jobject jBandConfig)339 static 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 
384 static jint
android_hardware_Radio_listModules(JNIEnv * env,jobject clazz,jobject jModules)385 android_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 
471 class JNIRadioCallback: public RadioCallback
472 {
473 public:
474     JNIRadioCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
475     ~JNIRadioCallback();
476 
477     virtual void onEvent(struct radio_event *event);
478 
479 private:
480     jclass      mClass;     // Reference to Radio class
481     jobject     mObject;    // Weak ref to Radio Java object to call on
482 };
483 
JNIRadioCallback(JNIEnv * env,jobject thiz,jobject weak_thiz)484 JNIRadioCallback::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 
~JNIRadioCallback()501 JNIRadioCallback::~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 
onEvent(struct radio_event * event)512 void 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 
562 static void
android_hardware_Radio_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jConfig,jboolean withAudio)563 android_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 
591 static void
android_hardware_Radio_close(JNIEnv * env,jobject thiz)592 android_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 
603 static void
android_hardware_Radio_finalize(JNIEnv * env,jobject thiz)604 android_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 
614 static jint
android_hardware_Radio_setConfiguration(JNIEnv * env,jobject thiz,jobject jConfig)615 android_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 
638 static jint
android_hardware_Radio_getConfiguration(JNIEnv * env,jobject thiz,jobjectArray jConfigs)639 android_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 
666 static jint
android_hardware_Radio_setMute(JNIEnv * env,jobject thiz,jboolean mute)667 android_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 
678 static jboolean
android_hardware_Radio_getMute(JNIEnv * env,jobject thiz)679 android_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 
694 static jint
android_hardware_Radio_step(JNIEnv * env,jobject thiz,jint direction,jboolean skipSubChannel)695 android_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 
706 static jint
android_hardware_Radio_scan(JNIEnv * env,jobject thiz,jint direction,jboolean skipSubChannel)707 android_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 
718 static jint
android_hardware_Radio_tune(JNIEnv * env,jobject thiz,jint channel,jint subChannel)719 android_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 
730 static jint
android_hardware_Radio_cancel(JNIEnv * env,jobject thiz)731 android_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 
742 static jint
android_hardware_Radio_getProgramInformation(JNIEnv * env,jobject thiz,jobjectArray jInfos)743 android_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 
769 exit:
770     if (jInfo != NULL) {
771         env->DeleteLocalRef(jInfo);
772     }
773     return jStatus;
774 }
775 
776 static jboolean
android_hardware_Radio_isAntennaConnected(JNIEnv * env,jobject thiz)777 android_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 
796 static jboolean
android_hardware_Radio_hasControl(JNIEnv * env,jobject thiz)797 android_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 
815 static JNINativeMethod gMethods[] = {
816     {"listModules",
817         "(Ljava/util/List;)I",
818         (void *)android_hardware_Radio_listModules},
819 };
820 
821 static 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 
register_android_hardware_Radio(JNIEnv * env)866 int 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