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