Home | History | Annotate | Download | only in radio
      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 */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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