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