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