• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "radio_hw_stub"
18 #define LOG_NDEBUG 0
19 
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <pthread.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/prctl.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <time.h>
31 #include <unistd.h>
32 
33 #include <cutils/list.h>
34 #include <log/log.h>
35 
36 #include <hardware/hardware.h>
37 #include <hardware/radio.h>
38 #include <system/radio.h>
39 #include <system/radio_metadata.h>
40 
41 static const radio_hal_properties_t hw_properties = {
42     .class_id = RADIO_CLASS_AM_FM,
43     .implementor = "The Android Open Source Project",
44     .product = "Radio stub HAL",
45     .version = "0.1",
46     .serial = "0123456789",
47     .num_tuners = 1,
48     .num_audio_sources = 1,
49     .supports_capture = false,
50     .num_bands = 2,
51     .bands = {
52         {
53             .type = RADIO_BAND_FM,
54             .antenna_connected = true,
55             .lower_limit = 87900,
56             .upper_limit = 107900,
57             .num_spacings = 1,
58             .spacings = { 200 },
59             .fm = {
60                 .deemphasis = RADIO_DEEMPHASIS_75,
61                 .stereo = true,
62                 .rds = RADIO_RDS_US,
63                 .ta = false,
64                 .af = false,
65                 .ea = true,
66             }
67         },
68         {
69             .type = RADIO_BAND_AM,
70             .antenna_connected = true,
71             .lower_limit = 540,
72             .upper_limit = 1610,
73             .num_spacings = 1,
74             .spacings = { 10 },
75             .am = {
76                 .stereo = true,
77             }
78         }
79     }
80 };
81 
82 static const radio_metadata_clock_t hw_clock = {
83     .utc_seconds_since_epoch = 1234567890,
84     .timezone_offset_in_minutes = (-8 * 60),
85 };
86 
87 struct stub_radio_tuner {
88     struct radio_tuner interface;
89     struct stub_radio_device *dev;
90     radio_callback_t callback;
91     void *cookie;
92     radio_hal_band_config_t config;
93     radio_program_info_t program;
94     bool audio;
95     pthread_t callback_thread;
96     pthread_mutex_t lock;
97     pthread_cond_t  cond;
98     struct listnode command_list;
99 };
100 
101 struct stub_radio_device {
102     struct radio_hw_device device;
103     struct stub_radio_tuner *tuner;
104     pthread_mutex_t lock;
105 };
106 
107 
108 typedef enum {
109     CMD_EXIT,
110     CMD_CONFIG,
111     CMD_STEP,
112     CMD_SCAN,
113     CMD_TUNE,
114     CMD_CANCEL,
115     CMD_METADATA,
116     CMD_ANNOUNCEMENTS,
117     CMD_NUM
118 } thread_cmd_type_t;
119 
120 uint32_t thread_cmd_delay_ms[CMD_NUM] = {
121     [CMD_EXIT]          = 0,
122     [CMD_CONFIG]        = 50,
123     [CMD_STEP]          = 100,
124     [CMD_SCAN]          = 200,
125     [CMD_TUNE]          = 150,
126     [CMD_CANCEL]        = 0,
127     [CMD_METADATA]      = 1000,
128     [CMD_ANNOUNCEMENTS] = 1000
129 };
130 struct thread_command {
131     struct listnode node;
132     thread_cmd_type_t type;
133     struct timespec ts;
134     union {
135         unsigned int param;
136         radio_hal_band_config_t config;
137     };
138 };
139 
140 /* must be called with out->lock locked */
send_command_l(struct stub_radio_tuner * tuner,thread_cmd_type_t type,unsigned int delay_ms,void * param)141 static int send_command_l(struct stub_radio_tuner *tuner,
142                           thread_cmd_type_t type,
143                           unsigned int delay_ms,
144                           void *param)
145 {
146     struct thread_command *cmd = (struct thread_command *)calloc(1, sizeof(struct thread_command));
147     struct timespec ts;
148 
149     if (cmd == NULL)
150         return -ENOMEM;
151 
152     ALOGV("%s %d delay_ms %d", __func__, type, delay_ms);
153 
154     cmd->type = type;
155     if (param != NULL) {
156         if (cmd->type == CMD_CONFIG) {
157             cmd->config = *(radio_hal_band_config_t *)param;
158             ALOGV("%s CMD_CONFIG type %d", __func__, cmd->config.type);
159         } else
160             cmd->param = *(unsigned int *)param;
161     }
162 
163     clock_gettime(CLOCK_REALTIME, &ts);
164 
165     ts.tv_sec  += delay_ms/1000;
166     ts.tv_nsec += (delay_ms%1000) * 1000000;
167     if (ts.tv_nsec >= 1000000000) {
168         ts.tv_nsec -= 1000000000;
169         ts.tv_sec  += 1;
170     }
171     cmd->ts = ts;
172     list_add_tail(&tuner->command_list, &cmd->node);
173     pthread_cond_signal(&tuner->cond);
174     return 0;
175 }
176 
177 #define BITMAP_FILE_PATH "/data/misc/audioserver/android.png"
178 
add_bitmap_metadata(radio_metadata_t ** metadata,radio_metadata_key_t key,const char * source)179 static int add_bitmap_metadata(radio_metadata_t **metadata, radio_metadata_key_t key,
180                                const char *source)
181 {
182     int fd;
183     ssize_t ret = 0;
184     struct stat info;
185     void *data = NULL;
186     size_t size;
187 
188     fd = open(source, O_RDONLY);
189     if (fd < 0)
190         return -EPIPE;
191 
192     fstat(fd, &info);
193     size = info.st_size;
194     data = malloc(size);
195     if (data == NULL) {
196         ret = -ENOMEM;
197         goto exit;
198     }
199     ret = read(fd, data, size);
200     if (ret < 0)
201         goto exit;
202     ret = radio_metadata_add_raw(metadata, key, (const unsigned char *)data, size);
203 
204 exit:
205     close(fd);
206     free(data);
207     ALOGE_IF(ret != 0, "%s error %d", __func__, (int)ret);
208     return (int)ret;
209 }
210 
prepare_metadata(struct stub_radio_tuner * tuner,radio_metadata_t ** metadata,bool program)211 static int prepare_metadata(struct stub_radio_tuner *tuner,
212                             radio_metadata_t **metadata, bool program)
213 {
214     int ret = 0;
215     char text[RADIO_STRING_LEN_MAX];
216     struct timespec ts;
217 
218     if (metadata == NULL)
219         return -EINVAL;
220 
221     if (*metadata != NULL)
222         radio_metadata_deallocate(*metadata);
223 
224     *metadata = NULL;
225 
226     ret = radio_metadata_allocate(metadata, tuner->program.channel, 0);
227 
228     if (ret != 0)
229         return ret;
230 
231     if (program) {
232         ret = radio_metadata_add_int(metadata, RADIO_METADATA_KEY_RBDS_PTY, 5);
233         if (ret != 0)
234             goto exit;
235         ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_RDS_PS, "RockBand");
236         if (ret != 0)
237             goto exit;
238         ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH);
239         if (ret != 0 && ret != -EPIPE)
240             goto exit;
241         ret = radio_metadata_add_clock(metadata, RADIO_METADATA_KEY_CLOCK, &hw_clock);
242         if (ret != 0)
243             goto exit;
244     } else {
245         ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH);
246         if (ret != 0 && ret != -EPIPE)
247             goto exit;
248     }
249 
250     clock_gettime(CLOCK_REALTIME, &ts);
251     snprintf(text, RADIO_STRING_LEN_MAX, "Artist %ld", ts.tv_sec % 10);
252     ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_ARTIST, text);
253     if (ret != 0)
254         goto exit;
255 
256     snprintf(text, RADIO_STRING_LEN_MAX, "Song %ld", ts.tv_nsec % 10);
257     ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_TITLE, text);
258     if (ret != 0)
259         goto exit;
260 
261     return 0;
262 
263 exit:
264     radio_metadata_deallocate(*metadata);
265     *metadata = NULL;
266     return ret;
267 }
268 
callback_thread_loop(void * context)269 static void *callback_thread_loop(void *context)
270 {
271     struct stub_radio_tuner *tuner = (struct stub_radio_tuner *)context;
272     struct timespec ts = {0, 0};
273 
274     ALOGI("%s", __func__);
275 
276     prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
277 
278     pthread_mutex_lock(&tuner->lock);
279 
280     // Fields which are used to toggle the state of traffic announcements and
281     // ea announcements at random. They are access protected by tuner->lock.
282     bool ea_state = false;
283 
284     while (true) {
285         struct thread_command *cmd = NULL;
286         struct listnode *item;
287         struct listnode *tmp;
288         struct timespec cur_ts;
289         bool got_cancel = false;
290         bool send_meta_data = false;
291 
292         if (list_empty(&tuner->command_list) || ts.tv_sec != 0) {
293             ALOGV("%s SLEEPING", __func__);
294             if (ts.tv_sec != 0) {
295                 ALOGV("%s SLEEPING with timeout", __func__);
296                 pthread_cond_timedwait(&tuner->cond, &tuner->lock, &ts);
297             } else {
298                 ALOGV("%s SLEEPING forever", __func__);
299                 pthread_cond_wait(&tuner->cond, &tuner->lock);
300             }
301             ts.tv_sec = 0;
302             ALOGV("%s RUNNING", __func__);
303         }
304 
305         clock_gettime(CLOCK_REALTIME, &cur_ts);
306 
307         list_for_each_safe(item, tmp, &tuner->command_list) {
308             cmd = node_to_item(item, struct thread_command, node);
309 
310             if (got_cancel && (cmd->type == CMD_STEP || cmd->type == CMD_SCAN ||
311                     cmd->type == CMD_TUNE || cmd->type == CMD_METADATA ||
312                     cmd->type == CMD_ANNOUNCEMENTS)) {
313                  list_remove(item);
314                  free(cmd);
315                  continue;
316             }
317 
318             if ((cmd->ts.tv_sec < cur_ts.tv_sec) ||
319                     ((cmd->ts.tv_sec == cur_ts.tv_sec) && (cmd->ts.tv_nsec < cur_ts.tv_nsec))) {
320                 radio_hal_event_t event;
321                 radio_metadata_t *metadata = NULL;
322 
323                 event.type = RADIO_EVENT_HW_FAILURE;
324                 list_remove(item);
325 
326                 ALOGV("%s processing command %d time %ld.%ld", __func__, cmd->type, cmd->ts.tv_sec,
327                       cmd->ts.tv_nsec);
328 
329                 switch (cmd->type) {
330                 default:
331                 case CMD_EXIT:
332                     free(cmd);
333                     goto exit;
334 
335                 case CMD_CONFIG: {
336                     tuner->config = cmd->config;
337                     tuner->config.antenna_connected = true;
338                     event.type = RADIO_EVENT_CONFIG;
339                     event.config = tuner->config;
340                     ALOGV("%s CMD_CONFIG type %d low %d up %d",
341                           __func__, tuner->config.type,
342                           tuner->config.lower_limit, tuner->config.upper_limit);
343                     if (tuner->config.type == RADIO_BAND_FM) {
344                         ALOGV("  - stereo %d\n  - rds %d\n  - ta %d\n  - af %d\n"
345                               "  - ea %d\n",
346                               tuner->config.fm.stereo, tuner->config.fm.rds,
347                               tuner->config.fm.ta, tuner->config.fm.af,
348                               tuner->config.fm.ea);
349                     } else {
350                         ALOGV("  - stereo %d", tuner->config.am.stereo);
351                     }
352                 } break;
353 
354                 case CMD_STEP: {
355                     int frequency;
356                     frequency = tuner->program.channel;
357                     if (cmd->param == RADIO_DIRECTION_UP) {
358                         frequency += tuner->config.spacings[0];
359                     } else {
360                         frequency -= tuner->config.spacings[0];
361                     }
362                     if (frequency > (int)tuner->config.upper_limit) {
363                         frequency = tuner->config.lower_limit;
364                     }
365                     if (frequency < (int)tuner->config.lower_limit) {
366                         frequency = tuner->config.upper_limit;
367                     }
368                     tuner->program.channel = frequency;
369                     tuner->program.tuned  = (frequency / (tuner->config.spacings[0] * 5)) % 2;
370                     tuner->program.signal_strength = 20;
371                     if (tuner->config.type == RADIO_BAND_FM)
372                         tuner->program.stereo = false;
373                     else
374                         tuner->program.stereo = false;
375                     prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
376 
377                     event.type = RADIO_EVENT_TUNED;
378                     event.info = tuner->program;
379                 } break;
380 
381                 case CMD_SCAN: {
382                     int frequency;
383                     frequency = tuner->program.channel;
384                     if (cmd->param == RADIO_DIRECTION_UP) {
385                         frequency += tuner->config.spacings[0] * 25;
386                     } else {
387                         frequency -= tuner->config.spacings[0] * 25;
388                     }
389                     if (frequency > (int)tuner->config.upper_limit) {
390                         frequency = tuner->config.lower_limit;
391                     }
392                     if (frequency < (int)tuner->config.lower_limit) {
393                         frequency = tuner->config.upper_limit;
394                     }
395                     tuner->program.channel = (unsigned int)frequency;
396                     tuner->program.tuned  = true;
397                     if (tuner->config.type == RADIO_BAND_FM)
398                         tuner->program.stereo = tuner->config.fm.stereo;
399                     else
400                         tuner->program.stereo = tuner->config.am.stereo;
401                     tuner->program.signal_strength = 50;
402                     prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
403 
404                     event.type = RADIO_EVENT_TUNED;
405                     event.info = tuner->program;
406                     send_meta_data = true;
407                 } break;
408 
409                 case CMD_TUNE: {
410                     tuner->program.channel = cmd->param;
411                     tuner->program.tuned  = (tuner->program.channel /
412                                                 (tuner->config.spacings[0] * 5)) % 2;
413 
414                     if (tuner->program.tuned) {
415                         send_command_l(tuner, CMD_ANNOUNCEMENTS, thread_cmd_delay_ms[CMD_ANNOUNCEMENTS], NULL);
416                     }
417                     tuner->program.signal_strength = 100;
418                     if (tuner->config.type == RADIO_BAND_FM)
419                         tuner->program.stereo =
420                                 tuner->program.tuned ? tuner->config.fm.stereo : false;
421                     else
422                         tuner->program.stereo =
423                             tuner->program.tuned ? tuner->config.am.stereo : false;
424                     prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
425 
426                     event.type = RADIO_EVENT_TUNED;
427                     event.info = tuner->program;
428                     send_meta_data = true;
429                 } break;
430 
431                 case CMD_METADATA: {
432                     int ret = prepare_metadata(tuner, &metadata, false);
433                     if (ret == 0) {
434                         event.type = RADIO_EVENT_METADATA;
435                         event.metadata = metadata;
436                     }
437                 } break;
438 
439                 case CMD_CANCEL: {
440                     got_cancel = true;
441                 } break;
442 
443                 // Fire emergency announcements if they are enabled in the config. Stub
444                 // implementation simply fires an announcement for 5 second
445                 // duration with a gap of 5 seconds.
446                 case CMD_ANNOUNCEMENTS: {
447                     ALOGV("In annoucements. %d %d %d\n",
448                           ea_state, tuner->config.type, tuner->config.fm.ea);
449                     if (tuner->config.type == RADIO_BAND_FM ||
450                         tuner->config.type == RADIO_BAND_FM_HD) {
451                         if (ea_state) {
452                             ea_state = false;
453                             event.type = RADIO_EVENT_EA;
454                         } else if (tuner->config.fm.ea) {
455                             ea_state = true;
456                             event.type = RADIO_EVENT_EA;
457                         }
458                         event.on = ea_state;
459 
460                         if (tuner->config.fm.ea) {
461                             send_command_l(tuner, CMD_ANNOUNCEMENTS, 5000, NULL);
462                         }
463                     }
464                 } break;
465                 }
466                 if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) {
467                     pthread_mutex_unlock(&tuner->lock);
468                     tuner->callback(&event, tuner->cookie);
469                     pthread_mutex_lock(&tuner->lock);
470                     if (event.type == RADIO_EVENT_METADATA && metadata != NULL) {
471                         radio_metadata_deallocate(metadata);
472                         metadata = NULL;
473                     }
474                 }
475                 ALOGV("%s processed command %d", __func__, cmd->type);
476                 free(cmd);
477             } else {
478                 if ((ts.tv_sec == 0) ||
479                         (cmd->ts.tv_sec < ts.tv_sec) ||
480                         ((cmd->ts.tv_sec == ts.tv_sec) && (cmd->ts.tv_nsec < ts.tv_nsec))) {
481                     ts.tv_sec = cmd->ts.tv_sec;
482                     ts.tv_nsec = cmd->ts.tv_nsec;
483                 }
484             }
485         }
486 
487         if (send_meta_data) {
488             list_for_each_safe(item, tmp, &tuner->command_list) {
489                 cmd = node_to_item(item, struct thread_command, node);
490                 if (cmd->type == CMD_METADATA) {
491                     list_remove(item);
492                     free(cmd);
493                 }
494             }
495             send_command_l(tuner, CMD_METADATA, thread_cmd_delay_ms[CMD_METADATA], NULL);
496         }
497     }
498 
499 exit:
500     pthread_mutex_unlock(&tuner->lock);
501 
502     ALOGV("%s Exiting", __func__);
503 
504     return NULL;
505 }
506 
507 
tuner_set_configuration(const struct radio_tuner * tuner,const radio_hal_band_config_t * config)508 static int tuner_set_configuration(const struct radio_tuner *tuner,
509                          const radio_hal_band_config_t *config)
510 {
511     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
512     int status = 0;
513 
514     ALOGI("%s stub_tuner %p", __func__, stub_tuner);
515     pthread_mutex_lock(&stub_tuner->lock);
516     if (config == NULL) {
517         status = -EINVAL;
518         goto exit;
519     }
520     if (config->lower_limit > config->upper_limit) {
521         status = -EINVAL;
522         goto exit;
523     }
524     send_command_l(stub_tuner, CMD_CANCEL, thread_cmd_delay_ms[CMD_CANCEL], NULL);
525     send_command_l(stub_tuner, CMD_CONFIG, thread_cmd_delay_ms[CMD_CONFIG], (void *)config);
526 
527 exit:
528     pthread_mutex_unlock(&stub_tuner->lock);
529     return status;
530 }
531 
tuner_get_configuration(const struct radio_tuner * tuner,radio_hal_band_config_t * config)532 static int tuner_get_configuration(const struct radio_tuner *tuner,
533                                    radio_hal_band_config_t *config)
534 {
535     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
536     int status = 0;
537     struct listnode *item;
538     radio_hal_band_config_t *src_config;
539 
540     ALOGI("%s stub_tuner %p", __func__, stub_tuner);
541     pthread_mutex_lock(&stub_tuner->lock);
542     src_config = &stub_tuner->config;
543 
544     if (config == NULL) {
545         status = -EINVAL;
546         goto exit;
547     }
548     list_for_each(item, &stub_tuner->command_list) {
549         struct thread_command *cmd = node_to_item(item, struct thread_command, node);
550         if (cmd->type == CMD_CONFIG) {
551             src_config = &cmd->config;
552         }
553     }
554     *config = *src_config;
555 
556 exit:
557     pthread_mutex_unlock(&stub_tuner->lock);
558     return status;
559 }
560 
tuner_step(const struct radio_tuner * tuner,radio_direction_t direction,bool skip_sub_channel)561 static int tuner_step(const struct radio_tuner *tuner,
562                      radio_direction_t direction, bool skip_sub_channel)
563 {
564     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
565 
566     ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
567           __func__, stub_tuner, direction, skip_sub_channel);
568 
569     pthread_mutex_lock(&stub_tuner->lock);
570     send_command_l(stub_tuner, CMD_STEP, thread_cmd_delay_ms[CMD_STEP], &direction);
571     pthread_mutex_unlock(&stub_tuner->lock);
572     return 0;
573 }
574 
tuner_scan(const struct radio_tuner * tuner,radio_direction_t direction,bool skip_sub_channel)575 static int tuner_scan(const struct radio_tuner *tuner,
576                      radio_direction_t direction, bool skip_sub_channel)
577 {
578     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
579 
580     ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
581           __func__, stub_tuner, direction, skip_sub_channel);
582 
583     pthread_mutex_lock(&stub_tuner->lock);
584     send_command_l(stub_tuner, CMD_SCAN, thread_cmd_delay_ms[CMD_SCAN], &direction);
585     pthread_mutex_unlock(&stub_tuner->lock);
586     return 0;
587 }
588 
tuner_tune(const struct radio_tuner * tuner,unsigned int channel,unsigned int sub_channel)589 static int tuner_tune(const struct radio_tuner *tuner,
590                      unsigned int channel, unsigned int sub_channel)
591 {
592     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
593 
594     ALOGI("%s stub_tuner %p channel %d, sub_channel %d",
595           __func__, stub_tuner, channel, sub_channel);
596 
597     pthread_mutex_lock(&stub_tuner->lock);
598     if (channel < stub_tuner->config.lower_limit || channel > stub_tuner->config.upper_limit) {
599         pthread_mutex_unlock(&stub_tuner->lock);
600         ALOGI("%s channel out of range", __func__);
601         return -EINVAL;
602     }
603     send_command_l(stub_tuner, CMD_TUNE, thread_cmd_delay_ms[CMD_TUNE], &channel);
604     pthread_mutex_unlock(&stub_tuner->lock);
605     return 0;
606 }
607 
tuner_cancel(const struct radio_tuner * tuner)608 static int tuner_cancel(const struct radio_tuner *tuner)
609 {
610     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
611 
612     ALOGI("%s stub_tuner %p", __func__, stub_tuner);
613 
614     pthread_mutex_lock(&stub_tuner->lock);
615     send_command_l(stub_tuner, CMD_CANCEL, thread_cmd_delay_ms[CMD_CANCEL], NULL);
616     pthread_mutex_unlock(&stub_tuner->lock);
617     return 0;
618 }
619 
tuner_get_program_information(const struct radio_tuner * tuner,radio_program_info_t * info)620 static int tuner_get_program_information(const struct radio_tuner *tuner,
621                                         radio_program_info_t *info)
622 {
623     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
624     int status = 0;
625     radio_metadata_t *metadata;
626 
627     ALOGI("%s stub_tuner %p", __func__, stub_tuner);
628     pthread_mutex_lock(&stub_tuner->lock);
629     if (info == NULL) {
630         status = -EINVAL;
631         goto exit;
632     }
633     metadata = info->metadata;
634     *info = stub_tuner->program;
635     info->metadata = metadata;
636     if (metadata == NULL) {
637         ALOGE("%s metadata is a nullptr", __func__);
638         status = -EINVAL;
639         goto exit;
640     }
641     if (stub_tuner->program.metadata != NULL)
642         radio_metadata_add_metadata(&info->metadata, stub_tuner->program.metadata);
643 
644 exit:
645     pthread_mutex_unlock(&stub_tuner->lock);
646     return status;
647 }
648 
rdev_get_properties(const struct radio_hw_device * dev,radio_hal_properties_t * properties)649 static int rdev_get_properties(const struct radio_hw_device *dev,
650                                 radio_hal_properties_t *properties)
651 {
652     ALOGI("%s", __func__);
653     if (properties == NULL)
654         return -EINVAL;
655     memcpy(properties, &hw_properties, sizeof(radio_hal_properties_t));
656     return 0;
657 }
658 
rdev_open_tuner(const struct radio_hw_device * dev,const radio_hal_band_config_t * config,bool audio,radio_callback_t callback,void * cookie,const struct radio_tuner ** tuner)659 static int rdev_open_tuner(const struct radio_hw_device *dev,
660                           const radio_hal_band_config_t *config,
661                           bool audio,
662                           radio_callback_t callback,
663                           void *cookie,
664                           const struct radio_tuner **tuner)
665 {
666     struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
667     int status = 0;
668 
669     ALOGI("%s rdev %p", __func__, rdev);
670     pthread_mutex_lock(&rdev->lock);
671 
672     if (rdev->tuner != NULL) {
673         ALOGE("Can't open tuner twice");
674         status = -ENOSYS;
675         goto exit;
676     }
677 
678     if (config == NULL || callback == NULL || tuner == NULL) {
679         status = -EINVAL;
680         goto exit;
681     }
682 
683     rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner));
684     if (rdev->tuner == NULL) {
685         status = -ENOMEM;
686         goto exit;
687     }
688 
689     rdev->tuner->interface.set_configuration = tuner_set_configuration;
690     rdev->tuner->interface.get_configuration = tuner_get_configuration;
691     rdev->tuner->interface.scan = tuner_scan;
692     rdev->tuner->interface.step = tuner_step;
693     rdev->tuner->interface.tune = tuner_tune;
694     rdev->tuner->interface.cancel = tuner_cancel;
695     rdev->tuner->interface.get_program_information = tuner_get_program_information;
696 
697     rdev->tuner->audio = audio;
698     rdev->tuner->callback = callback;
699     rdev->tuner->cookie = cookie;
700 
701     rdev->tuner->dev = rdev;
702 
703     pthread_mutex_init(&rdev->tuner->lock, (const pthread_mutexattr_t *) NULL);
704     pthread_cond_init(&rdev->tuner->cond, (const pthread_condattr_t *) NULL);
705     pthread_create(&rdev->tuner->callback_thread, (const pthread_attr_t *) NULL,
706                         callback_thread_loop, rdev->tuner);
707     list_init(&rdev->tuner->command_list);
708 
709     pthread_mutex_lock(&rdev->tuner->lock);
710     send_command_l(rdev->tuner, CMD_CONFIG, thread_cmd_delay_ms[CMD_CONFIG], (void *)config);
711     pthread_mutex_unlock(&rdev->tuner->lock);
712 
713     *tuner = &rdev->tuner->interface;
714 
715 exit:
716     pthread_mutex_unlock(&rdev->lock);
717     ALOGI("%s DONE", __func__);
718     return status;
719 }
720 
rdev_close_tuner(const struct radio_hw_device * dev,const struct radio_tuner * tuner)721 static int rdev_close_tuner(const struct radio_hw_device *dev,
722                             const struct radio_tuner *tuner)
723 {
724     struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
725     struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
726     int status = 0;
727 
728     ALOGI("%s tuner %p", __func__, tuner);
729     pthread_mutex_lock(&rdev->lock);
730 
731     if (tuner == NULL) {
732         status = -EINVAL;
733         goto exit;
734     }
735 
736     pthread_mutex_lock(&stub_tuner->lock);
737     stub_tuner->callback = NULL;
738     send_command_l(stub_tuner, CMD_EXIT, thread_cmd_delay_ms[CMD_EXIT], NULL);
739     pthread_mutex_unlock(&stub_tuner->lock);
740     pthread_join(stub_tuner->callback_thread, (void **) NULL);
741 
742     if (stub_tuner->program.metadata != NULL)
743         radio_metadata_deallocate(stub_tuner->program.metadata);
744 
745     free(stub_tuner);
746     rdev->tuner = NULL;
747 
748 exit:
749     pthread_mutex_unlock(&rdev->lock);
750     return status;
751 }
752 
rdev_close(hw_device_t * device)753 static int rdev_close(hw_device_t *device)
754 {
755     struct stub_radio_device *rdev = (struct stub_radio_device *)device;
756     if (rdev != NULL) {
757         free(rdev->tuner);
758     }
759     free(rdev);
760     return 0;
761 }
762 
rdev_open(const hw_module_t * module,const char * name,hw_device_t ** device)763 static int rdev_open(const hw_module_t* module, const char* name,
764                      hw_device_t** device)
765 {
766     struct stub_radio_device *rdev;
767 
768     if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0)
769         return -EINVAL;
770 
771     rdev = calloc(1, sizeof(struct stub_radio_device));
772     if (!rdev)
773         return -ENOMEM;
774 
775     rdev->device.common.tag = HARDWARE_DEVICE_TAG;
776     rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0;
777     rdev->device.common.module = (struct hw_module_t *) module;
778     rdev->device.common.close = rdev_close;
779     rdev->device.get_properties = rdev_get_properties;
780     rdev->device.open_tuner = rdev_open_tuner;
781     rdev->device.close_tuner = rdev_close_tuner;
782 
783     pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL);
784 
785     *device = &rdev->device.common;
786 
787     return 0;
788 }
789 
790 
791 static struct hw_module_methods_t hal_module_methods = {
792     .open = rdev_open,
793 };
794 
795 struct radio_module HAL_MODULE_INFO_SYM = {
796     .common = {
797         .tag = HARDWARE_MODULE_TAG,
798         .module_api_version = RADIO_MODULE_API_VERSION_1_0,
799         .hal_api_version = HARDWARE_HAL_API_VERSION,
800         .id = RADIO_HARDWARE_MODULE_ID,
801         .name = "Stub radio HAL",
802         .author = "The Android Open Source Project",
803         .methods = &hal_module_methods,
804     },
805 };
806