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