*/
#define _GNU_SOURCE // for fdprintf
#include <system/camera_metadata.h>
+
+#define LOG_TAG "camera_metadata"
#include <cutils/log.h>
#include <stdio.h>
#include <stdlib.h>
#define DATA_ALIGNMENT _Alignas(camera_metadata_data_t)
#define ALIGN_TO(val, alignment) \
- (((uint32_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+ (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+
+typedef size_t uptrdiff_t;
/**
* A single metadata entry, storing an array of values of a given type. If the
uint32_t flags;
size_t entry_count;
size_t entry_capacity;
- ptrdiff_t entries_start; // Offset from camera_metadata
+ uptrdiff_t entries_start; // Offset from camera_metadata
size_t data_count;
size_t data_capacity;
- ptrdiff_t data_start; // Offset from camera_metadata
+ uptrdiff_t data_start; // Offset from camera_metadata
void *user; // User set pointer, not copied with buffer
uint8_t reserved[0];
};
return (uint8_t*)metadata + metadata->data_start;
}
+camera_metadata_t *allocate_copy_camera_metadata_checked(
+ const camera_metadata_t *src,
+ size_t src_size) {
+
+ if (src == NULL) {
+ return NULL;
+ }
+
+ void *buffer = malloc(src_size);
+ memcpy(buffer, src, src_size);
+
+ camera_metadata_t *metadata = (camera_metadata_t*) buffer;
+ if (validate_camera_metadata_structure(metadata, &src_size) != OK) {
+ free(buffer);
+ return NULL;
+ }
+
+ return metadata;
+}
+
camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
size_t data_capacity) {
if (entry_capacity == 0) return NULL;
metadata->data_count = 0;
metadata->data_capacity = data_capacity;
metadata->size = memory_needed;
- if (metadata->data_capacity != 0) {
- size_t data_unaligned = (uint8_t*)(get_entries(metadata) +
- metadata->entry_capacity) - (uint8_t*)metadata;
- metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);
- } else {
- metadata->data_start = 0;
- }
+ size_t data_unaligned = (uint8_t*)(get_entries(metadata) +
+ metadata->entry_capacity) - (uint8_t*)metadata;
+ metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);
metadata->user = NULL;
return metadata;
return metadata;
}
+int validate_camera_metadata_structure(const camera_metadata_t *metadata,
+ const size_t *expected_size) {
+
+ if (metadata == NULL) {
+ return ERROR;
+ }
+
+ // Check that the metadata pointer is well-aligned first.
+ {
+ struct {
+ const char *name;
+ size_t alignment;
+ } alignments[] = {
+ {
+ .name = "camera_metadata",
+ .alignment = _Alignas(struct camera_metadata)
+ },
+ {
+ .name = "camera_metadata_buffer_entry",
+ .alignment = _Alignas(struct camera_metadata_buffer_entry)
+ },
+ {
+ .name = "camera_metadata_data",
+ .alignment = _Alignas(union camera_metadata_data)
+ },
+ };
+
+ for (size_t i = 0; i < sizeof(alignments)/sizeof(alignments[0]); ++i) {
+ uintptr_t aligned_ptr = ALIGN_TO(metadata, alignments[i].alignment);
+
+ if ((uintptr_t)metadata != aligned_ptr) {
+ ALOGE("%s: Metadata pointer is not aligned (actual %p, "
+ "expected %p) to type %s",
+ __FUNCTION__, metadata,
+ (void*)aligned_ptr, alignments[i].name);
+ return ERROR;
+ }
+ }
+ }
+
+ /**
+ * Check that the metadata contents are correct
+ */
+
+ if (expected_size != NULL && metadata->size > *expected_size) {
+ ALOGE("%s: Metadata size (%u) should be <= expected size (%u)",
+ __FUNCTION__, metadata->size, *expected_size);
+ return ERROR;
+ }
+
+ if (metadata->entry_count > metadata->entry_capacity) {
+ ALOGE("%s: Entry count (%u) should be <= entry capacity (%u)",
+ __FUNCTION__, metadata->entry_count, metadata->entry_capacity);
+ return ERROR;
+ }
+
+ uptrdiff_t entries_end = metadata->entries_start + metadata->entry_capacity;
+ if (entries_end < metadata->entries_start || // overflow check
+ entries_end > metadata->data_start) {
+
+ ALOGE("%s: Entry start + capacity (%u) should be <= data start (%u)",
+ __FUNCTION__,
+ (metadata->entries_start + metadata->entry_capacity),
+ metadata->data_start);
+ return ERROR;
+ }
+
+ uptrdiff_t data_end = metadata->data_start + metadata->data_capacity;
+ if (data_end < metadata->data_start || // overflow check
+ data_end > metadata->size) {
+
+ ALOGE("%s: Data start + capacity (%u) should be <= total size (%u)",
+ __FUNCTION__,
+ (metadata->data_start + metadata->data_capacity),
+ metadata->size);
+ return ERROR;
+ }
+
+ // Validate each entry
+ size_t entry_count = metadata->entry_count;
+ camera_metadata_buffer_entry_t *entries = get_entries(metadata);
+
+ for (size_t i = 0; i < entry_count; ++i) {
+
+ if ((uintptr_t)&entries[i] != ALIGN_TO(&entries[i], ENTRY_ALIGNMENT)) {
+ ALOGE("%s: Entry index %u had bad alignment (address %p),"
+ " expected alignment %d",
+ __FUNCTION__, i, &entries[i], ENTRY_ALIGNMENT);
+ return ERROR;
+ }
+
+ camera_metadata_buffer_entry_t entry = entries[i];
+
+ if (entry.type >= NUM_TYPES) {
+ ALOGE("%s: Entry index %u had a bad type %d",
+ __FUNCTION__, i, entry.type);
+ return ERROR;
+ }
+
+ // TODO: fix vendor_tag_ops across processes so we don't need to special
+ // case vendor-specific tags
+ uint32_t tag_section = entry.tag >> 16;
+ int tag_type = get_camera_metadata_tag_type(entry.tag);
+ if (tag_type != (int)entry.type && tag_section < VENDOR_SECTION) {
+ ALOGE("%s: Entry index %u had tag type %d, but the type was %d",
+ __FUNCTION__, i, tag_type, entry.type);
+ return ERROR;
+ }
+
+ size_t data_size =
+ calculate_camera_metadata_entry_data_size(entry.type,
+ entry.count);
+
+ if (data_size != 0) {
+ camera_metadata_data_t *data =
+ (camera_metadata_data_t*) (get_data(metadata) +
+ entry.data.offset);
+
+ if ((uintptr_t)data != ALIGN_TO(data, DATA_ALIGNMENT)) {
+ ALOGE("%s: Entry index %u had bad data alignment (address %p),"
+ " expected align %d, (tag name %s, data size %u)",
+ __FUNCTION__, i, data, DATA_ALIGNMENT,
+ get_camera_metadata_tag_name(entry.tag) ?: "unknown",
+ data_size);
+ return ERROR;
+ }
+
+ size_t data_entry_end = entry.data.offset + data_size;
+ if (data_entry_end < entry.data.offset || // overflow check
+ data_entry_end > metadata->data_capacity) {
+
+ ALOGE("%s: Entry index %u data ends (%u) beyond the capacity "
+ "%u", __FUNCTION__, i, data_entry_end,
+ metadata->data_capacity);
+ return ERROR;
+ }
+
+ } else if (entry.count == 0) {
+ if (entry.data.offset != 0) {
+ ALOGE("%s: Entry index %u had 0 items, but offset was non-0 "
+ "(%u)", __FUNCTION__, i, entry.data.offset);
+ return ERROR;
+ }
+ } // else data stored inline, so we look at value which can be anything.
+ }
+
+ return OK;
+}
+
int append_camera_metadata(camera_metadata_t *dst,
const camera_metadata_t *src) {
if (dst == NULL || src == NULL ) return ERROR;
({struct _AlignasStruct { char c; T field; }; \
offsetof(struct _AlignasStruct, field); })
+#define FINISH_USING_CAMERA_METADATA(m) \
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL)); \
+ free_camera_metadata(m); \
TEST(camera_metadata, allocate_normal) {
camera_metadata_t *m = NULL;
EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m));
EXPECT_EQ(data_capacity, get_camera_metadata_data_capacity(m));
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, allocate_nodata) {
EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m));
EXPECT_EQ((size_t)0, get_camera_metadata_data_capacity(m));
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, allocate_nothing) {
EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m));
EXPECT_EQ(data_capacity, get_camera_metadata_data_capacity(m));
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, &buf_size));
+
free(buf);
}
EXPECT_EQ(data_capacity, get_camera_metadata_data_capacity(m));
EXPECT_EQ(buf + buf_size - extra_space, (uint8_t*)m + get_camera_metadata_size(m));
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, &buf_size));
+
free(buf);
}
EXPECT_EQ(calculate_camera_metadata_size(0,0),
get_camera_metadata_compact_size(m) );
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, add_get_normal) {
m = allocate_camera_metadata(entry_capacity, data_capacity);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
int result;
size_t data_used = 0;
size_t entries_used = 0;
get_camera_metadata_tag_type(ANDROID_SENSOR_EXPOSURE_TIME), 1);
entries_used++;
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
// INT32
int32_t sensitivity = 800;
get_camera_metadata_tag_type(ANDROID_SENSOR_SENSITIVITY), 1);
entries_used++;
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
// FLOAT
float focusDistance = 0.5f;
get_camera_metadata_tag_type(ANDROID_LENS_FOCUS_DISTANCE), 1);
entries_used++;
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
// Array of FLOAT
float colorTransform[9] = {
get_camera_metadata_tag_type(ANDROID_COLOR_CORRECTION_TRANSFORM), 9);
entries_used++;
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
// Check added entries
camera_metadata_entry entry;
dump_camera_metadata(m, 0, 2);
}
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
void add_test_metadata(camera_metadata_t *m, int entry_count) {
dump_camera_metadata(m, 0, 2);
}
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, add_too_much_data) {
&exposure_time, 1);
EXPECT_EQ(ERROR, result);
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, copy_metadata) {
}
}
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &buf_size));
free(buf);
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, copy_metadata_extraspace) {
}
}
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &buf_size));
free(buf);
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, copy_metadata_nospace) {
free(buf);
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, append_metadata) {
}
}
- free_camera_metadata(m);
- free_camera_metadata(m2);
+ FINISH_USING_CAMERA_METADATA(m);
+ FINISH_USING_CAMERA_METADATA(m2);
}
TEST(camera_metadata, append_metadata_nospace) {
EXPECT_EQ((size_t)0, get_camera_metadata_entry_count(m2));
EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m2));
- free_camera_metadata(m);
- free_camera_metadata(m2);
+ FINISH_USING_CAMERA_METADATA(m);
+ FINISH_USING_CAMERA_METADATA(m2);
}
TEST(camera_metadata, append_metadata_onespace) {
}
}
- free_camera_metadata(m);
- free_camera_metadata(m2);
+ FINISH_USING_CAMERA_METADATA(m);
+ FINISH_USING_CAMERA_METADATA(m2);
}
TEST(camera_metadata, vendor_tags) {
FAKEVENDOR_SENSOR_SUPERMODE,
&superMode, 1);
EXPECT_EQ(ERROR, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
result = add_camera_metadata_entry(m,
ANDROID_REQUEST_METADATA_MODE,
&superMode, 1);
EXPECT_EQ(OK, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
EXPECT_NULL(get_camera_metadata_section_name(FAKEVENDOR_SENSOR_SUPERMODE));
EXPECT_NULL(get_camera_metadata_tag_name(FAKEVENDOR_SENSOR_SUPERMODE));
FAKEVENDOR_SENSOR_SUPERMODE,
&superMode, 1);
EXPECT_EQ(OK, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
result = add_camera_metadata_entry(m,
ANDROID_REQUEST_METADATA_MODE,
&superMode, 1);
EXPECT_EQ(OK, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
result = add_camera_metadata_entry(m,
FAKEVENDOR_SCALER_END,
&superMode, 1);
EXPECT_EQ(ERROR, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
EXPECT_STREQ("com.fakevendor.sensor",
get_camera_metadata_section_name(FAKEVENDOR_SENSOR_SUPERMODE));
EXPECT_EQ(-1, get_camera_metadata_tag_type(FAKEVENDOR_SCALER_END));
set_camera_metadata_vendor_tag_ops(NULL);
+ // TODO: fix vendor ops. Then the below 3 validations should fail.
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
result = add_camera_metadata_entry(m,
FAKEVENDOR_SENSOR_SUPERMODE,
&superMode, 1);
EXPECT_EQ(ERROR, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
result = add_camera_metadata_entry(m,
ANDROID_REQUEST_METADATA_MODE,
&superMode, 1);
EXPECT_EQ(OK, result);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
EXPECT_NULL(get_camera_metadata_section_name(FAKEVENDOR_SENSOR_SUPERMODE));
EXPECT_NULL(get_camera_metadata_tag_name(FAKEVENDOR_SENSOR_SUPERMODE));
EXPECT_EQ(-1, get_camera_metadata_tag_type(FAKEVENDOR_SENSOR_SUPERMODE));
- free_camera_metadata(m);
+ // Remove all vendor entries so validation passes
+ {
+ camera_metadata_ro_entry_t entry;
+ EXPECT_EQ(OK, find_camera_metadata_ro_entry(m,
+ FAKEVENDOR_SENSOR_SUPERMODE,
+ &entry));
+ EXPECT_EQ(OK, delete_camera_metadata_entry(m, entry.index));
+ }
+
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, add_all_tags) {
dump_camera_metadata(m, 0, 2);
}
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, sort_metadata) {
EXPECT_EQ(focus_distance, *entry.data.f);
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, delete_metadata) {
result = get_camera_metadata_user_pointer(m2, &ptr);
EXPECT_NULL(ptr);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &buf_size));
free(buf);
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, memcpy) {
add_test_metadata(m, 5);
- uint8_t *dst = new uint8_t[get_camera_metadata_size(m)];
+ size_t m_size = get_camera_metadata_size(m);
+ uint8_t *dst = new uint8_t[m_size];
- memcpy(dst, m, get_camera_metadata_size(m));
+ memcpy(dst, m, m_size);
camera_metadata_t *m2 = reinterpret_cast<camera_metadata_t*>(dst);
EXPECT_EQ(300, e2.data.i64[0]);
EXPECT_EQ(200, e2.data.i64[1]);
+ EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &m_size));
+
delete dst;
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
TEST(camera_metadata, data_alignment) {
" data_count " << data_count <<
" expected alignment was: " << m_type_align[m_type];
- free_camera_metadata(m);
+ FINISH_USING_CAMERA_METADATA(m);
}
}
}