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