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