• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
is_valid_metadata_key(const radio_metadata_key_t key)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 
check_size(radio_metadata_buffer_t ** metadata_ptr,const unsigned int size_int)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 */
add_metadata(radio_metadata_buffer_t ** metadata_ptr,const radio_metadata_key_t key,const radio_metadata_type_t type,const void * value,const unsigned int size)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 
get_entry_at_index(const radio_metadata_buffer_t * metadata,const unsigned index,bool check)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 
radio_metadata_type_of_key(const radio_metadata_key_t key)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 
radio_metadata_allocate(radio_metadata_t ** metadata,const unsigned int channel,const unsigned int sub_channel)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 
radio_metadata_deallocate(radio_metadata_t * metadata)195 void radio_metadata_deallocate(radio_metadata_t *metadata)
196 {
197     free(metadata);
198 }
199 
radio_metadata_add_int(radio_metadata_t ** metadata,const radio_metadata_key_t key,const int value)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 
radio_metadata_add_text(radio_metadata_t ** metadata,const radio_metadata_key_t key,const char * value)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 
radio_metadata_add_raw(radio_metadata_t ** metadata,const radio_metadata_key_t key,const unsigned char * value,const unsigned int size)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 
radio_metadata_add_clock(radio_metadata_t ** metadata,const radio_metadata_key_t key,const radio_metadata_clock_t * clock)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 
radio_metadata_add_metadata(radio_metadata_t ** dst_metadata,radio_metadata_t * src_metadata)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 
radio_metadata_check(const radio_metadata_t * metadata)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 
radio_metadata_get_size(const radio_metadata_t * metadata)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 
radio_metadata_get_count(const radio_metadata_t * metadata)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 
radio_metadata_get_at_index(const radio_metadata_t * metadata,const unsigned int index,radio_metadata_key_t * key,radio_metadata_type_t * type,void ** value,unsigned int * size)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 
radio_metadata_get_from_key(const radio_metadata_t * metadata,const radio_metadata_key_t key,radio_metadata_type_t * type,void ** value,unsigned int * size)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