Home | History | Annotate | Download | only in src
      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_metadata"
     18 /*#define LOG_NDEBUG 0*/
     19 
     20 #include <errno.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <limits.h>
     24 #include <system/radio.h>
     25 #include <system/radio_metadata.h>
     26 #include <radio_metadata_hidden.h>
     27 #include <cutils/log.h>
     28 
     29 const radio_metadata_type_t metadata_key_type_table[] =
     30 {
     31     RADIO_METADATA_TYPE_TEXT,
     32     RADIO_METADATA_TYPE_TEXT,
     33     RADIO_METADATA_TYPE_INT,
     34     RADIO_METADATA_TYPE_INT,
     35     RADIO_METADATA_TYPE_TEXT,
     36     RADIO_METADATA_TYPE_TEXT,
     37     RADIO_METADATA_TYPE_TEXT,
     38     RADIO_METADATA_TYPE_TEXT,
     39     RADIO_METADATA_TYPE_TEXT,
     40     RADIO_METADATA_TYPE_RAW,
     41     RADIO_METADATA_TYPE_RAW,
     42     RADIO_METADATA_TYPE_CLOCK,
     43 };
     44 
     45 /**
     46  * private functions
     47  */
     48 
     49 bool is_valid_metadata_key(const radio_metadata_key_t key)
     50 {
     51     if (key < RADIO_METADATA_KEY_MIN || key > RADIO_METADATA_KEY_MAX) {
     52         return false;
     53     }
     54     return true;
     55 }
     56 
     57 int check_size(radio_metadata_buffer_t **metadata_ptr, const unsigned int size_int)
     58 {
     59     radio_metadata_buffer_t *metadata = *metadata_ptr;
     60     unsigned int index_offset = metadata->size_int - metadata->count - 1;
     61     unsigned int data_offset = *((unsigned int *)metadata + index_offset);
     62     unsigned int req_size_int;
     63     unsigned int new_size_int;
     64 
     65     if (size_int == 0) {
     66         return 0;
     67     }
     68 
     69     req_size_int = data_offset + metadata->count + 1 + 1 + size_int;
     70     /* do not grow buffer if it can accommodate the new entry plus an additional index entry */
     71 
     72     if (req_size_int <= metadata->size_int) {
     73         return 0;
     74     }
     75 
     76     if (req_size_int > RADIO_METADATA_MAX_SIZE || metadata->size_int >= RADIO_METADATA_MAX_SIZE) {
     77         return -ENOMEM;
     78     }
     79     /* grow meta data buffer by a factor of 2 until new data fits */
     80     new_size_int = metadata->size_int;
     81     while (new_size_int < req_size_int)
     82         new_size_int *= 2;
     83 
     84     ALOGV("%s growing from %u to %u", __func__, metadata->size_int, new_size_int);
     85     metadata = realloc(metadata, new_size_int * sizeof(unsigned int));
     86     /* move index table */
     87     memmove((unsigned int *)metadata + new_size_int - (metadata->count + 1),
     88             (unsigned int *)metadata + metadata->size_int - (metadata->count + 1),
     89             (metadata->count + 1) * sizeof(unsigned int));
     90     metadata->size_int = new_size_int;
     91 
     92     *metadata_ptr = metadata;
     93     return 0;
     94 }
     95 
     96 /* checks on size and key validity are done before calling this function */
     97 int add_metadata(radio_metadata_buffer_t **metadata_ptr,
     98                  const radio_metadata_key_t key,
     99                  const radio_metadata_type_t type,
    100                  const void *value,
    101                  const unsigned int size)
    102 {
    103     unsigned int entry_size_int;
    104     int ret;
    105     radio_metadata_entry_t *entry;
    106     unsigned int index_offset;
    107     unsigned int data_offset;
    108     radio_metadata_buffer_t *metadata = *metadata_ptr;
    109 
    110     entry_size_int = size + sizeof(radio_metadata_entry_t);
    111     entry_size_int = (entry_size_int + sizeof(unsigned int) - 1) / sizeof(unsigned int);
    112 
    113     ret = check_size(metadata_ptr, entry_size_int);
    114     if (ret < 0) {
    115         return ret;
    116     }
    117     metadata = *metadata_ptr;
    118     index_offset = metadata->size_int - metadata->count - 1;
    119     data_offset = *((unsigned int *)metadata + index_offset);
    120 
    121     entry = (radio_metadata_entry_t *)((unsigned int *)metadata + data_offset);
    122     entry->key = key;
    123     entry->type = type;
    124     entry->size = size;
    125     memcpy(entry->data, value, size);
    126 
    127     data_offset += entry_size_int;
    128     *((unsigned int *)metadata + index_offset -1) = data_offset;
    129     metadata->count++;
    130     return 0;
    131 }
    132 
    133 radio_metadata_entry_t *get_entry_at_index(
    134                                     const radio_metadata_buffer_t *metadata,
    135                                     const unsigned index,
    136                                     bool check)
    137 {
    138     unsigned int index_offset = metadata->size_int - index - 1;
    139     unsigned int data_offset = *((unsigned int *)metadata + index_offset);
    140 
    141     if (check) {
    142         if (index >= metadata->count) {
    143             return NULL;
    144         }
    145         unsigned int min_offset;
    146         unsigned int max_offset;
    147         unsigned int min_entry_size_int;
    148         min_offset = (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) /
    149                         sizeof(unsigned int);
    150         if (data_offset < min_offset) {
    151             return NULL;
    152         }
    153         min_entry_size_int = 1 + sizeof(radio_metadata_entry_t);
    154         min_entry_size_int = (min_entry_size_int + sizeof(unsigned int) - 1) / sizeof(unsigned int);
    155         max_offset = metadata->size_int - metadata->count - 1 - min_entry_size_int;
    156         if (data_offset > max_offset) {
    157             return NULL;
    158         }
    159     }
    160     return (radio_metadata_entry_t *)((unsigned int *)metadata + data_offset);
    161 }
    162 
    163 /**
    164  * metadata API functions
    165  */
    166 
    167 radio_metadata_type_t radio_metadata_type_of_key(const radio_metadata_key_t key)
    168 {
    169     if (!is_valid_metadata_key(key)) {
    170         return RADIO_METADATA_TYPE_INVALID;
    171     }
    172     return metadata_key_type_table[key - RADIO_METADATA_KEY_MIN];
    173 }
    174 
    175 int radio_metadata_allocate(radio_metadata_t **metadata,
    176                             const unsigned int channel,
    177                             const unsigned int sub_channel)
    178 {
    179     radio_metadata_buffer_t *metadata_buf =
    180             (radio_metadata_buffer_t *)calloc(RADIO_METADATA_DEFAULT_SIZE, sizeof(unsigned int));
    181     if (metadata_buf == NULL) {
    182         return -ENOMEM;
    183     }
    184 
    185     metadata_buf->channel = channel;
    186     metadata_buf->sub_channel = sub_channel;
    187     metadata_buf->size_int = RADIO_METADATA_DEFAULT_SIZE;
    188     *((unsigned int *)metadata_buf + RADIO_METADATA_DEFAULT_SIZE - 1) =
    189             (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) /
    190                 sizeof(unsigned int);
    191     *metadata = (radio_metadata_t *)metadata_buf;
    192     return 0;
    193 }
    194 
    195 void radio_metadata_deallocate(radio_metadata_t *metadata)
    196 {
    197     free(metadata);
    198 }
    199 
    200 int radio_metadata_add_int(radio_metadata_t **metadata,
    201                            const radio_metadata_key_t key,
    202                            const int value)
    203 {
    204     radio_metadata_type_t type = radio_metadata_type_of_key(key);
    205     if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_INT) {
    206         return -EINVAL;
    207     }
    208     return add_metadata((radio_metadata_buffer_t **)metadata,
    209                         key, type, &value, sizeof(int));
    210 }
    211 
    212 int radio_metadata_add_text(radio_metadata_t **metadata,
    213                             const radio_metadata_key_t key,
    214                             const char *value)
    215 {
    216     radio_metadata_type_t type = radio_metadata_type_of_key(key);
    217     if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_TEXT ||
    218             value == NULL || strlen(value) >= RADIO_METADATA_TEXT_LEN_MAX) {
    219         return -EINVAL;
    220     }
    221     return add_metadata((radio_metadata_buffer_t **)metadata, key, type, value, strlen(value) + 1);
    222 }
    223 
    224 int radio_metadata_add_raw(radio_metadata_t **metadata,
    225                            const radio_metadata_key_t key,
    226                            const unsigned char *value,
    227                            const unsigned int size)
    228 {
    229     radio_metadata_type_t type = radio_metadata_type_of_key(key);
    230     if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_RAW || value == NULL) {
    231         return -EINVAL;
    232     }
    233     return add_metadata((radio_metadata_buffer_t **)metadata, key, type, value, size);
    234 }
    235 
    236 int radio_metadata_add_clock(radio_metadata_t **metadata,
    237                              const radio_metadata_key_t key,
    238                              const radio_metadata_clock_t *clock) {
    239     radio_metadata_type_t type = radio_metadata_type_of_key(key);
    240     if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_CLOCK ||
    241         clock == NULL || clock->timezone_offset_in_minutes < (-12 * 60) ||
    242         clock->timezone_offset_in_minutes > (14 * 60)) {
    243         return -EINVAL;
    244     }
    245     return add_metadata(
    246         (radio_metadata_buffer_t **)metadata, key, type, clock, sizeof(radio_metadata_clock_t));
    247 }
    248 
    249 int radio_metadata_add_metadata(radio_metadata_t **dst_metadata,
    250                            radio_metadata_t *src_metadata)
    251 {
    252     radio_metadata_buffer_t *src_metadata_buf = (radio_metadata_buffer_t *)src_metadata;
    253     radio_metadata_buffer_t *dst_metadata_buf;
    254     int status;
    255     unsigned int index;
    256 
    257     if (dst_metadata == NULL || src_metadata == NULL) {
    258         return -EINVAL;
    259     }
    260     if (*dst_metadata == NULL) {
    261         status = radio_metadata_allocate(dst_metadata, src_metadata_buf->channel,
    262                                 src_metadata_buf->sub_channel);
    263         if (status != 0) {
    264             return status;
    265         }
    266     }
    267 
    268     dst_metadata_buf = (radio_metadata_buffer_t *)*dst_metadata;
    269     dst_metadata_buf->channel = src_metadata_buf->channel;
    270     dst_metadata_buf->sub_channel = src_metadata_buf->sub_channel;
    271 
    272     for (index = 0; index < src_metadata_buf->count; index++) {
    273         radio_metadata_key_t key;
    274         radio_metadata_type_t type;
    275         void *value;
    276         unsigned int size;
    277         status = radio_metadata_get_at_index(src_metadata, index, &key, &type, &value, &size);
    278         if (status != 0)
    279             continue;
    280         status = add_metadata((radio_metadata_buffer_t **)dst_metadata, key, type, value, size);
    281         if (status != 0)
    282             break;
    283     }
    284     return status;
    285 }
    286 
    287 int radio_metadata_check(const radio_metadata_t *metadata)
    288 {
    289     radio_metadata_buffer_t *metadata_buf =
    290             (radio_metadata_buffer_t *)metadata;
    291     unsigned int count;
    292     unsigned int min_entry_size_int;
    293 
    294     if (metadata_buf == NULL) {
    295         return -EINVAL;
    296     }
    297 
    298     if (metadata_buf->size_int > RADIO_METADATA_MAX_SIZE) {
    299         return -EINVAL;
    300     }
    301 
    302     /* sanity check on entry count versus buffer size */
    303     min_entry_size_int = 1 + sizeof(radio_metadata_entry_t);
    304     min_entry_size_int = (min_entry_size_int + sizeof(unsigned int) - 1) /
    305                                 sizeof(unsigned int);
    306     if ((metadata_buf->count * min_entry_size_int + metadata_buf->count + 1 +
    307             (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) / sizeof(unsigned int)) >
    308                     metadata_buf->size_int) {
    309         return -EINVAL;
    310     }
    311 
    312     /* sanity check on each entry */
    313     for (count = 0; count < metadata_buf->count; count++) {
    314         radio_metadata_entry_t *entry = get_entry_at_index(metadata_buf, count, true);
    315         radio_metadata_entry_t *next_entry;
    316         if (entry == NULL) {
    317             return -EINVAL;
    318         }
    319         if (!is_valid_metadata_key(entry->key)) {
    320             return -EINVAL;
    321         }
    322         if (entry->type != radio_metadata_type_of_key(entry->key)) {
    323             return -EINVAL;
    324         }
    325 
    326         /* do not request check because next entry can be the free slot */
    327         next_entry = get_entry_at_index(metadata_buf, count + 1, false);
    328         if ((char *)entry->data + entry->size > (char *)next_entry) {
    329             return -EINVAL;
    330         }
    331     }
    332 
    333     return 0;
    334 }
    335 
    336 size_t radio_metadata_get_size(const radio_metadata_t *metadata)
    337 {
    338     radio_metadata_buffer_t *metadata_buf =
    339             (radio_metadata_buffer_t *)metadata;
    340 
    341     if (metadata_buf == NULL) {
    342         return 0;
    343     }
    344     return (size_t)(metadata_buf->size_int * sizeof(unsigned int));
    345 }
    346 
    347 int radio_metadata_get_count(const radio_metadata_t *metadata)
    348 {
    349     radio_metadata_buffer_t *metadata_buf =
    350             (radio_metadata_buffer_t *)metadata;
    351 
    352     if (metadata_buf == NULL) {
    353         return -EINVAL;
    354     }
    355     return (int)metadata_buf->count;
    356 }
    357 
    358 int radio_metadata_get_at_index(const radio_metadata_t *metadata,
    359                                 const unsigned int index,
    360                                 radio_metadata_key_t *key,
    361                                 radio_metadata_type_t *type,
    362                                 void **value,
    363                                 unsigned int *size)
    364 {
    365     radio_metadata_entry_t *entry;
    366     radio_metadata_buffer_t *metadata_buf =
    367             (radio_metadata_buffer_t *)metadata;
    368 
    369     if (metadata_buf == NULL || key == NULL || type == NULL ||
    370             value == NULL || size == NULL) {
    371         return -EINVAL;
    372     }
    373     if (index >= metadata_buf->count) {
    374         return -EINVAL;
    375     }
    376 
    377     entry = get_entry_at_index(metadata_buf, index, false);
    378     *key = entry->key;
    379     *type = entry->type;
    380     *value = (void *)entry->data;
    381     *size = entry->size;
    382 
    383     return 0;
    384 }
    385 
    386 int radio_metadata_get_from_key(const radio_metadata_t *metadata,
    387                                 const radio_metadata_key_t key,
    388                                 radio_metadata_type_t *type,
    389                                 void **value,
    390                                 unsigned int *size)
    391 {
    392     unsigned int count;
    393     radio_metadata_entry_t *entry = NULL;
    394     radio_metadata_buffer_t *metadata_buf =
    395             (radio_metadata_buffer_t *)metadata;
    396 
    397     if (metadata_buf == NULL || type == NULL || value == NULL || size == NULL) {
    398         return -EINVAL;
    399     }
    400     if (!is_valid_metadata_key(key)) {
    401         return -EINVAL;
    402     }
    403 
    404     for (count = 0; count < metadata_buf->count; entry = NULL, count++) {
    405         entry = get_entry_at_index(metadata_buf, count, false);
    406         if (entry->key == key) {
    407             break;
    408         }
    409     }
    410     if (entry == NULL) {
    411         return -ENOENT;
    412     }
    413     *type = entry->type;
    414     *value = (void *)entry->data;
    415     *size = entry->size;
    416     return 0;
    417 }
    418