diff options
Diffstat (limited to 'camera')
-rw-r--r-- | camera/Android.mk | 16 | ||||
-rw-r--r-- | camera/CameraMetadata.cpp | 137 | ||||
-rw-r--r-- | camera/CameraParameters.cpp | 53 | ||||
-rw-r--r-- | camera/ICameraService.cpp | 42 | ||||
-rw-r--r-- | camera/ProCamera.cpp | 9 | ||||
-rw-r--r-- | camera/VendorTagDescriptor.cpp | 319 | ||||
-rw-r--r-- | camera/tests/Android.mk | 17 | ||||
-rw-r--r-- | camera/tests/VendorTagDescriptorTests.cpp | 204 |
8 files changed, 756 insertions, 41 deletions
diff --git a/camera/Android.mk b/camera/Android.mk index e633450..369d0c5 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -1,3 +1,17 @@ +# Copyright 2010 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. + CAMERA_CLIENT_LOCAL_PATH:= $(call my-dir) include $(call all-subdir-makefiles) include $(CLEAR_VARS) @@ -21,6 +35,7 @@ LOCAL_SRC_FILES:= \ camera2/CaptureRequest.cpp \ ProCamera.cpp \ CameraBase.cpp \ + VendorTagDescriptor.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -34,6 +49,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES += \ system/media/camera/include \ + system/media/private/camera/include LOCAL_MODULE:= libcamera_client diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp index 7765914..6b726e0 100644 --- a/camera/CameraMetadata.cpp +++ b/camera/CameraMetadata.cpp @@ -25,6 +25,9 @@ namespace android { +#define ALIGN_TO(val, alignment) \ + (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1)) + typedef Parcel::WritableBlob WritableBlob; typedef Parcel::ReadableBlob ReadableBlob; @@ -431,40 +434,70 @@ status_t CameraMetadata::readFromParcel(const Parcel& data, *out = NULL; } - // arg0 = metadataSize (int32) - int32_t metadataSizeTmp = -1; - if ((err = data.readInt32(&metadataSizeTmp)) != OK) { + // See CameraMetadata::writeToParcel for parcel data layout diagram and explanation. + // arg0 = blobSize (int32) + int32_t blobSizeTmp = -1; + if ((err = data.readInt32(&blobSizeTmp)) != OK) { ALOGE("%s: Failed to read metadata size (error %d %s)", __FUNCTION__, err, strerror(-err)); return err; } - const size_t metadataSize = static_cast<size_t>(metadataSizeTmp); + const size_t blobSize = static_cast<size_t>(blobSizeTmp); + const size_t alignment = get_camera_metadata_alignment(); - if (metadataSize == 0) { + // Special case: zero blob size means zero sized (NULL) metadata. + if (blobSize == 0) { ALOGV("%s: Read 0-sized metadata", __FUNCTION__); return OK; } - // NOTE: this doesn't make sense to me. shouldnt the blob + if (blobSize <= alignment) { + ALOGE("%s: metadata blob is malformed, blobSize(%zu) should be larger than alignment(%zu)", + __FUNCTION__, blobSize, alignment); + return BAD_VALUE; + } + + const size_t metadataSize = blobSize - alignment; + + // NOTE: this doesn't make sense to me. shouldn't the blob // know how big it is? why do we have to specify the size // to Parcel::readBlob ? - ReadableBlob blob; // arg1 = metadata (blob) do { - if ((err = data.readBlob(metadataSize, &blob)) != OK) { - ALOGE("%s: Failed to read metadata blob (sized %d). Possible " + if ((err = data.readBlob(blobSize, &blob)) != OK) { + ALOGE("%s: Failed to read metadata blob (sized %zu). Possible " " serialization bug. Error %d %s", - __FUNCTION__, metadataSize, err, strerror(-err)); + __FUNCTION__, blobSize, err, strerror(-err)); break; } - const camera_metadata_t* tmp = - reinterpret_cast<const camera_metadata_t*>(blob.data()); + // arg2 = offset (blob) + // Must be after blob since we don't know offset until after writeBlob. + int32_t offsetTmp; + if ((err = data.readInt32(&offsetTmp)) != OK) { + ALOGE("%s: Failed to read metadata offsetTmp (error %d %s)", + __FUNCTION__, err, strerror(-err)); + break; + } + const size_t offset = static_cast<size_t>(offsetTmp); + if (offset >= alignment) { + ALOGE("%s: metadata offset(%zu) should be less than alignment(%zu)", + __FUNCTION__, blobSize, alignment); + err = BAD_VALUE; + break; + } + + const uintptr_t metadataStart = reinterpret_cast<uintptr_t>(blob.data()) + offset; + const camera_metadata_t* tmp = + reinterpret_cast<const camera_metadata_t*>(metadataStart); + ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu", + __FUNCTION__, alignment, tmp, offset); metadata = allocate_copy_camera_metadata_checked(tmp, metadataSize); if (metadata == NULL) { // We consider that allocation only fails if the validation // also failed, therefore the readFromParcel was a failure. + ALOGE("%s: metadata allocation and copy failed", __FUNCTION__); err = BAD_VALUE; } } while(0); @@ -485,38 +518,79 @@ status_t CameraMetadata::writeToParcel(Parcel& data, const camera_metadata_t* metadata) { status_t res = OK; - // arg0 = metadataSize (int32) - + /** + * Below is the camera metadata parcel layout: + * + * |--------------------------------------------| + * | arg0: blobSize | + * | (length = 4) | + * |--------------------------------------------|<--Skip the rest if blobSize == 0. + * | | + * | | + * | arg1: blob | + * | (length = variable, see arg1 layout below) | + * | | + * | | + * |--------------------------------------------| + * | arg2: offset | + * | (length = 4) | + * |--------------------------------------------| + */ + + // arg0 = blobSize (int32) if (metadata == NULL) { + // Write zero blobSize for null metadata. return data.writeInt32(0); } + /** + * Always make the blob size sufficiently larger, as we need put alignment + * padding and metadata into the blob. Since we don't know the alignment + * offset before writeBlob. Then write the metadata to aligned offset. + */ const size_t metadataSize = get_camera_metadata_compact_size(metadata); - res = data.writeInt32(static_cast<int32_t>(metadataSize)); + const size_t alignment = get_camera_metadata_alignment(); + const size_t blobSize = metadataSize + alignment; + res = data.writeInt32(static_cast<int32_t>(blobSize)); if (res != OK) { return res; } - // arg1 = metadata (blob) + size_t offset = 0; + /** + * arg1 = metadata (blob). + * + * The blob size is the sum of front padding size, metadata size and back padding + * size, which is equal to metadataSize + alignment. + * + * The blob layout is: + * |------------------------------------|<----Start address of the blob (unaligned). + * | front padding | + * | (size = offset) | + * |------------------------------------|<----Aligned start address of metadata. + * | | + * | | + * | metadata | + * | (size = metadataSize) | + * | | + * | | + * |------------------------------------| + * | back padding | + * | (size = alignment - offset) | + * |------------------------------------|<----End address of blob. + * (Blob start address + blob size). + */ WritableBlob blob; do { - res = data.writeBlob(metadataSize, &blob); + res = data.writeBlob(blobSize, &blob); if (res != OK) { break; } - copy_camera_metadata(blob.data(), metadataSize, metadata); - - IF_ALOGV() { - if (validate_camera_metadata_structure( - (const camera_metadata_t*)blob.data(), - &metadataSize) != OK) { - ALOGV("%s: Failed to validate metadata %p after writing blob", - __FUNCTION__, blob.data()); - } else { - ALOGV("%s: Metadata written to blob. Validation success", - __FUNCTION__); - } - } + const uintptr_t metadataStart = ALIGN_TO(blob.data(), alignment); + offset = metadataStart - reinterpret_cast<uintptr_t>(blob.data()); + ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu", + __FUNCTION__, alignment, metadataStart, offset); + copy_camera_metadata(reinterpret_cast<void*>(metadataStart), metadataSize, metadata); // Not too big of a problem since receiving side does hard validation // Don't check the size since the compact size could be larger @@ -528,6 +602,9 @@ status_t CameraMetadata::writeToParcel(Parcel& data, } while(false); blob.release(); + // arg2 = offset (int32) + res = data.writeInt32(static_cast<int32_t>(offset)); + return res; } diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp index af091f4..99e5df4 100644 --- a/camera/CameraParameters.cpp +++ b/camera/CameraParameters.cpp @@ -16,6 +16,7 @@ */ #define LOG_TAG "CameraParams" +// #define LOG_NDEBUG 0 #include <utils/Log.h> #include <string.h> @@ -198,6 +199,8 @@ String8 CameraParameters::flatten() const flattened += ";"; } + ALOGV("%s: Flattened params = %s", __FUNCTION__, flattened.string()); + return flattened; } @@ -247,7 +250,9 @@ void CameraParameters::set(const char *key, const char *value) return; } - mMap.replaceValueFor(String8(key), String8(value)); + // Replacing a value updates the key's order to be the new largest order + ssize_t res = mMap.replaceValueFor(String8(key), String8(value)); + LOG_ALWAYS_FATAL_IF(res < 0, "replaceValueFor(%s,%s) failed", key, value); } void CameraParameters::set(const char *key, int value) @@ -266,10 +271,12 @@ void CameraParameters::setFloat(const char *key, float value) const char *CameraParameters::get(const char *key) const { - String8 v = mMap.valueFor(String8(key)); - if (v.length() == 0) - return 0; - return v.string(); + ssize_t idx = mMap.indexOfKey(String8(key)); + if (idx < 0) { + return NULL; + } else { + return mMap.valueAt(idx).string(); + } } int CameraParameters::getInt(const char *key) const @@ -287,6 +294,36 @@ float CameraParameters::getFloat(const char *key) const return strtof(v, 0); } +status_t CameraParameters::compareSetOrder(const char *key1, const char *key2, + int *order) const { + if (key1 == NULL) { + ALOGE("%s: key1 must not be NULL", __FUNCTION__); + return BAD_VALUE; + } else if (key2 == NULL) { + ALOGE("%s: key2 must not be NULL", __FUNCTION__); + return BAD_VALUE; + } else if (order == NULL) { + ALOGE("%s: order must not be NULL", __FUNCTION__); + return BAD_VALUE; + } + + ssize_t index1 = mMap.indexOfKey(String8(key1)); + ssize_t index2 = mMap.indexOfKey(String8(key2)); + if (index1 < 0) { + ALOGW("%s: Key1 (%s) was not set", __FUNCTION__, key1); + return NAME_NOT_FOUND; + } else if (index2 < 0) { + ALOGW("%s: Key2 (%s) was not set", __FUNCTION__, key2); + return NAME_NOT_FOUND; + } + + *order = (index1 == index2) ? 0 : + (index1 < index2) ? -1 : + 1; + + return OK; +} + void CameraParameters::remove(const char *key) { mMap.removeItem(String8(key)); @@ -412,6 +449,12 @@ void CameraParameters::getPreviewFpsRange(int *min_fps, int *max_fps) const parse_pair(p, min_fps, max_fps, ','); } +void CameraParameters::setPreviewFpsRange(int min_fps, int max_fps) +{ + String8 str = String8::format("%d,%d", min_fps, max_fps); + set(KEY_PREVIEW_FPS_RANGE, str.string()); +} + void CameraParameters::setPreviewFormat(const char *format) { set(KEY_PREVIEW_FORMAT, format); diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index 5fc89fb..b86651f 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BpCameraService" #include <utils/Log.h> +#include <utils/Errors.h> #include <stdint.h> #include <sys/types.h> @@ -34,6 +35,7 @@ #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> #include <camera/CameraMetadata.h> +#include <camera/VendorTagDescriptor.h> namespace android { @@ -143,6 +145,24 @@ public: return result; } + // Get enumeration and description of vendor tags for camera + virtual status_t getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + remote()->transact(BnCameraService::GET_CAMERA_VENDOR_TAG_DESCRIPTOR, data, &reply); + + if (readExceptionCode(reply)) return -EPROTO; + status_t result = reply.readInt32(); + + if (reply.readInt32() != 0) { + sp<VendorTagDescriptor> d; + if (VendorTagDescriptor::createFromParcel(&reply, /*out*/d) == OK) { + desc = d; + } + } + return result; + } + // connect to camera service (android.hardware.Camera) virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16 &clientPackageName, int clientUid, @@ -275,6 +295,22 @@ status_t BnCameraService::onTransact( info.writeToParcel(reply); return NO_ERROR; } break; + case GET_CAMERA_VENDOR_TAG_DESCRIPTOR: { + CHECK_INTERFACE(ICameraService, data, reply); + sp<VendorTagDescriptor> d; + status_t result = getCameraVendorTagDescriptor(d); + reply->writeNoException(); + reply->writeInt32(result); + + // out-variables are after exception and return value + if (d == NULL) { + reply->writeInt32(0); + } else { + reply->writeInt32(1); // means the parcelable is included + d->writeToParcel(reply); + } + return NO_ERROR; + } break; case CONNECT: { CHECK_INTERFACE(ICameraService, data, reply); sp<ICameraClient> cameraClient = @@ -284,7 +320,7 @@ status_t BnCameraService::onTransact( int32_t clientUid = data.readInt32(); sp<ICamera> camera; status_t status = connect(cameraClient, cameraId, - clientName, clientUid, /*out*/ camera); + clientName, clientUid, /*out*/camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { @@ -304,7 +340,7 @@ status_t BnCameraService::onTransact( int32_t clientUid = data.readInt32(); sp<IProCameraUser> camera; status_t status = connectPro(cameraClient, cameraId, - clientName, clientUid, /*out*/ camera); + clientName, clientUid, /*out*/camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { @@ -324,7 +360,7 @@ status_t BnCameraService::onTransact( int32_t clientUid = data.readInt32(); sp<ICameraDeviceUser> camera; status_t status = connectDevice(cameraClient, cameraId, - clientName, clientUid, /*out*/ camera); + clientName, clientUid, /*out*/camera); reply->writeNoException(); reply->writeInt32(status); if (camera != NULL) { diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp index ba5a48c..48f8e8e 100644 --- a/camera/ProCamera.cpp +++ b/camera/ProCamera.cpp @@ -249,11 +249,14 @@ status_t ProCamera::createStreamCpu(int width, int height, int format, sp <IProCameraUser> c = mCamera; if (c == 0) return NO_INIT; - sp<BufferQueue> bq = new BufferQueue(); - sp<CpuConsumer> cc = new CpuConsumer(bq, heapCount/*, synchronousMode*/); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + sp<CpuConsumer> cc = new CpuConsumer(consumer, heapCount + /*, synchronousMode*/); cc->setName(String8("ProCamera::mCpuConsumer")); - sp<Surface> stc = new Surface(bq); + sp<Surface> stc = new Surface(producer); status_t s = createStream(width, height, format, stc->getIGraphicBufferProducer(), diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp new file mode 100644 index 0000000..a0a6a51 --- /dev/null +++ b/camera/VendorTagDescriptor.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2014 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 "VenderTagDescriptor" + +#include <binder/Parcel.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/Mutex.h> +#include <utils/Vector.h> +#include <system/camera_metadata.h> +#include <camera_metadata_hidden.h> + +#include "camera/VendorTagDescriptor.h" + +#include <string.h> + +namespace android { + +extern "C" { + +static int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v); +static void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tagArray); +static const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, uint32_t tag); +static const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32_t tag); +static int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* v, uint32_t tag); + +} /* extern "C" */ + + +static Mutex sLock; +static sp<VendorTagDescriptor> sGlobalVendorTagDescriptor; + +VendorTagDescriptor::VendorTagDescriptor() {} +VendorTagDescriptor::~VendorTagDescriptor() {} + +status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vOps, + /*out*/ + sp<VendorTagDescriptor>& descriptor) { + if (vOps == NULL) { + ALOGE("%s: vendor_tag_ops argument was NULL.", __FUNCTION__); + return BAD_VALUE; + } + + int tagCount = vOps->get_tag_count(vOps); + if (tagCount < 0 || tagCount > INT32_MAX) { + ALOGE("%s: tag count %d from vendor ops is invalid.", __FUNCTION__, tagCount); + return BAD_VALUE; + } + + Vector<uint32_t> tagArray; + LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount, + "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount); + + vOps->get_all_tags(vOps, /*out*/tagArray.editArray()); + + sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); + desc->mTagCount = tagCount; + + for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) { + uint32_t tag = tagArray[i]; + if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { + ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag); + return BAD_VALUE; + } + const char *tagName = vOps->get_tag_name(vOps, tag); + if (tagName == NULL) { + ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag); + return BAD_VALUE; + } + desc->mTagToNameMap.add(tag, String8(tagName)); + const char *sectionName = vOps->get_section_name(vOps, tag); + if (sectionName == NULL) { + ALOGE("%s: no section name defined for vendor tag %d.", __FUNCTION__, tag); + return BAD_VALUE; + } + desc->mTagToSectionMap.add(tag, String8(sectionName)); + int tagType = vOps->get_tag_type(vOps, tag); + if (tagType < 0 || tagType >= NUM_TYPES) { + ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); + return BAD_VALUE; + } + desc->mTagToTypeMap.add(tag, tagType); + } + descriptor = desc; + return OK; +} + +status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, + /*out*/ + sp<VendorTagDescriptor>& descriptor) { + status_t res = OK; + if (parcel == NULL) { + ALOGE("%s: parcel argument was NULL.", __FUNCTION__); + return BAD_VALUE; + } + + int32_t tagCount = 0; + if ((res = parcel->readInt32(&tagCount)) != OK) { + ALOGE("%s: could not read tag count from parcel", __FUNCTION__); + return res; + } + + if (tagCount < 0 || tagCount > INT32_MAX) { + ALOGE("%s: tag count %d from vendor ops is invalid.", __FUNCTION__, tagCount); + return BAD_VALUE; + } + + sp<VendorTagDescriptor> desc = new VendorTagDescriptor(); + desc->mTagCount = tagCount; + + uint32_t tag; + int32_t tagType; + for (int32_t i = 0; i < tagCount; ++i) { + if ((res = parcel->readInt32(reinterpret_cast<int32_t*>(&tag))) != OK) { + ALOGE("%s: could not read tag id from parcel for index %d", __FUNCTION__, i); + break; + } + if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { + ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag); + res = BAD_VALUE; + break; + } + if ((res = parcel->readInt32(&tagType)) != OK) { + ALOGE("%s: could not read tag type from parcel for tag %d", __FUNCTION__, tag); + break; + } + if (tagType < 0 || tagType >= NUM_TYPES) { + ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); + res = BAD_VALUE; + break; + } + String8 tagName = parcel->readString8(); + if (tagName.isEmpty()) { + ALOGE("%s: parcel tag name was NULL for tag %d.", __FUNCTION__, tag); + res = NOT_ENOUGH_DATA; + break; + } + String8 sectionName = parcel->readString8(); + if (sectionName.isEmpty()) { + ALOGE("%s: parcel section name was NULL for tag %d.", __FUNCTION__, tag); + res = NOT_ENOUGH_DATA; + break; + } + + desc->mTagToNameMap.add(tag, tagName); + desc->mTagToSectionMap.add(tag, sectionName); + desc->mTagToTypeMap.add(tag, tagType); + } + + if (res != OK) { + return res; + } + + descriptor = desc; + return res; +} + +int VendorTagDescriptor::getTagCount() const { + size_t size = mTagToNameMap.size(); + if (size == 0) { + return VENDOR_TAG_COUNT_ERR; + } + return size; +} + +void VendorTagDescriptor::getTagArray(uint32_t* tagArray) const { + size_t size = mTagToNameMap.size(); + for (size_t i = 0; i < size; ++i) { + tagArray[i] = mTagToNameMap.keyAt(i); + } +} + +const char* VendorTagDescriptor::getSectionName(uint32_t tag) const { + ssize_t index = mTagToSectionMap.indexOfKey(tag); + if (index < 0) { + return VENDOR_SECTION_NAME_ERR; + } + return mTagToSectionMap.valueAt(index).string(); +} + +const char* VendorTagDescriptor::getTagName(uint32_t tag) const { + ssize_t index = mTagToNameMap.indexOfKey(tag); + if (index < 0) { + return VENDOR_TAG_NAME_ERR; + } + return mTagToNameMap.valueAt(index).string(); +} + +int VendorTagDescriptor::getTagType(uint32_t tag) const { + ssize_t index = mTagToNameMap.indexOfKey(tag); + if (index < 0) { + return VENDOR_TAG_TYPE_ERR; + } + return mTagToTypeMap.valueFor(tag); +} + +status_t VendorTagDescriptor::writeToParcel(Parcel* parcel) const { + status_t res = OK; + if (parcel == NULL) { + ALOGE("%s: parcel argument was NULL.", __FUNCTION__); + return BAD_VALUE; + } + + if ((res = parcel->writeInt32(mTagCount)) != OK) { + return res; + } + + size_t size = mTagToNameMap.size(); + uint32_t tag; + int32_t tagType; + for (size_t i = 0; i < size; ++i) { + tag = mTagToNameMap.keyAt(i); + String8 tagName = mTagToNameMap[i]; + String8 sectionName = mTagToSectionMap.valueFor(tag); + tagType = mTagToTypeMap.valueFor(tag); + if ((res = parcel->writeInt32(tag)) != OK) break; + if ((res = parcel->writeInt32(tagType)) != OK) break; + if ((res = parcel->writeString8(tagName)) != OK) break; + if ((res = parcel->writeString8(sectionName)) != OK) break; + } + + return res; +} + +status_t VendorTagDescriptor::setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc) { + status_t res = OK; + Mutex::Autolock al(sLock); + sGlobalVendorTagDescriptor = desc; + + vendor_tag_ops_t* opsPtr = NULL; + if (desc != NULL) { + opsPtr = &(desc->mVendorOps); + opsPtr->get_tag_count = vendor_tag_descriptor_get_tag_count; + opsPtr->get_all_tags = vendor_tag_descriptor_get_all_tags; + opsPtr->get_section_name = vendor_tag_descriptor_get_section_name; + opsPtr->get_tag_name = vendor_tag_descriptor_get_tag_name; + opsPtr->get_tag_type = vendor_tag_descriptor_get_tag_type; + } + if((res = set_camera_metadata_vendor_ops(opsPtr)) != OK) { + ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d)." + , __FUNCTION__, strerror(-res), res); + } + return res; +} + +void VendorTagDescriptor::clearGlobalVendorTagDescriptor() { + Mutex::Autolock al(sLock); + set_camera_metadata_vendor_ops(NULL); + sGlobalVendorTagDescriptor.clear(); +} + +sp<VendorTagDescriptor> VendorTagDescriptor::getGlobalVendorTagDescriptor() { + Mutex::Autolock al(sLock); + return sGlobalVendorTagDescriptor; +} + +extern "C" { + +int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_TAG_COUNT_ERR; + } + return sGlobalVendorTagDescriptor->getTagCount(); +} + +void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tagArray) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return; + } + sGlobalVendorTagDescriptor->getTagArray(tagArray); +} + +const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, uint32_t tag) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_SECTION_NAME_ERR; + } + return sGlobalVendorTagDescriptor->getSectionName(tag); +} + +const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32_t tag) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_TAG_NAME_ERR; + } + return sGlobalVendorTagDescriptor->getTagName(tag); +} + +int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* v, uint32_t tag) { + Mutex::Autolock al(sLock); + if (sGlobalVendorTagDescriptor == NULL) { + ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); + return VENDOR_TAG_TYPE_ERR; + } + return sGlobalVendorTagDescriptor->getTagType(tag); +} + +} /* extern "C" */ +} /* namespace android */ diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk index ec13911..61385e5 100644 --- a/camera/tests/Android.mk +++ b/camera/tests/Android.mk @@ -1,9 +1,24 @@ +# Copyright 2013 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. + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ main.cpp \ ProCameraTests.cpp \ + VendorTagDescriptorTests.cpp LOCAL_SHARED_LIBRARIES := \ libutils \ @@ -26,6 +41,8 @@ LOCAL_C_INCLUDES += \ external/gtest/include \ external/stlport/stlport \ system/media/camera/include \ + system/media/private/camera/include \ + system/media/camera/tests \ frameworks/av/services/camera/libcameraservice \ frameworks/av/include/camera \ frameworks/native/include \ diff --git a/camera/tests/VendorTagDescriptorTests.cpp b/camera/tests/VendorTagDescriptorTests.cpp new file mode 100644 index 0000000..6624e79 --- /dev/null +++ b/camera/tests/VendorTagDescriptorTests.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014 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_NDEBUG 0 +#define LOG_TAG "VendorTagDescriptorTests" + +#include <binder/Parcel.h> +#include <camera/VendorTagDescriptor.h> +#include <camera_metadata_tests_fake_vendor.h> +#include <camera_metadata_hidden.h> +#include <system/camera_vendor_tags.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/RefBase.h> + +#include <gtest/gtest.h> +#include <stdint.h> + +using namespace android; + +enum { + BAD_TAG_ARRAY = 0xDEADBEEFu, + BAD_TAG = 0x8DEADBADu, +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static bool ContainsTag(uint32_t* tagArray, size_t size, uint32_t tag) { + for (size_t i = 0; i < size; ++i) { + if (tag == tagArray[i]) return true; + } + return false; +} + +#define EXPECT_CONTAINS_TAG(t, a) \ + EXPECT_TRUE(ContainsTag(a, ARRAY_SIZE(a), t)) + +#define ASSERT_NOT_NULL(x) \ + ASSERT_TRUE((x) != NULL) + +extern "C" { + +static int default_get_tag_count(const vendor_tag_ops_t* vOps) { + return VENDOR_TAG_COUNT_ERR; +} + +static void default_get_all_tags(const vendor_tag_ops_t* vOps, uint32_t* tagArray) { + //Noop +} + +static const char* default_get_section_name(const vendor_tag_ops_t* vOps, uint32_t tag) { + return VENDOR_SECTION_NAME_ERR; +} + +static const char* default_get_tag_name(const vendor_tag_ops_t* vOps, uint32_t tag) { + return VENDOR_TAG_NAME_ERR; +} + +static int default_get_tag_type(const vendor_tag_ops_t* vOps, uint32_t tag) { + return VENDOR_TAG_TYPE_ERR; +} + +} /*extern "C"*/ + +// Set default vendor operations for a vendor_tag_ops struct +static void FillWithDefaults(vendor_tag_ops_t* vOps) { + ASSERT_NOT_NULL(vOps); + vOps->get_tag_count = default_get_tag_count; + vOps->get_all_tags = default_get_all_tags; + vOps->get_section_name = default_get_section_name; + vOps->get_tag_name = default_get_tag_name; + vOps->get_tag_type = default_get_tag_type; +} + +/** + * Test if values from VendorTagDescriptor methods match corresponding values + * from vendor_tag_ops functions. + */ +TEST(VendorTagDescriptorTest, ConsistentWithVendorTags) { + sp<VendorTagDescriptor> vDesc; + const vendor_tag_ops_t *vOps = &fakevendor_ops; + EXPECT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(vOps, /*out*/vDesc)); + + ASSERT_NOT_NULL(vDesc); + + // Ensure reasonable tag count + int tagCount = vDesc->getTagCount(); + EXPECT_EQ(tagCount, vOps->get_tag_count(vOps)); + + uint32_t descTagArray[tagCount]; + uint32_t opsTagArray[tagCount]; + + // Get all tag ids + vDesc->getTagArray(descTagArray); + vOps->get_all_tags(vOps, opsTagArray); + + ASSERT_NOT_NULL(descTagArray); + ASSERT_NOT_NULL(opsTagArray); + + uint32_t tag; + for (int i = 0; i < tagCount; ++i) { + // For each tag id, check whether type, section name, tag name match + tag = descTagArray[i]; + EXPECT_CONTAINS_TAG(tag, opsTagArray); + EXPECT_EQ(vDesc->getTagType(tag), vOps->get_tag_type(vOps, tag)); + EXPECT_STREQ(vDesc->getSectionName(tag), vOps->get_section_name(vOps, tag)); + EXPECT_STREQ(vDesc->getTagName(tag), vOps->get_tag_name(vOps, tag)); + } +} + +/** + * Test if values from VendorTagDescriptor methods stay consistent after being + * parcelled/unparcelled. + */ +TEST(VendorTagDescriptorTest, ConsistentAcrossParcel) { + sp<VendorTagDescriptor> vDescOriginal, vDescParceled; + const vendor_tag_ops_t *vOps = &fakevendor_ops; + EXPECT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(vOps, /*out*/vDescOriginal)); + + ASSERT_TRUE(vDescOriginal != NULL); + + Parcel p; + + // Check whether parcel read/write succeed + EXPECT_EQ(OK, vDescOriginal->writeToParcel(&p)); + p.setDataPosition(0); + ASSERT_EQ(OK, VendorTagDescriptor::createFromParcel(&p, vDescParceled)); + + // Ensure consistent tag count + int tagCount = vDescOriginal->getTagCount(); + ASSERT_EQ(tagCount, vDescParceled->getTagCount()); + + uint32_t descTagArray[tagCount]; + uint32_t desc2TagArray[tagCount]; + + // Get all tag ids + vDescOriginal->getTagArray(descTagArray); + vDescParceled->getTagArray(desc2TagArray); + + ASSERT_NOT_NULL(descTagArray); + ASSERT_NOT_NULL(desc2TagArray); + + uint32_t tag; + for (int i = 0; i < tagCount; ++i) { + // For each tag id, check consistency between the two vendor tag + // descriptors for each type, section name, tag name + tag = descTagArray[i]; + EXPECT_CONTAINS_TAG(tag, desc2TagArray); + EXPECT_EQ(vDescOriginal->getTagType(tag), vDescParceled->getTagType(tag)); + EXPECT_STREQ(vDescOriginal->getSectionName(tag), vDescParceled->getSectionName(tag)); + EXPECT_STREQ(vDescOriginal->getTagName(tag), vDescParceled->getTagName(tag)); + } +} + +/** + * Test defaults and error conditions. + */ +TEST(VendorTagDescriptorTest, ErrorConditions) { + sp<VendorTagDescriptor> vDesc; + vendor_tag_ops_t vOps; + FillWithDefaults(&vOps); + + // Ensure create fails when using null vOps + EXPECT_EQ(BAD_VALUE, VendorTagDescriptor::createDescriptorFromOps(/*vOps*/NULL, vDesc)); + + // Ensure create works when there are no vtags defined in a well-formed vOps + ASSERT_EQ(OK, VendorTagDescriptor::createDescriptorFromOps(&vOps, vDesc)); + + // Ensure defaults are returned when no vtags are defined, or tag is unknown + EXPECT_EQ(VENDOR_TAG_COUNT_ERR, vDesc->getTagCount()); + uint32_t* tagArray = reinterpret_cast<uint32_t*>(BAD_TAG_ARRAY); + uint32_t* testArray = tagArray; + vDesc->getTagArray(tagArray); + EXPECT_EQ(testArray, tagArray); + EXPECT_EQ(VENDOR_SECTION_NAME_ERR, vDesc->getSectionName(BAD_TAG)); + EXPECT_EQ(VENDOR_TAG_NAME_ERR, vDesc->getTagName(BAD_TAG)); + EXPECT_EQ(VENDOR_TAG_TYPE_ERR, vDesc->getTagType(BAD_TAG)); + + // Make sure global can be set/cleared + const vendor_tag_ops_t *fakeOps = &fakevendor_ops; + sp<VendorTagDescriptor> prevGlobal = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + + EXPECT_TRUE(VendorTagDescriptor::getGlobalVendorTagDescriptor() == NULL); + EXPECT_EQ(OK, VendorTagDescriptor::setAsGlobalVendorTagDescriptor(vDesc)); + EXPECT_TRUE(VendorTagDescriptor::getGlobalVendorTagDescriptor() != NULL); + EXPECT_EQ(VENDOR_SECTION_NAME_ERR, vDesc->getSectionName(BAD_TAG)); + EXPECT_EQ(OK, VendorTagDescriptor::setAsGlobalVendorTagDescriptor(prevGlobal)); + EXPECT_EQ(prevGlobal, VendorTagDescriptor::getGlobalVendorTagDescriptor()); +} + |