summaryrefslogtreecommitdiffstats
path: root/radio
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2015-03-05 13:01:38 -0800
committerEric Laurent <elaurent@google.com>2015-03-12 16:32:15 -0700
commit6caceaed8856e7e3cf05fa3232bc4fd8a22d2295 (patch)
tree9f42bbb6f53fb5c7c7e3c5e9b1a3e1f79ef6f88a /radio
parentb589c0f8618367dd421ea3dfb84e00ae4d5ab311 (diff)
downloadandroid_system_media-6caceaed8856e7e3cf05fa3232bc4fd8a22d2295.tar.gz
android_system_media-6caceaed8856e7e3cf05fa3232bc4fd8a22d2295.tar.bz2
android_system_media-6caceaed8856e7e3cf05fa3232bc4fd8a22d2295.zip
radio meta data utility library
Added utility library to manage radio meta data. Radio HALs can link against this library and use it to create meta data entries and pass them to the framework. Change-Id: I7acd585246042024e641ac048815861373ab6d94
Diffstat (limited to 'radio')
-rw-r--r--radio/src/Android.mk24
-rw-r--r--radio/src/radio_metadata.c405
2 files changed, 429 insertions, 0 deletions
diff --git a/radio/src/Android.mk b/radio/src/Android.mk
new file mode 100644
index 00000000..b96a40a3
--- /dev/null
+++ b/radio/src/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ radio_metadata.c
+
+LOCAL_C_INCLUDES:= \
+ system/media/radio/include \
+ system/media/private/radio/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog
+
+LOCAL_MODULE := libradio_metadata
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS += \
+ -fvisibility=hidden
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/radio/src/radio_metadata.c b/radio/src/radio_metadata.c
new file mode 100644
index 00000000..41c67d89
--- /dev/null
+++ b/radio/src/radio_metadata.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "radio_metadata"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+#include <radio_metadata_hidden.h>
+#include <cutils/log.h>
+
+const radio_metadata_type_t metadata_key_type_table[] =
+{
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_INT,
+ RADIO_METADATA_TYPE_INT,
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_TEXT,
+ RADIO_METADATA_TYPE_RAW,
+ RADIO_METADATA_TYPE_RAW,
+};
+
+/**
+ * private functions
+ */
+
+bool is_valid_metadata_key(const radio_metadata_key_t key)
+{
+ if (key < RADIO_METADATA_KEY_MIN || key > RADIO_METADATA_KEY_MAX) {
+ return false;
+ }
+ return true;
+}
+
+int check_size(radio_metadata_buffer_t **metadata_ptr, const unsigned int size_int)
+{
+ radio_metadata_buffer_t *metadata = *metadata_ptr;
+ unsigned int index_offset = metadata->size_int - metadata->count - 1;
+ unsigned int data_offset = *((unsigned int *)metadata + index_offset);
+ unsigned int req_size_int;
+ unsigned int new_size_int;
+
+ if (size_int == 0) {
+ return 0;
+ }
+
+ req_size_int = data_offset + metadata->count + 1 + 1 + size_int;
+ /* do not grow buffer if it can accommodate the new entry plus an additional index entry */
+
+ if (req_size_int <= metadata->size_int) {
+ return 0;
+ }
+
+ if (req_size_int > RADIO_METADATA_MAX_SIZE || metadata->size_int >= RADIO_METADATA_MAX_SIZE) {
+ return -ENOMEM;
+ }
+ /* grow meta data buffer by a factor of 2 until new data fits */
+ new_size_int = metadata->size_int;
+ while (new_size_int < req_size_int)
+ new_size_int *= 2;
+
+ ALOGV("%s growing from %u to %u", __func__, metadata->size_int, new_size_int);
+ metadata = realloc(metadata, new_size_int * sizeof(unsigned int));
+ /* move index table */
+ memmove((unsigned int *)metadata + new_size_int - (metadata->count + 1),
+ (unsigned int *)metadata + metadata->size_int - (metadata->count + 1),
+ (metadata->count + 1) * sizeof(unsigned int));
+ metadata->size_int = new_size_int;
+
+ *metadata_ptr = metadata;
+ return 0;
+}
+
+/* checks on size and key validity are done before calling this function */
+int 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)
+{
+ unsigned int entry_size_int;
+ int ret;
+ radio_metadata_entry_t *entry;
+ unsigned int index_offset;
+ unsigned int data_offset;
+ radio_metadata_buffer_t *metadata = *metadata_ptr;
+
+ entry_size_int = size + sizeof(radio_metadata_entry_t);
+ entry_size_int = (entry_size_int + sizeof(unsigned int) - 1) / sizeof(unsigned int);
+
+ ret = check_size(metadata_ptr, entry_size_int);
+ if (ret < 0) {
+ return ret;
+ }
+ metadata = *metadata_ptr;
+ index_offset = metadata->size_int - metadata->count - 1;
+ data_offset = *((unsigned int *)metadata + index_offset);
+
+ entry = (radio_metadata_entry_t *)((unsigned int *)metadata + data_offset);
+ entry->key = key;
+ entry->type = type;
+ entry->size = size;
+ memcpy(entry->data, value, size);
+
+ data_offset += entry_size_int;
+ *((unsigned int *)metadata + index_offset -1) = data_offset;
+ metadata->count++;
+ return 0;
+}
+
+radio_metadata_entry_t *get_entry_at_index(
+ const radio_metadata_buffer_t *metadata,
+ const unsigned index,
+ bool check)
+{
+ unsigned int index_offset = metadata->size_int - index - 1;
+ unsigned int data_offset = *((unsigned int *)metadata + index_offset);
+
+ if (check) {
+ if (index >= metadata->count) {
+ return NULL;
+ }
+ unsigned int min_offset;
+ unsigned int max_offset;
+ unsigned int min_entry_size_int;
+ min_offset = (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) /
+ sizeof(unsigned int);
+ if (data_offset < min_offset) {
+ return NULL;
+ }
+ min_entry_size_int = 1 + sizeof(radio_metadata_entry_t);
+ min_entry_size_int = (min_entry_size_int + sizeof(unsigned int) - 1) / sizeof(unsigned int);
+ max_offset = metadata->size_int - metadata->count - 1 - min_entry_size_int;
+ if (data_offset > max_offset) {
+ return NULL;
+ }
+ }
+ return (radio_metadata_entry_t *)((unsigned int *)metadata + data_offset);
+}
+
+/**
+ * metadata API functions
+ */
+
+radio_metadata_type_t radio_metadata_type_of_key(const radio_metadata_key_t key)
+{
+ if (!is_valid_metadata_key(key)) {
+ return RADIO_METADATA_TYPE_INVALID;
+ }
+ return metadata_key_type_table[key - RADIO_METADATA_KEY_MIN];
+}
+
+int radio_metadata_allocate(radio_metadata_t **metadata,
+ const unsigned int channel,
+ const unsigned int sub_channel)
+{
+ radio_metadata_buffer_t *metadata_buf =
+ (radio_metadata_buffer_t *)calloc(RADIO_METADATA_DEFAULT_SIZE, sizeof(unsigned int));
+ if (metadata_buf == NULL) {
+ return -ENOMEM;
+ }
+
+ metadata_buf->channel = channel;
+ metadata_buf->sub_channel = sub_channel;
+ metadata_buf->size_int = RADIO_METADATA_DEFAULT_SIZE;
+ *((unsigned int *)metadata_buf + RADIO_METADATA_DEFAULT_SIZE - 1) =
+ (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) /
+ sizeof(unsigned int);
+ *metadata = (radio_metadata_t *)metadata_buf;
+ return 0;
+}
+
+void radio_metadata_deallocate(radio_metadata_t *metadata)
+{
+ free(metadata);
+}
+
+int radio_metadata_add_int(radio_metadata_t **metadata,
+ const radio_metadata_key_t key,
+ const int value)
+{
+ radio_metadata_type_t type = radio_metadata_type_of_key(key);
+ if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_INT) {
+ return -EINVAL;
+ }
+ return add_metadata((radio_metadata_buffer_t **)metadata,
+ key, type, &value, sizeof(int));
+}
+
+int radio_metadata_add_text(radio_metadata_t **metadata,
+ const radio_metadata_key_t key,
+ const char *value)
+{
+ radio_metadata_type_t type = radio_metadata_type_of_key(key);
+ if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_TEXT ||
+ value == NULL || strlen(value) >= RADIO_METADATA_TEXT_LEN_MAX) {
+ return -EINVAL;
+ }
+ return add_metadata((radio_metadata_buffer_t **)metadata, key, type, value, strlen(value) + 1);
+}
+
+int radio_metadata_add_raw(radio_metadata_t **metadata,
+ const radio_metadata_key_t key,
+ const unsigned char *value,
+ const unsigned int size)
+{
+ radio_metadata_type_t type = radio_metadata_type_of_key(key);
+ if (metadata == NULL || *metadata == NULL || type != RADIO_METADATA_TYPE_RAW || value == NULL) {
+ return -EINVAL;
+ }
+ return add_metadata((radio_metadata_buffer_t **)metadata, key, type, value, size);
+}
+
+int radio_metadata_add_metadata(radio_metadata_t **dst_metadata,
+ radio_metadata_t *src_metadata)
+{
+ radio_metadata_buffer_t *src_metadata_buf = (radio_metadata_buffer_t *)src_metadata;
+ radio_metadata_buffer_t *dst_metadata_buf;
+ int status;
+ unsigned int index;
+
+ if (dst_metadata == NULL || src_metadata == NULL) {
+ return -EINVAL;
+ }
+ if (*dst_metadata == NULL) {
+ status = radio_metadata_allocate(dst_metadata, src_metadata_buf->channel,
+ src_metadata_buf->sub_channel);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ dst_metadata_buf = (radio_metadata_buffer_t *)*dst_metadata;
+ dst_metadata_buf->channel = src_metadata_buf->channel;
+ dst_metadata_buf->sub_channel = src_metadata_buf->sub_channel;
+
+ for (index = 0; index < src_metadata_buf->count; index++) {
+ radio_metadata_key_t key;
+ radio_metadata_type_t type;
+ void *value;
+ unsigned int size;
+ status = radio_metadata_get_at_index(src_metadata, index, &key, &type, &value, &size);
+ if (status != 0)
+ continue;
+ status = add_metadata((radio_metadata_buffer_t **)dst_metadata, key, type, value, size);
+ if (status != 0)
+ break;
+ }
+ return status;
+}
+
+int radio_metadata_check(const radio_metadata_t *metadata)
+{
+ radio_metadata_buffer_t *metadata_buf =
+ (radio_metadata_buffer_t *)metadata;
+ unsigned int count;
+ unsigned int min_entry_size_int;
+
+ if (metadata_buf == NULL) {
+ return -EINVAL;
+ }
+
+ if (metadata_buf->size_int > RADIO_METADATA_MAX_SIZE) {
+ return -EINVAL;
+ }
+
+ /* sanity check on entry count versus buffer size */
+ min_entry_size_int = 1 + sizeof(radio_metadata_entry_t);
+ min_entry_size_int = (min_entry_size_int + sizeof(unsigned int) - 1) /
+ sizeof(unsigned int);
+ if ((metadata_buf->count * min_entry_size_int + metadata_buf->count + 1 +
+ (sizeof(radio_metadata_buffer_t) + sizeof(unsigned int) - 1) / sizeof(unsigned int)) >
+ metadata_buf->size_int) {
+ return -EINVAL;
+ }
+
+ /* sanity check on each entry */
+ for (count = 0; count < metadata_buf->count; count++) {
+ radio_metadata_entry_t *entry = get_entry_at_index(metadata_buf, count, true);
+ radio_metadata_entry_t *next_entry;
+ if (entry == NULL) {
+ return -EINVAL;
+ }
+ if (!is_valid_metadata_key(entry->key)) {
+ return -EINVAL;
+ }
+ if (entry->type != radio_metadata_type_of_key(entry->key)) {
+ return -EINVAL;
+ }
+
+ /* do not request check because next entry can be the free slot */
+ next_entry = get_entry_at_index(metadata_buf, count + 1, false);
+ if ((char *)entry->data + entry->size > (char *)next_entry) {
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+size_t radio_metadata_get_size(const radio_metadata_t *metadata)
+{
+ radio_metadata_buffer_t *metadata_buf =
+ (radio_metadata_buffer_t *)metadata;
+
+ if (metadata_buf == NULL) {
+ return 0;
+ }
+ return (size_t)(metadata_buf->size_int * sizeof(unsigned int));
+}
+
+int radio_metadata_get_count(const radio_metadata_t *metadata)
+{
+ radio_metadata_buffer_t *metadata_buf =
+ (radio_metadata_buffer_t *)metadata;
+
+ if (metadata_buf == NULL) {
+ return -EINVAL;
+ }
+ return (int)metadata_buf->count;
+}
+
+int 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)
+{
+ unsigned int index_offset;
+ unsigned int data_offset;
+ radio_metadata_entry_t *entry;
+ radio_metadata_buffer_t *metadata_buf =
+ (radio_metadata_buffer_t *)metadata;
+
+ if (metadata_buf == NULL || key == NULL || type == NULL ||
+ value == NULL || size == NULL) {
+ return -EINVAL;
+ }
+ if (index >= metadata_buf->count) {
+ return -EINVAL;
+ }
+
+ entry = get_entry_at_index(metadata_buf, index, false);
+ *key = entry->key;
+ *type = entry->type;
+ *value = (void *)entry->data;
+ *size = entry->size;
+
+ return 0;
+}
+
+int 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)
+{
+ unsigned int count;
+ radio_metadata_entry_t *entry = NULL;
+ radio_metadata_buffer_t *metadata_buf =
+ (radio_metadata_buffer_t *)metadata;
+
+ if (metadata_buf == NULL || type == NULL || value == NULL || size == NULL) {
+ return -EINVAL;
+ }
+ if (!is_valid_metadata_key(key)) {
+ return -EINVAL;
+ }
+
+ for (count = 0; count < metadata_buf->count; entry = NULL, count++) {
+ entry = get_entry_at_index(metadata_buf, count, false);
+ if (entry->key == key) {
+ break;
+ }
+ }
+ if (entry == NULL) {
+ return -ENOENT;
+ }
+ *type = entry->type;
+ *value = (void *)entry->data;
+ *size = entry->size;
+ return 0;
+}