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_TEXT,
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 /* 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 */
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)101 int 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
get_entry_at_index(const radio_metadata_buffer_t * metadata,const unsigned index,bool check)138 radio_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
radio_metadata_type_of_key(const radio_metadata_key_t key)179 radio_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
radio_metadata_allocate(radio_metadata_t ** metadata,const uint32_t channel,const uint32_t sub_channel)187 int 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
radio_metadata_deallocate(radio_metadata_t * metadata)207 void radio_metadata_deallocate(radio_metadata_t *metadata)
208 {
209 free(metadata);
210 }
211
radio_metadata_add_int(radio_metadata_t ** metadata,const radio_metadata_key_t key,const int32_t value)212 int 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
radio_metadata_add_text(radio_metadata_t ** metadata,const radio_metadata_key_t key,const char * value)224 int 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
radio_metadata_add_raw(radio_metadata_t ** metadata,const radio_metadata_key_t key,const unsigned char * value,const size_t size)236 int 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
radio_metadata_add_clock(radio_metadata_t ** metadata,const radio_metadata_key_t key,const radio_metadata_clock_t * clock)248 int 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
radio_metadata_add_metadata(radio_metadata_t ** dst_metadata,radio_metadata_t * src_metadata)261 int 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
radio_metadata_check(const radio_metadata_t * metadata)299 int 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
radio_metadata_get_size(const radio_metadata_t * metadata)348 size_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
radio_metadata_get_count(const radio_metadata_t * metadata)359 int 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
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)370 int 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
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)398 int 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
radio_metadata_get_channel(radio_metadata_t * metadata,uint32_t * channel,uint32_t * sub_channel)431 int 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