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