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    RADIO_METADATA_TYPE_CLOCK,
43};
44
45/**
46 * private functions
47 */
48
49bool 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
57int 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 */
97int 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
133radio_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
167radio_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
175int 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
195void radio_metadata_deallocate(radio_metadata_t *metadata)
196{
197    free(metadata);
198}
199
200int 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
212int 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
224int 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
236int 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
249int 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
287int 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
336size_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
347int 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
358int 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
386int 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