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