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 };
43
44 /**
45 * private functions
46 */
47
is_valid_metadata_key(const radio_metadata_key_t key)48 bool is_valid_metadata_key(const radio_metadata_key_t key)
49 {
50 if (key < RADIO_METADATA_KEY_MIN || key > RADIO_METADATA_KEY_MAX) {
51 return false;
52 }
53 return true;
54 }
55
check_size(radio_metadata_buffer_t ** metadata_ptr,const unsigned int size_int)56 int check_size(radio_metadata_buffer_t **metadata_ptr, const unsigned int size_int)
57 {
58 radio_metadata_buffer_t *metadata = *metadata_ptr;
59 unsigned int index_offset = metadata->size_int - metadata->count - 1;
60 unsigned int data_offset = *((unsigned int *)metadata + index_offset);
61 unsigned int req_size_int;
62 unsigned int new_size_int;
63
64 if (size_int == 0) {
65 return 0;
66 }
67
68 req_size_int = data_offset + metadata->count + 1 + 1 + size_int;
69 /* do not grow buffer if it can accommodate the new entry plus an additional index entry */
70
71 if (req_size_int <= metadata->size_int) {
72 return 0;
73 }
74
75 if (req_size_int > RADIO_METADATA_MAX_SIZE || metadata->size_int >= RADIO_METADATA_MAX_SIZE) {
76 return -ENOMEM;
77 }
78 /* grow meta data buffer by a factor of 2 until new data fits */
79 new_size_int = metadata->size_int;
80 while (new_size_int < req_size_int)
81 new_size_int *= 2;
82
83 ALOGV("%s growing from %u to %u", __func__, metadata->size_int, new_size_int);
84 metadata = realloc(metadata, new_size_int * sizeof(unsigned int));
85 /* move index table */
86 memmove((unsigned int *)metadata + new_size_int - (metadata->count + 1),
87 (unsigned int *)metadata + metadata->size_int - (metadata->count + 1),
88 (metadata->count + 1) * sizeof(unsigned int));
89 metadata->size_int = new_size_int;
90
91 *metadata_ptr = metadata;
92 return 0;
93 }
94
95 /* 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)96 int add_metadata(radio_metadata_buffer_t **metadata_ptr,
97 const radio_metadata_key_t key,
98 const radio_metadata_type_t type,
99 const void *value,
100 const unsigned int size)
101 {
102 unsigned int entry_size_int;
103 int ret;
104 radio_metadata_entry_t *entry;
105 unsigned int index_offset;
106 unsigned int data_offset;
107 radio_metadata_buffer_t *metadata = *metadata_ptr;
108
109 entry_size_int = size + sizeof(radio_metadata_entry_t);
110 entry_size_int = (entry_size_int + sizeof(unsigned int) - 1) / sizeof(unsigned int);
111
112 ret = check_size(metadata_ptr, entry_size_int);
113 if (ret < 0) {
114 return ret;
115 }
116 metadata = *metadata_ptr;
117 index_offset = metadata->size_int - metadata->count - 1;
118 data_offset = *((unsigned int *)metadata + index_offset);
119
120 entry = (radio_metadata_entry_t *)((unsigned int *)metadata + data_offset);
121 entry->key = key;
122 entry->type = type;
123 entry->size = size;
124 memcpy(entry->data, value, size);
125
126 data_offset += entry_size_int;
127 *((unsigned int *)metadata + index_offset -1) = data_offset;
128 metadata->count++;
129 return 0;
130 }
131
get_entry_at_index(const radio_metadata_buffer_t * metadata,const unsigned index,bool check)132 radio_metadata_entry_t *get_entry_at_index(
133 const radio_metadata_buffer_t *metadata,
134 const unsigned index,
135 bool check)
136 {
137 unsigned int index_offset = metadata->size_int - index - 1;
138 unsigned int data_offset = *((unsigned int *)metadata + index_offset);
139
140 if (check) {
141 if (index >= metadata->count) {
142 return NULL;
143 }
144 unsigned int min_offset;
145 unsigned int max_offset;
146 unsigned int min_entry_size_int;
147 min_offset = (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) /
148 sizeof(unsigned int);
149 if (data_offset < min_offset) {
150 return NULL;
151 }
152 min_entry_size_int = 1 + sizeof(radio_metadata_entry_t);
153 min_entry_size_int = (min_entry_size_int + sizeof(unsigned int) - 1) / sizeof(unsigned int);
154 max_offset = metadata->size_int - metadata->count - 1 - min_entry_size_int;
155 if (data_offset > max_offset) {
156 return NULL;
157 }
158 }
159 return (radio_metadata_entry_t *)((unsigned int *)metadata + data_offset);
160 }
161
162 /**
163 * metadata API functions
164 */
165
radio_metadata_type_of_key(const radio_metadata_key_t key)166 radio_metadata_type_t radio_metadata_type_of_key(const radio_metadata_key_t key)
167 {
168 if (!is_valid_metadata_key(key)) {
169 return RADIO_METADATA_TYPE_INVALID;
170 }
171 return metadata_key_type_table[key - RADIO_METADATA_KEY_MIN];
172 }
173
radio_metadata_allocate(radio_metadata_t ** metadata,const unsigned int channel,const unsigned int sub_channel)174 int radio_metadata_allocate(radio_metadata_t **metadata,
175 const unsigned int channel,
176 const unsigned int sub_channel)
177 {
178 radio_metadata_buffer_t *metadata_buf =
179 (radio_metadata_buffer_t *)calloc(RADIO_METADATA_DEFAULT_SIZE, sizeof(unsigned int));
180 if (metadata_buf == NULL) {
181 return -ENOMEM;
182 }
183
184 metadata_buf->channel = channel;
185 metadata_buf->sub_channel = sub_channel;
186 metadata_buf->size_int = RADIO_METADATA_DEFAULT_SIZE;
187 *((unsigned int *)metadata_buf + RADIO_METADATA_DEFAULT_SIZE - 1) =
188 (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) /
189 sizeof(unsigned int);
190 *metadata = (radio_metadata_t *)metadata_buf;
191 return 0;
192 }
193
radio_metadata_deallocate(radio_metadata_t * metadata)194 void radio_metadata_deallocate(radio_metadata_t *metadata)
195 {
196 free(metadata);
197 }
198
radio_metadata_add_int(radio_metadata_t ** metadata,const radio_metadata_key_t key,const int value)199 int radio_metadata_add_int(radio_metadata_t **metadata,
200 const radio_metadata_key_t key,
201 const int value)
202 {
203 radio_metadata_type_t type = radio_metadata_type_of_key(key);
204 if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_INT) {
205 return -EINVAL;
206 }
207 return add_metadata((radio_metadata_buffer_t **)metadata,
208 key, type, &value, sizeof(int));
209 }
210
radio_metadata_add_text(radio_metadata_t ** metadata,const radio_metadata_key_t key,const char * value)211 int radio_metadata_add_text(radio_metadata_t **metadata,
212 const radio_metadata_key_t key,
213 const char *value)
214 {
215 radio_metadata_type_t type = radio_metadata_type_of_key(key);
216 if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_TEXT ||
217 value == NULL || strlen(value) >= RADIO_METADATA_TEXT_LEN_MAX) {
218 return -EINVAL;
219 }
220 return add_metadata((radio_metadata_buffer_t **)metadata, key, type, value, strlen(value) + 1);
221 }
222
radio_metadata_add_raw(radio_metadata_t ** metadata,const radio_metadata_key_t key,const unsigned char * value,const unsigned int size)223 int radio_metadata_add_raw(radio_metadata_t **metadata,
224 const radio_metadata_key_t key,
225 const unsigned char *value,
226 const unsigned int size)
227 {
228 radio_metadata_type_t type = radio_metadata_type_of_key(key);
229 if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_RAW || value == NULL) {
230 return -EINVAL;
231 }
232 return add_metadata((radio_metadata_buffer_t **)metadata, key, type, value, size);
233 }
234
radio_metadata_add_metadata(radio_metadata_t ** dst_metadata,radio_metadata_t * src_metadata)235 int radio_metadata_add_metadata(radio_metadata_t **dst_metadata,
236 radio_metadata_t *src_metadata)
237 {
238 radio_metadata_buffer_t *src_metadata_buf = (radio_metadata_buffer_t *)src_metadata;
239 radio_metadata_buffer_t *dst_metadata_buf;
240 int status;
241 unsigned int index;
242
243 if (dst_metadata == NULL || src_metadata == NULL) {
244 return -EINVAL;
245 }
246 if (*dst_metadata == NULL) {
247 status = radio_metadata_allocate(dst_metadata, src_metadata_buf->channel,
248 src_metadata_buf->sub_channel);
249 if (status != 0) {
250 return status;
251 }
252 }
253
254 dst_metadata_buf = (radio_metadata_buffer_t *)*dst_metadata;
255 dst_metadata_buf->channel = src_metadata_buf->channel;
256 dst_metadata_buf->sub_channel = src_metadata_buf->sub_channel;
257
258 for (index = 0; index < src_metadata_buf->count; index++) {
259 radio_metadata_key_t key;
260 radio_metadata_type_t type;
261 void *value;
262 unsigned int size;
263 status = radio_metadata_get_at_index(src_metadata, index, &key, &type, &value, &size);
264 if (status != 0)
265 continue;
266 status = add_metadata((radio_metadata_buffer_t **)dst_metadata, key, type, value, size);
267 if (status != 0)
268 break;
269 }
270 return status;
271 }
272
radio_metadata_check(const radio_metadata_t * metadata)273 int radio_metadata_check(const radio_metadata_t *metadata)
274 {
275 radio_metadata_buffer_t *metadata_buf =
276 (radio_metadata_buffer_t *)metadata;
277 unsigned int count;
278 unsigned int min_entry_size_int;
279
280 if (metadata_buf == NULL) {
281 return -EINVAL;
282 }
283
284 if (metadata_buf->size_int > RADIO_METADATA_MAX_SIZE) {
285 return -EINVAL;
286 }
287
288 /* sanity check on entry count versus buffer size */
289 min_entry_size_int = 1 + sizeof(radio_metadata_entry_t);
290 min_entry_size_int = (min_entry_size_int + sizeof(unsigned int) - 1) /
291 sizeof(unsigned int);
292 if ((metadata_buf->count * min_entry_size_int + metadata_buf->count + 1 +
293 (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) / sizeof(unsigned int)) >
294 metadata_buf->size_int) {
295 return -EINVAL;
296 }
297
298 /* sanity check on each entry */
299 for (count = 0; count < metadata_buf->count; count++) {
300 radio_metadata_entry_t *entry = get_entry_at_index(metadata_buf, count, true);
301 radio_metadata_entry_t *next_entry;
302 if (entry == NULL) {
303 return -EINVAL;
304 }
305 if (!is_valid_metadata_key(entry->key)) {
306 return -EINVAL;
307 }
308 if (entry->type != radio_metadata_type_of_key(entry->key)) {
309 return -EINVAL;
310 }
311
312 /* do not request check because next entry can be the free slot */
313 next_entry = get_entry_at_index(metadata_buf, count + 1, false);
314 if ((char *)entry->data + entry->size > (char *)next_entry) {
315 return -EINVAL;
316 }
317 }
318
319 return 0;
320 }
321
radio_metadata_get_size(const radio_metadata_t * metadata)322 size_t radio_metadata_get_size(const radio_metadata_t *metadata)
323 {
324 radio_metadata_buffer_t *metadata_buf =
325 (radio_metadata_buffer_t *)metadata;
326
327 if (metadata_buf == NULL) {
328 return 0;
329 }
330 return (size_t)(metadata_buf->size_int * sizeof(unsigned int));
331 }
332
radio_metadata_get_count(const radio_metadata_t * metadata)333 int radio_metadata_get_count(const radio_metadata_t *metadata)
334 {
335 radio_metadata_buffer_t *metadata_buf =
336 (radio_metadata_buffer_t *)metadata;
337
338 if (metadata_buf == NULL) {
339 return -EINVAL;
340 }
341 return (int)metadata_buf->count;
342 }
343
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)344 int radio_metadata_get_at_index(const radio_metadata_t *metadata,
345 const unsigned int index,
346 radio_metadata_key_t *key,
347 radio_metadata_type_t *type,
348 void **value,
349 unsigned int *size)
350 {
351 unsigned int index_offset;
352 unsigned int data_offset;
353 radio_metadata_entry_t *entry;
354 radio_metadata_buffer_t *metadata_buf =
355 (radio_metadata_buffer_t *)metadata;
356
357 if (metadata_buf == NULL || key == NULL || type == NULL ||
358 value == NULL || size == NULL) {
359 return -EINVAL;
360 }
361 if (index >= metadata_buf->count) {
362 return -EINVAL;
363 }
364
365 entry = get_entry_at_index(metadata_buf, index, false);
366 *key = entry->key;
367 *type = entry->type;
368 *value = (void *)entry->data;
369 *size = entry->size;
370
371 return 0;
372 }
373
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)374 int radio_metadata_get_from_key(const radio_metadata_t *metadata,
375 const radio_metadata_key_t key,
376 radio_metadata_type_t *type,
377 void **value,
378 unsigned int *size)
379 {
380 unsigned int count;
381 radio_metadata_entry_t *entry = NULL;
382 radio_metadata_buffer_t *metadata_buf =
383 (radio_metadata_buffer_t *)metadata;
384
385 if (metadata_buf == NULL || type == NULL || value == NULL || size == NULL) {
386 return -EINVAL;
387 }
388 if (!is_valid_metadata_key(key)) {
389 return -EINVAL;
390 }
391
392 for (count = 0; count < metadata_buf->count; entry = NULL, count++) {
393 entry = get_entry_at_index(metadata_buf, count, false);
394 if (entry->key == key) {
395 break;
396 }
397 }
398 if (entry == NULL) {
399 return -ENOENT;
400 }
401 *type = entry->type;
402 *value = (void *)entry->data;
403 *size = entry->size;
404 return 0;
405 }
406