mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
There is no guarantee that the static metadata pointer passed in the "DeviceCb" contructor will be valid after the call completes. The device callback instance is expected to be active until the camera session is open. Clone the required metadata in "DeviceCb" and manage the lifetime appropriately by using the "CameraMetadata" wrapper. Bug: 135976837 Test: CameraHidlTest#processCaptureRequestPreview Change-Id: Idd3c6c8c2e5a3fc44a49712e25a04009cbd471b1
6334 lines
272 KiB
C++
6334 lines
272 KiB
C++
/*
|
|
* Copyright (C) 2016-2018 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 "camera_hidl_hal_test"
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <mutex>
|
|
#include <regex>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <condition_variable>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <android/hardware/camera/device/1.0/ICameraDevice.h>
|
|
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
|
|
#include <android/hardware/camera/device/3.5/ICameraDevice.h>
|
|
#include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
|
|
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
|
|
#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
|
|
#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
|
|
#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
|
|
#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
|
|
#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
|
|
#include <android/hardware/camera/metadata/3.4/types.h>
|
|
#include <android/hidl/manager/1.0/IServiceManager.h>
|
|
#include <binder/MemoryHeapBase.h>
|
|
#include <CameraMetadata.h>
|
|
#include <CameraParameters.h>
|
|
#include <cutils/properties.h>
|
|
#include <fmq/MessageQueue.h>
|
|
#include <grallocusage/GrallocUsageConversion.h>
|
|
#include <gui/BufferItemConsumer.h>
|
|
#include <gui/BufferQueue.h>
|
|
#include <gui/Surface.h>
|
|
#include <hardware/gralloc.h>
|
|
#include <hardware/gralloc1.h>
|
|
#include <system/camera.h>
|
|
#include <system/camera_metadata.h>
|
|
#include <ui/GraphicBuffer.h>
|
|
|
|
#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
|
|
#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
|
|
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
|
|
#include <android/hardware/graphics/mapper/2.0/types.h>
|
|
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
|
|
#include <android/hidl/allocator/1.0/IAllocator.h>
|
|
#include <android/hidl/memory/1.0/IMapper.h>
|
|
#include <android/hidl/memory/1.0/IMemory.h>
|
|
|
|
#include <VtsHalHidlTargetTestBase.h>
|
|
#include <VtsHalHidlTargetTestEnvBase.h>
|
|
|
|
using namespace ::android::hardware::camera::device;
|
|
using ::android::hardware::Return;
|
|
using ::android::hardware::Void;
|
|
using ::android::hardware::hidl_bitfield;
|
|
using ::android::hardware::hidl_handle;
|
|
using ::android::hardware::hidl_string;
|
|
using ::android::hardware::hidl_vec;
|
|
using ::android::sp;
|
|
using ::android::wp;
|
|
using ::android::GraphicBuffer;
|
|
using ::android::IGraphicBufferProducer;
|
|
using ::android::IGraphicBufferConsumer;
|
|
using ::android::BufferQueue;
|
|
using ::android::BufferItemConsumer;
|
|
using ::android::Surface;
|
|
using ::android::hardware::graphics::common::V1_0::BufferUsage;
|
|
using ::android::hardware::graphics::common::V1_0::Dataspace;
|
|
using ::android::hardware::graphics::common::V1_0::PixelFormat;
|
|
using ::android::hardware::camera::common::V1_0::Status;
|
|
using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
|
|
using ::android::hardware::camera::common::V1_0::TorchMode;
|
|
using ::android::hardware::camera::common::V1_0::TorchModeStatus;
|
|
using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
|
|
using ::android::hardware::camera::common::V1_0::helper::Size;
|
|
using ::android::hardware::camera::provider::V2_4::ICameraProvider;
|
|
using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
|
|
using ::android::hardware::camera::device::V3_2::ICameraDevice;
|
|
using ::android::hardware::camera::device::V3_2::BufferCache;
|
|
using ::android::hardware::camera::device::V3_2::CaptureRequest;
|
|
using ::android::hardware::camera::device::V3_2::CaptureResult;
|
|
using ::android::hardware::camera::device::V3_2::ICameraDeviceSession;
|
|
using ::android::hardware::camera::device::V3_2::NotifyMsg;
|
|
using ::android::hardware::camera::device::V3_2::RequestTemplate;
|
|
using ::android::hardware::camera::device::V3_2::StreamType;
|
|
using ::android::hardware::camera::device::V3_2::StreamRotation;
|
|
using ::android::hardware::camera::device::V3_2::StreamConfiguration;
|
|
using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
|
|
using ::android::hardware::camera::device::V3_2::CameraMetadata;
|
|
using ::android::hardware::camera::device::V3_2::HalStreamConfiguration;
|
|
using ::android::hardware::camera::device::V3_2::BufferStatus;
|
|
using ::android::hardware::camera::device::V3_2::StreamBuffer;
|
|
using ::android::hardware::camera::device::V3_2::MsgType;
|
|
using ::android::hardware::camera::device::V3_2::ErrorMsg;
|
|
using ::android::hardware::camera::device::V3_2::ErrorCode;
|
|
using ::android::hardware::camera::device::V1_0::CameraFacing;
|
|
using ::android::hardware::camera::device::V1_0::NotifyCallbackMsg;
|
|
using ::android::hardware::camera::device::V1_0::CommandType;
|
|
using ::android::hardware::camera::device::V1_0::DataCallbackMsg;
|
|
using ::android::hardware::camera::device::V1_0::CameraFrameMetadata;
|
|
using ::android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback;
|
|
using ::android::hardware::camera::device::V1_0::FrameCallbackFlag;
|
|
using ::android::hardware::camera::device::V1_0::HandleTimestampMessage;
|
|
using ::android::hardware::camera::metadata::V3_4::CameraMetadataEnumAndroidSensorInfoColorFilterArrangement;
|
|
using ::android::hardware::camera::metadata::V3_4::CameraMetadataTag;
|
|
using ::android::hardware::camera::device::V3_4::PhysicalCameraMetadata;
|
|
using ::android::hardware::MessageQueue;
|
|
using ::android::hardware::kSynchronizedReadWrite;
|
|
using ::android::hidl::allocator::V1_0::IAllocator;
|
|
using ::android::hidl::memory::V1_0::IMemory;
|
|
using ::android::hidl::memory::V1_0::IMapper;
|
|
using ResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
|
|
using ::android::hidl::manager::V1_0::IServiceManager;
|
|
|
|
using namespace ::android::hardware::camera;
|
|
|
|
const uint32_t kMaxPreviewWidth = 1920;
|
|
const uint32_t kMaxPreviewHeight = 1080;
|
|
const uint32_t kMaxVideoWidth = 4096;
|
|
const uint32_t kMaxVideoHeight = 2160;
|
|
const int64_t kStreamBufferTimeoutSec = 3;
|
|
const int64_t kAutoFocusTimeoutSec = 5;
|
|
const int64_t kTorchTimeoutSec = 1;
|
|
const int64_t kEmptyFlushTimeoutMSec = 200;
|
|
const char kDumpOutput[] = "/dev/null";
|
|
const uint32_t kBurstFrameCount = 10;
|
|
const int64_t kBufferReturnTimeoutSec = 1;
|
|
|
|
struct AvailableStream {
|
|
int32_t width;
|
|
int32_t height;
|
|
int32_t format;
|
|
};
|
|
|
|
struct AvailableZSLInputOutput {
|
|
int32_t inputFormat;
|
|
int32_t outputFormat;
|
|
};
|
|
|
|
enum ReprocessType {
|
|
PRIV_REPROCESS,
|
|
YUV_REPROCESS,
|
|
};
|
|
|
|
namespace {
|
|
// "device@<version>/legacy/<id>"
|
|
const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
|
|
const int CAMERA_DEVICE_API_VERSION_3_5 = 0x305;
|
|
const int CAMERA_DEVICE_API_VERSION_3_4 = 0x304;
|
|
const int CAMERA_DEVICE_API_VERSION_3_3 = 0x303;
|
|
const int CAMERA_DEVICE_API_VERSION_3_2 = 0x302;
|
|
const int CAMERA_DEVICE_API_VERSION_1_0 = 0x100;
|
|
const char *kHAL3_5 = "3.5";
|
|
const char *kHAL3_4 = "3.4";
|
|
const char *kHAL3_3 = "3.3";
|
|
const char *kHAL3_2 = "3.2";
|
|
const char *kHAL1_0 = "1.0";
|
|
|
|
bool matchDeviceName(const hidl_string& deviceName,
|
|
const hidl_string &providerType,
|
|
std::string* deviceVersion,
|
|
std::string* cameraId) {
|
|
::android::String8 pattern;
|
|
pattern.appendFormat(kDeviceNameRE, providerType.c_str());
|
|
std::regex e(pattern.string());
|
|
std::string deviceNameStd(deviceName.c_str());
|
|
std::smatch sm;
|
|
if (std::regex_match(deviceNameStd, sm, e)) {
|
|
if (deviceVersion != nullptr) {
|
|
*deviceVersion = sm[1];
|
|
}
|
|
if (cameraId != nullptr) {
|
|
*cameraId = sm[2];
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int getCameraDeviceVersion(const hidl_string& deviceName,
|
|
const hidl_string &providerType) {
|
|
std::string version;
|
|
bool match = matchDeviceName(deviceName, providerType, &version, nullptr);
|
|
if (!match) {
|
|
return -1;
|
|
}
|
|
|
|
if (version.compare(kHAL3_5) == 0) {
|
|
return CAMERA_DEVICE_API_VERSION_3_5;
|
|
} else if (version.compare(kHAL3_4) == 0) {
|
|
return CAMERA_DEVICE_API_VERSION_3_4;
|
|
} else if (version.compare(kHAL3_3) == 0) {
|
|
return CAMERA_DEVICE_API_VERSION_3_3;
|
|
} else if (version.compare(kHAL3_2) == 0) {
|
|
return CAMERA_DEVICE_API_VERSION_3_2;
|
|
} else if (version.compare(kHAL1_0) == 0) {
|
|
return CAMERA_DEVICE_API_VERSION_1_0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool parseProviderName(const std::string& name, std::string *type /*out*/,
|
|
uint32_t *id /*out*/) {
|
|
if (!type || !id) {
|
|
ADD_FAILURE();
|
|
return false;
|
|
}
|
|
|
|
std::string::size_type slashIdx = name.find('/');
|
|
if (slashIdx == std::string::npos || slashIdx == name.size() - 1) {
|
|
ADD_FAILURE() << "Provider name does not have / separator between type"
|
|
"and id";
|
|
return false;
|
|
}
|
|
|
|
std::string typeVal = name.substr(0, slashIdx);
|
|
|
|
char *endPtr;
|
|
errno = 0;
|
|
long idVal = strtol(name.c_str() + slashIdx + 1, &endPtr, 10);
|
|
if (errno != 0) {
|
|
ADD_FAILURE() << "cannot parse provider id as an integer:" <<
|
|
name.c_str() << strerror(errno) << errno;
|
|
return false;
|
|
}
|
|
if (endPtr != name.c_str() + name.size()) {
|
|
ADD_FAILURE() << "provider id has unexpected length " << name.c_str();
|
|
return false;
|
|
}
|
|
if (idVal < 0) {
|
|
ADD_FAILURE() << "id is negative: " << name.c_str() << idVal;
|
|
return false;
|
|
}
|
|
|
|
*type = typeVal;
|
|
*id = static_cast<uint32_t>(idVal);
|
|
|
|
return true;
|
|
}
|
|
|
|
Status mapToStatus(::android::status_t s) {
|
|
switch(s) {
|
|
case ::android::OK:
|
|
return Status::OK ;
|
|
case ::android::BAD_VALUE:
|
|
return Status::ILLEGAL_ARGUMENT ;
|
|
case -EBUSY:
|
|
return Status::CAMERA_IN_USE;
|
|
case -EUSERS:
|
|
return Status::MAX_CAMERAS_IN_USE;
|
|
case ::android::UNKNOWN_TRANSACTION:
|
|
return Status::METHOD_NOT_SUPPORTED;
|
|
case ::android::INVALID_OPERATION:
|
|
return Status::OPERATION_NOT_SUPPORTED;
|
|
case ::android::DEAD_OBJECT:
|
|
return Status::CAMERA_DISCONNECTED;
|
|
}
|
|
ALOGW("Unexpected HAL status code %d", s);
|
|
return Status::OPERATION_NOT_SUPPORTED;
|
|
}
|
|
|
|
void getFirstApiLevel(/*out*/int32_t* outApiLevel) {
|
|
int32_t firstApiLevel = property_get_int32("ro.product.first_api_level", /*default*/-1);
|
|
if (firstApiLevel < 0) {
|
|
firstApiLevel = property_get_int32("ro.build.version.sdk", /*default*/-1);
|
|
}
|
|
ASSERT_GT(firstApiLevel, 0); // first_api_level must exist
|
|
*outApiLevel = firstApiLevel;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Test environment for camera
|
|
class CameraHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
|
|
public:
|
|
// get the test environment singleton
|
|
static CameraHidlEnvironment* Instance() {
|
|
static CameraHidlEnvironment* instance = new CameraHidlEnvironment;
|
|
return instance;
|
|
}
|
|
|
|
virtual void HidlSetUp() override { ALOGI("SetUp CameraHidlEnvironment"); }
|
|
|
|
virtual void HidlTearDown() override { ALOGI("TearDown CameraHidlEnvironment"); }
|
|
|
|
virtual void registerTestServices() override { registerTestService<ICameraProvider>(); }
|
|
|
|
private:
|
|
CameraHidlEnvironment() {}
|
|
|
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(CameraHidlEnvironment);
|
|
};
|
|
|
|
struct BufferItemHander: public BufferItemConsumer::FrameAvailableListener {
|
|
BufferItemHander(wp<BufferItemConsumer> consumer) : mConsumer(consumer) {}
|
|
|
|
void onFrameAvailable(const android::BufferItem&) override {
|
|
sp<BufferItemConsumer> consumer = mConsumer.promote();
|
|
ASSERT_NE(nullptr, consumer.get());
|
|
|
|
android::BufferItem buffer;
|
|
ASSERT_EQ(android::OK, consumer->acquireBuffer(&buffer, 0));
|
|
ASSERT_EQ(android::OK, consumer->releaseBuffer(buffer));
|
|
}
|
|
|
|
private:
|
|
wp<BufferItemConsumer> mConsumer;
|
|
};
|
|
|
|
struct PreviewWindowCb : public ICameraDevicePreviewCallback {
|
|
PreviewWindowCb(sp<ANativeWindow> anw) : mPreviewWidth(0),
|
|
mPreviewHeight(0), mFormat(0), mPreviewUsage(0),
|
|
mPreviewSwapInterval(-1), mCrop{-1, -1, -1, -1}, mAnw(anw) {}
|
|
|
|
using dequeueBuffer_cb =
|
|
std::function<void(Status status, uint64_t bufferId,
|
|
const hidl_handle& buffer, uint32_t stride)>;
|
|
Return<void> dequeueBuffer(dequeueBuffer_cb _hidl_cb) override;
|
|
|
|
Return<Status> enqueueBuffer(uint64_t bufferId) override;
|
|
|
|
Return<Status> cancelBuffer(uint64_t bufferId) override;
|
|
|
|
Return<Status> setBufferCount(uint32_t count) override;
|
|
|
|
Return<Status> setBuffersGeometry(uint32_t w,
|
|
uint32_t h, PixelFormat format) override;
|
|
|
|
Return<Status> setCrop(int32_t left, int32_t top,
|
|
int32_t right, int32_t bottom) override;
|
|
|
|
Return<Status> setUsage(BufferUsage usage) override;
|
|
|
|
Return<Status> setSwapInterval(int32_t interval) override;
|
|
|
|
using getMinUndequeuedBufferCount_cb =
|
|
std::function<void(Status status, uint32_t count)>;
|
|
Return<void> getMinUndequeuedBufferCount(
|
|
getMinUndequeuedBufferCount_cb _hidl_cb) override;
|
|
|
|
Return<Status> setTimestamp(int64_t timestamp) override;
|
|
|
|
private:
|
|
struct BufferHasher {
|
|
size_t operator()(const buffer_handle_t& buf) const {
|
|
if (buf == nullptr)
|
|
return 0;
|
|
|
|
size_t result = 1;
|
|
result = 31 * result + buf->numFds;
|
|
for (int i = 0; i < buf->numFds; i++) {
|
|
result = 31 * result + buf->data[i];
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
struct BufferComparator {
|
|
bool operator()(const buffer_handle_t& buf1,
|
|
const buffer_handle_t& buf2) const {
|
|
if (buf1->numFds == buf2->numFds) {
|
|
for (int i = 0; i < buf1->numFds; i++) {
|
|
if (buf1->data[i] != buf2->data[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
std::pair<bool, uint64_t> getBufferId(ANativeWindowBuffer* anb);
|
|
void cleanupCirculatingBuffers();
|
|
|
|
std::mutex mBufferIdMapLock; // protecting mBufferIdMap and mNextBufferId
|
|
typedef std::unordered_map<const buffer_handle_t, uint64_t,
|
|
BufferHasher, BufferComparator> BufferIdMap;
|
|
|
|
BufferIdMap mBufferIdMap; // stream ID -> per stream buffer ID map
|
|
std::unordered_map<uint64_t, ANativeWindowBuffer*> mReversedBufMap;
|
|
uint64_t mNextBufferId = 1;
|
|
|
|
uint32_t mPreviewWidth, mPreviewHeight;
|
|
int mFormat, mPreviewUsage;
|
|
int32_t mPreviewSwapInterval;
|
|
android_native_rect_t mCrop;
|
|
sp<ANativeWindow> mAnw; //Native window reference
|
|
};
|
|
|
|
std::pair<bool, uint64_t> PreviewWindowCb::getBufferId(
|
|
ANativeWindowBuffer* anb) {
|
|
std::lock_guard<std::mutex> lock(mBufferIdMapLock);
|
|
|
|
buffer_handle_t& buf = anb->handle;
|
|
auto it = mBufferIdMap.find(buf);
|
|
if (it == mBufferIdMap.end()) {
|
|
uint64_t bufId = mNextBufferId++;
|
|
mBufferIdMap[buf] = bufId;
|
|
mReversedBufMap[bufId] = anb;
|
|
return std::make_pair(true, bufId);
|
|
} else {
|
|
return std::make_pair(false, it->second);
|
|
}
|
|
}
|
|
|
|
void PreviewWindowCb::cleanupCirculatingBuffers() {
|
|
std::lock_guard<std::mutex> lock(mBufferIdMapLock);
|
|
mBufferIdMap.clear();
|
|
mReversedBufMap.clear();
|
|
}
|
|
|
|
Return<void> PreviewWindowCb::dequeueBuffer(dequeueBuffer_cb _hidl_cb) {
|
|
ANativeWindowBuffer* anb;
|
|
auto rc = native_window_dequeue_buffer_and_wait(mAnw.get(), &anb);
|
|
uint64_t bufferId = 0;
|
|
uint32_t stride = 0;
|
|
hidl_handle buf = nullptr;
|
|
if (rc == ::android::OK) {
|
|
auto pair = getBufferId(anb);
|
|
buf = (pair.first) ? anb->handle : nullptr;
|
|
bufferId = pair.second;
|
|
stride = anb->stride;
|
|
}
|
|
|
|
_hidl_cb(mapToStatus(rc), bufferId, buf, stride);
|
|
return Void();
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::enqueueBuffer(uint64_t bufferId) {
|
|
if (mReversedBufMap.count(bufferId) == 0) {
|
|
ALOGE("%s: bufferId %" PRIu64 " not found", __FUNCTION__, bufferId);
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
return mapToStatus(mAnw->queueBuffer(mAnw.get(),
|
|
mReversedBufMap.at(bufferId), -1));
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::cancelBuffer(uint64_t bufferId) {
|
|
if (mReversedBufMap.count(bufferId) == 0) {
|
|
ALOGE("%s: bufferId %" PRIu64 " not found", __FUNCTION__, bufferId);
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
return mapToStatus(mAnw->cancelBuffer(mAnw.get(),
|
|
mReversedBufMap.at(bufferId), -1));
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::setBufferCount(uint32_t count) {
|
|
if (mAnw.get() != nullptr) {
|
|
// WAR for b/27039775
|
|
native_window_api_disconnect(mAnw.get(), NATIVE_WINDOW_API_CAMERA);
|
|
native_window_api_connect(mAnw.get(), NATIVE_WINDOW_API_CAMERA);
|
|
if (mPreviewWidth != 0) {
|
|
native_window_set_buffers_dimensions(mAnw.get(),
|
|
mPreviewWidth, mPreviewHeight);
|
|
native_window_set_buffers_format(mAnw.get(), mFormat);
|
|
}
|
|
if (mPreviewUsage != 0) {
|
|
native_window_set_usage(mAnw.get(), mPreviewUsage);
|
|
}
|
|
if (mPreviewSwapInterval >= 0) {
|
|
mAnw->setSwapInterval(mAnw.get(), mPreviewSwapInterval);
|
|
}
|
|
if (mCrop.left >= 0) {
|
|
native_window_set_crop(mAnw.get(), &(mCrop));
|
|
}
|
|
}
|
|
|
|
auto rc = native_window_set_buffer_count(mAnw.get(), count);
|
|
if (rc == ::android::OK) {
|
|
cleanupCirculatingBuffers();
|
|
}
|
|
|
|
return mapToStatus(rc);
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::setBuffersGeometry(uint32_t w, uint32_t h,
|
|
PixelFormat format) {
|
|
auto rc = native_window_set_buffers_dimensions(mAnw.get(), w, h);
|
|
if (rc == ::android::OK) {
|
|
mPreviewWidth = w;
|
|
mPreviewHeight = h;
|
|
rc = native_window_set_buffers_format(mAnw.get(),
|
|
static_cast<int>(format));
|
|
if (rc == ::android::OK) {
|
|
mFormat = static_cast<int>(format);
|
|
}
|
|
}
|
|
|
|
return mapToStatus(rc);
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::setCrop(int32_t left, int32_t top,
|
|
int32_t right, int32_t bottom) {
|
|
android_native_rect_t crop = { left, top, right, bottom };
|
|
auto rc = native_window_set_crop(mAnw.get(), &crop);
|
|
if (rc == ::android::OK) {
|
|
mCrop = crop;
|
|
}
|
|
return mapToStatus(rc);
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::setUsage(BufferUsage usage) {
|
|
auto rc = native_window_set_usage(mAnw.get(), static_cast<int>(usage));
|
|
if (rc == ::android::OK) {
|
|
mPreviewUsage = static_cast<int>(usage);
|
|
}
|
|
return mapToStatus(rc);
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::setSwapInterval(int32_t interval) {
|
|
auto rc = mAnw->setSwapInterval(mAnw.get(), interval);
|
|
if (rc == ::android::OK) {
|
|
mPreviewSwapInterval = interval;
|
|
}
|
|
return mapToStatus(rc);
|
|
}
|
|
|
|
Return<void> PreviewWindowCb::getMinUndequeuedBufferCount(
|
|
getMinUndequeuedBufferCount_cb _hidl_cb) {
|
|
int count = 0;
|
|
auto rc = mAnw->query(mAnw.get(),
|
|
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &count);
|
|
_hidl_cb(mapToStatus(rc), count);
|
|
return Void();
|
|
}
|
|
|
|
Return<Status> PreviewWindowCb::setTimestamp(int64_t timestamp) {
|
|
return mapToStatus(native_window_set_buffers_timestamp(mAnw.get(),
|
|
timestamp));
|
|
}
|
|
|
|
// The main test class for camera HIDL HAL.
|
|
class CameraHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|
public:
|
|
virtual void SetUp() override {
|
|
string service_name = CameraHidlEnvironment::Instance()->getServiceName<ICameraProvider>();
|
|
ALOGI("get service with name: %s", service_name.c_str());
|
|
mProvider = ::testing::VtsHalHidlTargetTestBase::getService<ICameraProvider>(service_name);
|
|
ASSERT_NE(mProvider, nullptr);
|
|
|
|
uint32_t id;
|
|
ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id));
|
|
|
|
castProvider(mProvider, &mProvider2_5);
|
|
notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
|
|
}
|
|
virtual void TearDown() override {}
|
|
|
|
hidl_vec<hidl_string> getCameraDeviceNames(sp<ICameraProvider> provider);
|
|
|
|
struct EmptyDeviceCb : public V3_5::ICameraDeviceCallback {
|
|
virtual Return<void> processCaptureResult(
|
|
const hidl_vec<CaptureResult>& /*results*/) override {
|
|
ALOGI("processCaptureResult callback");
|
|
ADD_FAILURE(); // Empty callback should not reach here
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> processCaptureResult_3_4(
|
|
const hidl_vec<V3_4::CaptureResult>& /*results*/) override {
|
|
ALOGI("processCaptureResult_3_4 callback");
|
|
ADD_FAILURE(); // Empty callback should not reach here
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> notify(const hidl_vec<NotifyMsg>& /*msgs*/) override {
|
|
ALOGI("notify callback");
|
|
ADD_FAILURE(); // Empty callback should not reach here
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> requestStreamBuffers(
|
|
const hidl_vec<V3_5::BufferRequest>&,
|
|
requestStreamBuffers_cb _hidl_cb) override {
|
|
ALOGI("requestStreamBuffers callback");
|
|
// HAL might want to request buffer after configureStreams, but tests with EmptyDeviceCb
|
|
// doesn't actually need to send capture requests, so just return an error.
|
|
hidl_vec<V3_5::StreamBufferRet> emptyBufRets;
|
|
_hidl_cb(V3_5::BufferRequestStatus::FAILED_UNKNOWN, emptyBufRets);
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> returnStreamBuffers(const hidl_vec<StreamBuffer>&) override {
|
|
ALOGI("returnStreamBuffers");
|
|
ADD_FAILURE(); // Empty callback should not reach here
|
|
return Void();
|
|
}
|
|
|
|
};
|
|
|
|
struct DeviceCb : public V3_5::ICameraDeviceCallback {
|
|
DeviceCb(CameraHidlTest *parent, int deviceVersion, const camera_metadata_t *staticMeta) :
|
|
mParent(parent), mDeviceVersion(deviceVersion) {
|
|
mStaticMetadata = staticMeta;
|
|
}
|
|
|
|
Return<void> processCaptureResult_3_4(
|
|
const hidl_vec<V3_4::CaptureResult>& results) override;
|
|
Return<void> processCaptureResult(const hidl_vec<CaptureResult>& results) override;
|
|
Return<void> notify(const hidl_vec<NotifyMsg>& msgs) override;
|
|
|
|
Return<void> requestStreamBuffers(
|
|
const hidl_vec<V3_5::BufferRequest>& bufReqs,
|
|
requestStreamBuffers_cb _hidl_cb) override;
|
|
|
|
Return<void> returnStreamBuffers(const hidl_vec<StreamBuffer>& buffers) override;
|
|
|
|
void setCurrentStreamConfig(const hidl_vec<V3_2::Stream>& streams,
|
|
const hidl_vec<V3_2::HalStream>& halStreams);
|
|
|
|
void waitForBuffersReturned();
|
|
|
|
private:
|
|
bool processCaptureResultLocked(const CaptureResult& results,
|
|
hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata);
|
|
|
|
CameraHidlTest *mParent; // Parent object
|
|
int mDeviceVersion;
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata mStaticMetadata;
|
|
bool hasOutstandingBuffersLocked();
|
|
|
|
/* members for requestStreamBuffers() and returnStreamBuffers()*/
|
|
std::mutex mLock; // protecting members below
|
|
bool mUseHalBufManager = false;
|
|
hidl_vec<V3_2::Stream> mStreams;
|
|
hidl_vec<V3_2::HalStream> mHalStreams;
|
|
uint64_t mNextBufferId = 1;
|
|
using OutstandingBuffers = std::unordered_map<uint64_t, hidl_handle>;
|
|
// size == mStreams.size(). Tracking each streams outstanding buffers
|
|
std::vector<OutstandingBuffers> mOutstandingBufferIds;
|
|
std::condition_variable mFlushedCondition;
|
|
};
|
|
|
|
struct TorchProviderCb : public ICameraProviderCallback {
|
|
TorchProviderCb(CameraHidlTest *parent) : mParent(parent) {}
|
|
virtual Return<void> cameraDeviceStatusChange(
|
|
const hidl_string&, CameraDeviceStatus) override {
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> torchModeStatusChange(
|
|
const hidl_string&, TorchModeStatus newStatus) override {
|
|
std::lock_guard<std::mutex> l(mParent->mTorchLock);
|
|
mParent->mTorchStatus = newStatus;
|
|
mParent->mTorchCond.notify_one();
|
|
return Void();
|
|
}
|
|
|
|
private:
|
|
CameraHidlTest *mParent; // Parent object
|
|
};
|
|
|
|
struct Camera1DeviceCb :
|
|
public ::android::hardware::camera::device::V1_0::ICameraDeviceCallback {
|
|
Camera1DeviceCb(CameraHidlTest *parent) : mParent(parent) {}
|
|
|
|
Return<void> notifyCallback(NotifyCallbackMsg msgType,
|
|
int32_t ext1, int32_t ext2) override;
|
|
|
|
Return<uint32_t> registerMemory(const hidl_handle& descriptor,
|
|
uint32_t bufferSize, uint32_t bufferCount) override;
|
|
|
|
Return<void> unregisterMemory(uint32_t memId) override;
|
|
|
|
Return<void> dataCallback(DataCallbackMsg msgType,
|
|
uint32_t data, uint32_t bufferIndex,
|
|
const CameraFrameMetadata& metadata) override;
|
|
|
|
Return<void> dataCallbackTimestamp(DataCallbackMsg msgType,
|
|
uint32_t data, uint32_t bufferIndex,
|
|
int64_t timestamp) override;
|
|
|
|
Return<void> handleCallbackTimestamp(DataCallbackMsg msgType,
|
|
const hidl_handle& frameData,uint32_t data,
|
|
uint32_t bufferIndex, int64_t timestamp) override;
|
|
|
|
Return<void> handleCallbackTimestampBatch(DataCallbackMsg msgType,
|
|
const ::android::hardware::hidl_vec<HandleTimestampMessage>& batch) override;
|
|
|
|
|
|
private:
|
|
CameraHidlTest *mParent; // Parent object
|
|
};
|
|
|
|
void notifyDeviceState(::android::hardware::camera::provider::V2_5::DeviceState newState);
|
|
|
|
void openCameraDevice(const std::string &name, sp<ICameraProvider> provider,
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> *device /*out*/);
|
|
void setupPreviewWindow(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
|
|
sp<BufferItemConsumer> *bufferItemConsumer /*out*/,
|
|
sp<BufferItemHander> *bufferHandler /*out*/);
|
|
void stopPreviewAndClose(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
|
|
void startPreview(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
|
|
void enableMsgType(unsigned int msgType,
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
|
|
void disableMsgType(unsigned int msgType,
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device);
|
|
void getParameters(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
|
|
CameraParameters *cameraParams /*out*/);
|
|
void setParameters(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
|
|
const CameraParameters &cameraParams);
|
|
void allocateGraphicBuffer(uint32_t width, uint32_t height, uint64_t usage,
|
|
PixelFormat format, hidl_handle *buffer_handle /*out*/);
|
|
void waitForFrameLocked(DataCallbackMsg msgFrame,
|
|
std::unique_lock<std::mutex> &l);
|
|
void openEmptyDeviceSession(const std::string &name,
|
|
sp<ICameraProvider> provider,
|
|
sp<ICameraDeviceSession> *session /*out*/,
|
|
camera_metadata_t **staticMeta /*out*/,
|
|
::android::sp<ICameraDevice> *device = nullptr/*out*/);
|
|
void castProvider(const sp<provider::V2_4::ICameraProvider> &provider,
|
|
sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/);
|
|
void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
|
|
sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
|
|
sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
|
|
sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/);
|
|
void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
|
|
sp<device::V3_5::ICameraDevice> *device3_5/*out*/);
|
|
void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
|
|
StreamConfigurationMode configMode,
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2,
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration *config3_4,
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration *config3_5,
|
|
uint32_t jpegBufferSize = 0);
|
|
|
|
void configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
|
|
sp<ICameraProvider> provider,
|
|
const AvailableStream *previewThreshold,
|
|
const std::unordered_set<std::string>& physicalIds,
|
|
sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
|
|
sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
|
|
V3_2::Stream* previewStream /*out*/,
|
|
device::V3_4::HalStreamConfiguration *halStreamConfig /*out*/,
|
|
bool *supportsPartialResults /*out*/,
|
|
uint32_t *partialResultCount /*out*/,
|
|
bool *useHalBufManager /*out*/,
|
|
sp<DeviceCb> *cb /*out*/,
|
|
uint32_t streamConfigCounter = 0,
|
|
bool allowUnsupport = false);
|
|
void configurePreviewStream(const std::string &name, int32_t deviceVersion,
|
|
sp<ICameraProvider> provider,
|
|
const AvailableStream *previewThreshold,
|
|
sp<ICameraDeviceSession> *session /*out*/,
|
|
V3_2::Stream *previewStream /*out*/,
|
|
HalStreamConfiguration *halStreamConfig /*out*/,
|
|
bool *supportsPartialResults /*out*/,
|
|
uint32_t *partialResultCount /*out*/,
|
|
bool *useHalBufManager /*out*/,
|
|
sp<DeviceCb> *cb /*out*/,
|
|
uint32_t streamConfigCounter = 0);
|
|
|
|
void verifyLogicalCameraMetadata(const std::string& cameraName,
|
|
const ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice>& device,
|
|
const CameraMetadata& chars, int deviceVersion,
|
|
const hidl_vec<hidl_string>& deviceNames);
|
|
void verifyCameraCharacteristics(Status status, const CameraMetadata& chars);
|
|
void verifyRecommendedConfigs(const CameraMetadata& metadata);
|
|
void verifyMonochromeCharacteristics(const CameraMetadata& chars, int deviceVersion);
|
|
void verifyMonochromeCameraResult(
|
|
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& metadata);
|
|
void verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
|
|
const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
|
|
bool expectedStatus, bool expectStreamCombQuery);
|
|
void verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
|
|
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata);
|
|
|
|
void verifyBuffersReturned(sp<device::V3_2::ICameraDeviceSession> session,
|
|
int deviceVerison, int32_t streamId, sp<DeviceCb> cb,
|
|
uint32_t streamConfigCounter = 0);
|
|
|
|
void verifyBuffersReturned(sp<device::V3_4::ICameraDeviceSession> session,
|
|
hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
|
|
uint32_t streamConfigCounter = 0);
|
|
|
|
void verifySessionReconfigurationQuery(sp<device::V3_5::ICameraDeviceSession> session3_5,
|
|
camera_metadata* oldSessionParams, camera_metadata* newSessionParams);
|
|
|
|
bool isDepthOnly(camera_metadata_t* staticMeta);
|
|
|
|
static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
|
|
std::vector<AvailableStream> &outputStreams,
|
|
const AvailableStream *threshold = nullptr);
|
|
static Status getJpegBufferSize(camera_metadata_t *staticMeta,
|
|
uint32_t* outBufSize);
|
|
static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
|
|
static Status isLogicalMultiCamera(const camera_metadata_t *staticMeta);
|
|
static Status getPhysicalCameraIds(const camera_metadata_t *staticMeta,
|
|
std::unordered_set<std::string> *physicalIds/*out*/);
|
|
static Status getSupportedKeys(camera_metadata_t *staticMeta,
|
|
uint32_t tagId, std::unordered_set<int32_t> *requestIDs/*out*/);
|
|
static void fillOutputStreams(camera_metadata_ro_entry_t* entry,
|
|
std::vector<AvailableStream>& outputStreams,
|
|
const AvailableStream *threshold = nullptr,
|
|
const int32_t availableConfigOutputTag = 0u);
|
|
static void constructFilteredSettings(const sp<ICameraDeviceSession>& session,
|
|
const std::unordered_set<int32_t>& availableKeys, RequestTemplate reqTemplate,
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata* defaultSettings/*out*/,
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata* filteredSettings
|
|
/*out*/);
|
|
static Status pickConstrainedModeSize(camera_metadata_t *staticMeta,
|
|
AvailableStream &hfrStream);
|
|
static Status isZSLModeAvailable(const camera_metadata_t *staticMeta);
|
|
static Status isZSLModeAvailable(const camera_metadata_t *staticMeta, ReprocessType reprocType);
|
|
static Status getZSLInputOutputMap(camera_metadata_t *staticMeta,
|
|
std::vector<AvailableZSLInputOutput> &inputOutputMap);
|
|
static Status findLargestSize(
|
|
const std::vector<AvailableStream> &streamSizes,
|
|
int32_t format, AvailableStream &result);
|
|
static Status isAutoFocusModeAvailable(
|
|
CameraParameters &cameraParams, const char *mode) ;
|
|
static Status isMonochromeCamera(const camera_metadata_t *staticMeta);
|
|
|
|
protected:
|
|
|
|
// In-flight queue for tracking completion of capture requests.
|
|
struct InFlightRequest {
|
|
// Set by notify() SHUTTER call.
|
|
nsecs_t shutterTimestamp;
|
|
|
|
bool errorCodeValid;
|
|
ErrorCode errorCode;
|
|
|
|
//Is partial result supported
|
|
bool usePartialResult;
|
|
|
|
//Partial result count expected
|
|
uint32_t numPartialResults;
|
|
|
|
// Message queue
|
|
std::shared_ptr<ResultMetadataQueue> resultQueue;
|
|
|
|
// Set by process_capture_result call with valid metadata
|
|
bool haveResultMetadata;
|
|
|
|
// Decremented by calls to process_capture_result with valid output
|
|
// and input buffers
|
|
ssize_t numBuffersLeft;
|
|
|
|
// A 64bit integer to index the frame number associated with this result.
|
|
int64_t frameNumber;
|
|
|
|
// The partial result count (index) for this capture result.
|
|
int32_t partialResultCount;
|
|
|
|
// For buffer drop errors, the stream ID for the stream that lost a buffer.
|
|
// Otherwise -1.
|
|
int32_t errorStreamId;
|
|
|
|
// If this request has any input buffer
|
|
bool hasInputBuffer;
|
|
|
|
// Result metadata
|
|
::android::hardware::camera::common::V1_0::helper::CameraMetadata collectedResult;
|
|
|
|
// Buffers are added by process_capture_result when output buffers
|
|
// return from HAL but framework.
|
|
::android::Vector<StreamBuffer> resultOutputBuffers;
|
|
|
|
InFlightRequest() :
|
|
shutterTimestamp(0),
|
|
errorCodeValid(false),
|
|
errorCode(ErrorCode::ERROR_BUFFER),
|
|
usePartialResult(false),
|
|
numPartialResults(0),
|
|
resultQueue(nullptr),
|
|
haveResultMetadata(false),
|
|
numBuffersLeft(0),
|
|
frameNumber(0),
|
|
partialResultCount(0),
|
|
errorStreamId(-1),
|
|
hasInputBuffer(false) {}
|
|
|
|
InFlightRequest(ssize_t numBuffers, bool hasInput,
|
|
bool partialResults, uint32_t partialCount,
|
|
std::shared_ptr<ResultMetadataQueue> queue = nullptr) :
|
|
shutterTimestamp(0),
|
|
errorCodeValid(false),
|
|
errorCode(ErrorCode::ERROR_BUFFER),
|
|
usePartialResult(partialResults),
|
|
numPartialResults(partialCount),
|
|
resultQueue(queue),
|
|
haveResultMetadata(false),
|
|
numBuffersLeft(numBuffers),
|
|
frameNumber(0),
|
|
partialResultCount(0),
|
|
errorStreamId(-1),
|
|
hasInputBuffer(hasInput) {}
|
|
};
|
|
|
|
// Map from frame number to the in-flight request state
|
|
typedef ::android::KeyedVector<uint32_t, InFlightRequest*> InFlightMap;
|
|
|
|
std::mutex mLock; // Synchronize access to member variables
|
|
std::condition_variable mResultCondition; // Condition variable for incoming results
|
|
InFlightMap mInflightMap; // Map of all inflight requests
|
|
|
|
DataCallbackMsg mDataMessageTypeReceived; // Most recent message type received through data callbacks
|
|
uint32_t mVideoBufferIndex; // Buffer index of the most recent video buffer
|
|
uint32_t mVideoData; // Buffer data of the most recent video buffer
|
|
hidl_handle mVideoNativeHandle; // Most recent video buffer native handle
|
|
NotifyCallbackMsg mNotifyMessage; // Current notification message
|
|
|
|
std::mutex mTorchLock; // Synchronize access to torch status
|
|
std::condition_variable mTorchCond; // Condition variable for torch status
|
|
TorchModeStatus mTorchStatus; // Current torch status
|
|
|
|
// Holds camera registered buffers
|
|
std::unordered_map<uint32_t, sp<::android::MemoryHeapBase> > mMemoryPool;
|
|
|
|
// Camera provider service
|
|
sp<ICameraProvider> mProvider;
|
|
sp<::android::hardware::camera::provider::V2_5::ICameraProvider> mProvider2_5;
|
|
|
|
// Camera provider type.
|
|
std::string mProviderType;
|
|
};
|
|
|
|
Return<void> CameraHidlTest::Camera1DeviceCb::notifyCallback(
|
|
NotifyCallbackMsg msgType, int32_t ext1 __unused,
|
|
int32_t ext2 __unused) {
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
mParent->mNotifyMessage = msgType;
|
|
mParent->mResultCondition.notify_one();
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<uint32_t> CameraHidlTest::Camera1DeviceCb::registerMemory(
|
|
const hidl_handle& descriptor, uint32_t bufferSize,
|
|
uint32_t bufferCount) {
|
|
if (descriptor->numFds != 1) {
|
|
ADD_FAILURE() << "camera memory descriptor has"
|
|
" numFds " << descriptor->numFds << " (expect 1)" ;
|
|
return 0;
|
|
}
|
|
if (descriptor->data[0] < 0) {
|
|
ADD_FAILURE() << "camera memory descriptor has"
|
|
" FD " << descriptor->data[0] << " (expect >= 0)";
|
|
return 0;
|
|
}
|
|
|
|
sp<::android::MemoryHeapBase> pool = new ::android::MemoryHeapBase(
|
|
descriptor->data[0], bufferSize*bufferCount, 0, 0);
|
|
mParent->mMemoryPool.emplace(pool->getHeapID(), pool);
|
|
|
|
return pool->getHeapID();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::Camera1DeviceCb::unregisterMemory(uint32_t memId) {
|
|
if (mParent->mMemoryPool.count(memId) == 0) {
|
|
ALOGE("%s: memory pool ID %d not found", __FUNCTION__, memId);
|
|
ADD_FAILURE();
|
|
return Void();
|
|
}
|
|
|
|
mParent->mMemoryPool.erase(memId);
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::Camera1DeviceCb::dataCallback(
|
|
DataCallbackMsg msgType __unused, uint32_t data __unused,
|
|
uint32_t bufferIndex __unused,
|
|
const CameraFrameMetadata& metadata __unused) {
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
mParent->mDataMessageTypeReceived = msgType;
|
|
mParent->mResultCondition.notify_one();
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::Camera1DeviceCb::dataCallbackTimestamp(
|
|
DataCallbackMsg msgType, uint32_t data,
|
|
uint32_t bufferIndex, int64_t timestamp __unused) {
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
mParent->mDataMessageTypeReceived = msgType;
|
|
mParent->mVideoBufferIndex = bufferIndex;
|
|
if (mParent->mMemoryPool.count(data) == 0) {
|
|
ADD_FAILURE() << "memory pool ID " << data << "not found";
|
|
}
|
|
mParent->mVideoData = data;
|
|
mParent->mResultCondition.notify_one();
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::Camera1DeviceCb::handleCallbackTimestamp(
|
|
DataCallbackMsg msgType, const hidl_handle& frameData,
|
|
uint32_t data __unused, uint32_t bufferIndex,
|
|
int64_t timestamp __unused) {
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
mParent->mDataMessageTypeReceived = msgType;
|
|
mParent->mVideoBufferIndex = bufferIndex;
|
|
if (mParent->mMemoryPool.count(data) == 0) {
|
|
ADD_FAILURE() << "memory pool ID " << data << " not found";
|
|
}
|
|
mParent->mVideoData = data;
|
|
mParent->mVideoNativeHandle = frameData;
|
|
mParent->mResultCondition.notify_one();
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::Camera1DeviceCb::handleCallbackTimestampBatch(
|
|
DataCallbackMsg msgType,
|
|
const hidl_vec<HandleTimestampMessage>& batch) {
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
for (auto& msg : batch) {
|
|
mParent->mDataMessageTypeReceived = msgType;
|
|
mParent->mVideoBufferIndex = msg.bufferIndex;
|
|
if (mParent->mMemoryPool.count(msg.data) == 0) {
|
|
ADD_FAILURE() << "memory pool ID " << msg.data << " not found";
|
|
}
|
|
mParent->mVideoData = msg.data;
|
|
mParent->mVideoNativeHandle = msg.frameData;
|
|
mParent->mResultCondition.notify_one();
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::DeviceCb::processCaptureResult_3_4(
|
|
const hidl_vec<V3_4::CaptureResult>& results) {
|
|
|
|
if (nullptr == mParent) {
|
|
return Void();
|
|
}
|
|
|
|
bool notify = false;
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
for (size_t i = 0 ; i < results.size(); i++) {
|
|
notify = processCaptureResultLocked(results[i].v3_2, results[i].physicalCameraMetadata);
|
|
}
|
|
|
|
l.unlock();
|
|
if (notify) {
|
|
mParent->mResultCondition.notify_one();
|
|
}
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::DeviceCb::processCaptureResult(
|
|
const hidl_vec<CaptureResult>& results) {
|
|
if (nullptr == mParent) {
|
|
return Void();
|
|
}
|
|
|
|
bool notify = false;
|
|
std::unique_lock<std::mutex> l(mParent->mLock);
|
|
::android::hardware::hidl_vec<PhysicalCameraMetadata> noPhysMetadata;
|
|
for (size_t i = 0 ; i < results.size(); i++) {
|
|
notify = processCaptureResultLocked(results[i], noPhysMetadata);
|
|
}
|
|
|
|
l.unlock();
|
|
if (notify) {
|
|
mParent->mResultCondition.notify_one();
|
|
}
|
|
|
|
return Void();
|
|
}
|
|
|
|
bool CameraHidlTest::DeviceCb::processCaptureResultLocked(const CaptureResult& results,
|
|
hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata) {
|
|
bool notify = false;
|
|
uint32_t frameNumber = results.frameNumber;
|
|
|
|
if ((results.result.size() == 0) &&
|
|
(results.outputBuffers.size() == 0) &&
|
|
(results.inputBuffer.buffer == nullptr) &&
|
|
(results.fmqResultSize == 0)) {
|
|
ALOGE("%s: No result data provided by HAL for frame %d result count: %d",
|
|
__func__, frameNumber, (int) results.fmqResultSize);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
|
|
ssize_t idx = mParent->mInflightMap.indexOfKey(frameNumber);
|
|
if (::android::NAME_NOT_FOUND == idx) {
|
|
ALOGE("%s: Unexpected frame number! received: %u",
|
|
__func__, frameNumber);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
|
|
bool isPartialResult = false;
|
|
bool hasInputBufferInRequest = false;
|
|
InFlightRequest *request = mParent->mInflightMap.editValueAt(idx);
|
|
::android::hardware::camera::device::V3_2::CameraMetadata resultMetadata;
|
|
size_t resultSize = 0;
|
|
if (results.fmqResultSize > 0) {
|
|
resultMetadata.resize(results.fmqResultSize);
|
|
if (request->resultQueue == nullptr) {
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
if (!request->resultQueue->read(resultMetadata.data(),
|
|
results.fmqResultSize)) {
|
|
ALOGE("%s: Frame %d: Cannot read camera metadata from fmq,"
|
|
"size = %" PRIu64, __func__, frameNumber,
|
|
results.fmqResultSize);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
|
|
std::vector<::android::hardware::camera::device::V3_2::CameraMetadata> physResultMetadata;
|
|
physResultMetadata.resize(physicalCameraMetadata.size());
|
|
for (size_t i = 0; i < physicalCameraMetadata.size(); i++) {
|
|
physResultMetadata[i].resize(physicalCameraMetadata[i].fmqMetadataSize);
|
|
if (!request->resultQueue->read(physResultMetadata[i].data(),
|
|
physicalCameraMetadata[i].fmqMetadataSize)) {
|
|
ALOGE("%s: Frame %d: Cannot read physical camera metadata from fmq,"
|
|
"size = %" PRIu64, __func__, frameNumber,
|
|
physicalCameraMetadata[i].fmqMetadataSize);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
}
|
|
resultSize = resultMetadata.size();
|
|
} else if (results.result.size() > 0) {
|
|
resultMetadata.setToExternal(const_cast<uint8_t *>(
|
|
results.result.data()), results.result.size());
|
|
resultSize = resultMetadata.size();
|
|
}
|
|
|
|
if (!request->usePartialResult && (resultSize > 0) &&
|
|
(results.partialResult != 1)) {
|
|
ALOGE("%s: Result is malformed for frame %d: partial_result %u "
|
|
"must be 1 if partial result is not supported", __func__,
|
|
frameNumber, results.partialResult);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
|
|
if (results.partialResult != 0) {
|
|
request->partialResultCount = results.partialResult;
|
|
}
|
|
|
|
// Check if this result carries only partial metadata
|
|
if (request->usePartialResult && (resultSize > 0)) {
|
|
if ((results.partialResult > request->numPartialResults) ||
|
|
(results.partialResult < 1)) {
|
|
ALOGE("%s: Result is malformed for frame %d: partial_result %u"
|
|
" must be in the range of [1, %d] when metadata is "
|
|
"included in the result", __func__, frameNumber,
|
|
results.partialResult, request->numPartialResults);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
request->collectedResult.append(
|
|
reinterpret_cast<const camera_metadata_t*>(
|
|
resultMetadata.data()));
|
|
|
|
isPartialResult =
|
|
(results.partialResult < request->numPartialResults);
|
|
} else if (resultSize > 0) {
|
|
request->collectedResult.append(reinterpret_cast<const camera_metadata_t*>(
|
|
resultMetadata.data()));
|
|
isPartialResult = false;
|
|
}
|
|
|
|
hasInputBufferInRequest = request->hasInputBuffer;
|
|
|
|
// Did we get the (final) result metadata for this capture?
|
|
if ((resultSize > 0) && !isPartialResult) {
|
|
if (request->haveResultMetadata) {
|
|
ALOGE("%s: Called multiple times with metadata for frame %d",
|
|
__func__, frameNumber);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
request->haveResultMetadata = true;
|
|
request->collectedResult.sort();
|
|
|
|
// Verify final result metadata
|
|
bool isAtLeast_3_5 = mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_5;
|
|
if (isAtLeast_3_5) {
|
|
auto staticMetadataBuffer = mStaticMetadata.getAndLock();
|
|
bool isMonochrome = Status::OK ==
|
|
CameraHidlTest::isMonochromeCamera(staticMetadataBuffer);
|
|
if (isMonochrome) {
|
|
mParent->verifyMonochromeCameraResult(request->collectedResult);
|
|
}
|
|
|
|
// Verify logical camera result metadata
|
|
bool isLogicalCamera =
|
|
Status::OK == CameraHidlTest::isLogicalMultiCamera(staticMetadataBuffer);
|
|
if (isLogicalCamera) {
|
|
mParent->verifyLogicalCameraResult(staticMetadataBuffer, request->collectedResult);
|
|
}
|
|
mStaticMetadata.unlock(staticMetadataBuffer);
|
|
}
|
|
}
|
|
|
|
uint32_t numBuffersReturned = results.outputBuffers.size();
|
|
if (results.inputBuffer.buffer != nullptr) {
|
|
if (hasInputBufferInRequest) {
|
|
numBuffersReturned += 1;
|
|
} else {
|
|
ALOGW("%s: Input buffer should be NULL if there is no input"
|
|
" buffer sent in the request", __func__);
|
|
}
|
|
}
|
|
request->numBuffersLeft -= numBuffersReturned;
|
|
if (request->numBuffersLeft < 0) {
|
|
ALOGE("%s: Too many buffers returned for frame %d", __func__,
|
|
frameNumber);
|
|
ADD_FAILURE();
|
|
return notify;
|
|
}
|
|
|
|
request->resultOutputBuffers.appendArray(results.outputBuffers.data(),
|
|
results.outputBuffers.size());
|
|
// If shutter event is received notify the pending threads.
|
|
if (request->shutterTimestamp != 0) {
|
|
notify = true;
|
|
}
|
|
|
|
if (mUseHalBufManager) {
|
|
// Don't return buffers of bufId 0 (empty buffer)
|
|
std::vector<StreamBuffer> buffers;
|
|
for (const auto& sb : results.outputBuffers) {
|
|
if (sb.bufferId != 0) {
|
|
buffers.push_back(sb);
|
|
}
|
|
}
|
|
returnStreamBuffers(buffers);
|
|
}
|
|
return notify;
|
|
}
|
|
|
|
void CameraHidlTest::DeviceCb::setCurrentStreamConfig(
|
|
const hidl_vec<V3_2::Stream>& streams, const hidl_vec<V3_2::HalStream>& halStreams) {
|
|
ASSERT_EQ(streams.size(), halStreams.size());
|
|
ASSERT_NE(streams.size(), 0);
|
|
for (size_t i = 0; i < streams.size(); i++) {
|
|
ASSERT_EQ(streams[i].id, halStreams[i].id);
|
|
}
|
|
std::lock_guard<std::mutex> l(mLock);
|
|
mUseHalBufManager = true;
|
|
mStreams = streams;
|
|
mHalStreams = halStreams;
|
|
mOutstandingBufferIds.clear();
|
|
for (size_t i = 0; i < streams.size(); i++) {
|
|
mOutstandingBufferIds.emplace_back();
|
|
}
|
|
}
|
|
|
|
bool CameraHidlTest::DeviceCb::hasOutstandingBuffersLocked() {
|
|
if (!mUseHalBufManager) {
|
|
return false;
|
|
}
|
|
for (const auto& outstandingBuffers : mOutstandingBufferIds) {
|
|
if (!outstandingBuffers.empty()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CameraHidlTest::DeviceCb::waitForBuffersReturned() {
|
|
std::unique_lock<std::mutex> lk(mLock);
|
|
if (hasOutstandingBuffersLocked()) {
|
|
auto timeout = std::chrono::seconds(kBufferReturnTimeoutSec);
|
|
auto st = mFlushedCondition.wait_for(lk, timeout);
|
|
ASSERT_NE(std::cv_status::timeout, st);
|
|
}
|
|
}
|
|
|
|
Return<void> CameraHidlTest::DeviceCb::notify(
|
|
const hidl_vec<NotifyMsg>& messages) {
|
|
std::lock_guard<std::mutex> l(mParent->mLock);
|
|
|
|
for (size_t i = 0; i < messages.size(); i++) {
|
|
ssize_t idx = mParent->mInflightMap.indexOfKey(
|
|
messages[i].msg.shutter.frameNumber);
|
|
if (::android::NAME_NOT_FOUND == idx) {
|
|
ALOGE("%s: Unexpected frame number! received: %u",
|
|
__func__, messages[i].msg.shutter.frameNumber);
|
|
ADD_FAILURE();
|
|
break;
|
|
}
|
|
InFlightRequest *r = mParent->mInflightMap.editValueAt(idx);
|
|
|
|
switch(messages[i].type) {
|
|
case MsgType::ERROR:
|
|
if (ErrorCode::ERROR_DEVICE == messages[i].msg.error.errorCode) {
|
|
ALOGE("%s: Camera reported serious device error",
|
|
__func__);
|
|
ADD_FAILURE();
|
|
} else {
|
|
r->errorCodeValid = true;
|
|
r->errorCode = messages[i].msg.error.errorCode;
|
|
r->errorStreamId = messages[i].msg.error.errorStreamId;
|
|
}
|
|
break;
|
|
case MsgType::SHUTTER:
|
|
r->shutterTimestamp = messages[i].msg.shutter.timestamp;
|
|
break;
|
|
default:
|
|
ALOGE("%s: Unsupported notify message %d", __func__,
|
|
messages[i].type);
|
|
ADD_FAILURE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
mParent->mResultCondition.notify_one();
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::DeviceCb::requestStreamBuffers(
|
|
const hidl_vec<V3_5::BufferRequest>& bufReqs,
|
|
requestStreamBuffers_cb _hidl_cb) {
|
|
using V3_5::BufferRequestStatus;
|
|
using V3_5::StreamBufferRet;
|
|
using V3_5::StreamBufferRequestError;
|
|
hidl_vec<StreamBufferRet> bufRets;
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
|
|
if (!mUseHalBufManager) {
|
|
ALOGE("%s: Camera does not support HAL buffer management", __FUNCTION__);
|
|
ADD_FAILURE();
|
|
_hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
|
|
return Void();
|
|
}
|
|
|
|
if (bufReqs.size() > mStreams.size()) {
|
|
ALOGE("%s: illegal buffer request: too many requests!", __FUNCTION__);
|
|
ADD_FAILURE();
|
|
_hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
|
|
return Void();
|
|
}
|
|
|
|
std::vector<int32_t> indexes(bufReqs.size());
|
|
for (size_t i = 0; i < bufReqs.size(); i++) {
|
|
bool found = false;
|
|
for (size_t idx = 0; idx < mStreams.size(); idx++) {
|
|
if (bufReqs[i].streamId == mStreams[idx].id) {
|
|
found = true;
|
|
indexes[i] = idx;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
ALOGE("%s: illegal buffer request: unknown streamId %d!",
|
|
__FUNCTION__, bufReqs[i].streamId);
|
|
ADD_FAILURE();
|
|
_hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
|
|
return Void();
|
|
}
|
|
}
|
|
|
|
bool allStreamOk = true;
|
|
bool atLeastOneStreamOk = false;
|
|
bufRets.resize(bufReqs.size());
|
|
for (size_t i = 0; i < bufReqs.size(); i++) {
|
|
int32_t idx = indexes[i];
|
|
const auto& stream = mStreams[idx];
|
|
const auto& halStream = mHalStreams[idx];
|
|
const V3_5::BufferRequest& bufReq = bufReqs[i];
|
|
if (mOutstandingBufferIds[idx].size() + bufReq.numBuffersRequested > halStream.maxBuffers) {
|
|
bufRets[i].streamId = stream.id;
|
|
bufRets[i].val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED);
|
|
allStreamOk = false;
|
|
continue;
|
|
}
|
|
|
|
hidl_vec<StreamBuffer> tmpRetBuffers(bufReq.numBuffersRequested);
|
|
for (size_t j = 0; j < bufReq.numBuffersRequested; j++) {
|
|
hidl_handle buffer_handle;
|
|
mParent->allocateGraphicBuffer(stream.width, stream.height,
|
|
android_convertGralloc1To0Usage(
|
|
halStream.producerUsage, halStream.consumerUsage),
|
|
halStream.overrideFormat, &buffer_handle);
|
|
|
|
tmpRetBuffers[j] = {stream.id, mNextBufferId, buffer_handle, BufferStatus::OK,
|
|
nullptr, nullptr};
|
|
mOutstandingBufferIds[idx].insert(std::make_pair(mNextBufferId++, buffer_handle));
|
|
}
|
|
atLeastOneStreamOk = true;
|
|
bufRets[i].streamId = stream.id;
|
|
bufRets[i].val.buffers(std::move(tmpRetBuffers));
|
|
}
|
|
|
|
if (allStreamOk) {
|
|
_hidl_cb(BufferRequestStatus::OK, bufRets);
|
|
} else if (atLeastOneStreamOk) {
|
|
_hidl_cb(BufferRequestStatus::FAILED_PARTIAL, bufRets);
|
|
} else {
|
|
_hidl_cb(BufferRequestStatus::FAILED_UNKNOWN, bufRets);
|
|
}
|
|
|
|
if (!hasOutstandingBuffersLocked()) {
|
|
l.unlock();
|
|
mFlushedCondition.notify_one();
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
Return<void> CameraHidlTest::DeviceCb::returnStreamBuffers(
|
|
const hidl_vec<StreamBuffer>& buffers) {
|
|
if (!mUseHalBufManager) {
|
|
ALOGE("%s: Camera does not support HAL buffer management", __FUNCTION__);
|
|
ADD_FAILURE();
|
|
}
|
|
|
|
std::lock_guard<std::mutex> l(mLock);
|
|
for (const auto& buf : buffers) {
|
|
bool found = false;
|
|
for (size_t idx = 0; idx < mOutstandingBufferIds.size(); idx++) {
|
|
if (mStreams[idx].id == buf.streamId &&
|
|
mOutstandingBufferIds[idx].count(buf.bufferId) == 1) {
|
|
mOutstandingBufferIds[idx].erase(buf.bufferId);
|
|
// TODO: check do we need to close/delete native handle or assume we have enough
|
|
// memory to run till the test finish? since we do not capture much requests (and
|
|
// most of time one buffer is sufficient)
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
continue;
|
|
}
|
|
ALOGE("%s: unknown buffer ID %" PRIu64, __FUNCTION__, buf.bufferId);
|
|
ADD_FAILURE();
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
hidl_vec<hidl_string> CameraHidlTest::getCameraDeviceNames(sp<ICameraProvider> provider) {
|
|
std::vector<std::string> cameraDeviceNames;
|
|
Return<void> ret;
|
|
ret = provider->getCameraIdList(
|
|
[&](auto status, const auto& idList) {
|
|
ALOGI("getCameraIdList returns status:%d", (int)status);
|
|
for (size_t i = 0; i < idList.size(); i++) {
|
|
ALOGI("Camera Id[%zu] is %s", i, idList[i].c_str());
|
|
}
|
|
ASSERT_EQ(Status::OK, status);
|
|
for (const auto& id : idList) {
|
|
cameraDeviceNames.push_back(id);
|
|
}
|
|
});
|
|
if (!ret.isOk()) {
|
|
ADD_FAILURE();
|
|
}
|
|
|
|
// External camera devices are reported through cameraDeviceStatusChange
|
|
struct ProviderCb : public ICameraProviderCallback {
|
|
virtual Return<void> cameraDeviceStatusChange(
|
|
const hidl_string& devName,
|
|
CameraDeviceStatus newStatus) override {
|
|
ALOGI("camera device status callback name %s, status %d",
|
|
devName.c_str(), (int) newStatus);
|
|
if (newStatus == CameraDeviceStatus::PRESENT) {
|
|
externalCameraDeviceNames.push_back(devName);
|
|
|
|
}
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> torchModeStatusChange(
|
|
const hidl_string&, TorchModeStatus) override {
|
|
return Void();
|
|
}
|
|
|
|
std::vector<std::string> externalCameraDeviceNames;
|
|
};
|
|
sp<ProviderCb> cb = new ProviderCb;
|
|
auto status = mProvider->setCallback(cb);
|
|
|
|
for (const auto& devName : cb->externalCameraDeviceNames) {
|
|
if (cameraDeviceNames.end() == std::find(
|
|
cameraDeviceNames.begin(), cameraDeviceNames.end(), devName)) {
|
|
cameraDeviceNames.push_back(devName);
|
|
}
|
|
}
|
|
|
|
hidl_vec<hidl_string> retList(cameraDeviceNames.size());
|
|
for (size_t i = 0; i < cameraDeviceNames.size(); i++) {
|
|
retList[i] = cameraDeviceNames[i];
|
|
}
|
|
return retList;
|
|
}
|
|
|
|
// Test devices with first_api_level >= P does not advertise device@1.0
|
|
TEST_F(CameraHidlTest, noHal1AfterP) {
|
|
constexpr int32_t HAL1_PHASE_OUT_API_LEVEL = 28;
|
|
int32_t firstApiLevel = 0;
|
|
getFirstApiLevel(&firstApiLevel);
|
|
|
|
// all devices with first API level == 28 and <= 1GB of RAM must set low_ram
|
|
// and thus be allowed to continue using HAL1
|
|
if ((firstApiLevel == HAL1_PHASE_OUT_API_LEVEL) &&
|
|
(property_get_bool("ro.config.low_ram", /*default*/ false))) {
|
|
ALOGI("Hal1 allowed for low ram device");
|
|
return;
|
|
}
|
|
|
|
if (firstApiLevel >= HAL1_PHASE_OUT_API_LEVEL) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
ASSERT_NE(deviceVersion, 0); // Must be a valid device version
|
|
ASSERT_NE(deviceVersion, CAMERA_DEVICE_API_VERSION_1_0); // Must not be device@1.0
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test if ICameraProvider::isTorchModeSupported returns Status::OK
|
|
// Also if first_api_level >= Q torch API must be supported.
|
|
TEST_F(CameraHidlTest, isTorchModeSupported) {
|
|
constexpr int32_t API_LEVEL_Q = 29;
|
|
int32_t firstApiLevel = 0;
|
|
getFirstApiLevel(&firstApiLevel);
|
|
|
|
Return<void> ret;
|
|
ret = mProvider->isSetTorchModeSupported([&](auto status, bool support) {
|
|
ALOGI("isSetTorchModeSupported returns status:%d supported:%d", (int)status, support);
|
|
ASSERT_EQ(Status::OK, status);
|
|
if (firstApiLevel >= API_LEVEL_Q) {
|
|
ASSERT_EQ(true, support);
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
// TODO: consider removing this test if getCameraDeviceNames() has the same coverage
|
|
TEST_F(CameraHidlTest, getCameraIdList) {
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraIdList([&](auto status, const auto& idList) {
|
|
ALOGI("getCameraIdList returns status:%d", (int)status);
|
|
for (size_t i = 0; i < idList.size(); i++) {
|
|
ALOGI("Camera Id[%zu] is %s", i, idList[i].c_str());
|
|
}
|
|
ASSERT_EQ(Status::OK, status);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
// Test if ICameraProvider::getVendorTags returns Status::OK
|
|
TEST_F(CameraHidlTest, getVendorTags) {
|
|
Return<void> ret;
|
|
ret = mProvider->getVendorTags([&](auto status, const auto& vendorTagSecs) {
|
|
ALOGI("getVendorTags returns status:%d numSections %zu", (int)status, vendorTagSecs.size());
|
|
for (size_t i = 0; i < vendorTagSecs.size(); i++) {
|
|
ALOGI("Vendor tag section %zu name %s", i, vendorTagSecs[i].sectionName.c_str());
|
|
for (size_t j = 0; j < vendorTagSecs[i].tags.size(); j++) {
|
|
const auto& tag = vendorTagSecs[i].tags[j];
|
|
ALOGI("Vendor tag id %u name %s type %d", tag.tagId, tag.tagName.c_str(),
|
|
(int)tag.tagType);
|
|
}
|
|
}
|
|
ASSERT_EQ(Status::OK, status);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
// Test if ICameraProvider::setCallback returns Status::OK
|
|
TEST_F(CameraHidlTest, setCallback) {
|
|
struct ProviderCb : public ICameraProviderCallback {
|
|
virtual Return<void> cameraDeviceStatusChange(
|
|
const hidl_string& cameraDeviceName,
|
|
CameraDeviceStatus newStatus) override {
|
|
ALOGI("camera device status callback name %s, status %d",
|
|
cameraDeviceName.c_str(), (int) newStatus);
|
|
return Void();
|
|
}
|
|
|
|
virtual Return<void> torchModeStatusChange(
|
|
const hidl_string& cameraDeviceName,
|
|
TorchModeStatus newStatus) override {
|
|
ALOGI("Torch mode status callback name %s, status %d",
|
|
cameraDeviceName.c_str(), (int) newStatus);
|
|
return Void();
|
|
}
|
|
};
|
|
sp<ProviderCb> cb = new ProviderCb;
|
|
auto status = mProvider->setCallback(cb);
|
|
ASSERT_TRUE(status.isOk());
|
|
ASSERT_EQ(Status::OK, status);
|
|
status = mProvider->setCallback(nullptr);
|
|
ASSERT_TRUE(status.isOk());
|
|
ASSERT_EQ(Status::OK, status);
|
|
}
|
|
|
|
// Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device
|
|
TEST_F(CameraHidlTest, getCameraDeviceInterface) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device3_x) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device3_x, nullptr);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V1_x(
|
|
name, [&](auto status, const auto& device1) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device1, nullptr);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that the device resource cost can be retrieved and the values are
|
|
// sane.
|
|
TEST_F(CameraHidlTest, getResourceCost) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
|
|
ALOGI("getResourceCost: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = device3_x->getResourceCost([&](auto status, const auto& resourceCost) {
|
|
ALOGI("getResourceCost returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ALOGI(" Resource cost is %d", resourceCost.resourceCost);
|
|
ASSERT_LE(resourceCost.resourceCost, 100u);
|
|
for (const auto& name : resourceCost.conflictingDevices) {
|
|
ALOGI(" Conflicting device: %s", name.c_str());
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
ALOGI("getResourceCost: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V1_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device1 = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = device1->getResourceCost([&](auto status, const auto& resourceCost) {
|
|
ALOGI("getResourceCost returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ALOGI(" Resource cost is %d", resourceCost.resourceCost);
|
|
ASSERT_LE(resourceCost.resourceCost, 100u);
|
|
for (const auto& name : resourceCost.conflictingDevices) {
|
|
ALOGI(" Conflicting device: %s", name.c_str());
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that the static camera info can be retrieved
|
|
// successfully.
|
|
TEST_F(CameraHidlTest, getCameraInfo) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V1_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device1 = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = device1->getCameraInfo([&](auto status, const auto& info) {
|
|
ALOGI("getCameraInfo returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
switch (info.orientation) {
|
|
case 0:
|
|
case 90:
|
|
case 180:
|
|
case 270:
|
|
// Expected cases
|
|
ALOGI("camera orientation: %d", info.orientation);
|
|
break;
|
|
default:
|
|
FAIL() << "Unexpected camera orientation:" << info.orientation;
|
|
}
|
|
switch (info.facing) {
|
|
case CameraFacing::BACK:
|
|
case CameraFacing::FRONT:
|
|
case CameraFacing::EXTERNAL:
|
|
// Expected cases
|
|
ALOGI("camera facing: %d", info.facing);
|
|
break;
|
|
default:
|
|
FAIL() << "Unexpected camera facing:" << static_cast<uint32_t>(info.facing);
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether preview window can be configured
|
|
TEST_F(CameraHidlTest, setPreviewWindow) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
|
|
Return<void> ret;
|
|
ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that setting preview window fails in case device is not open
|
|
TEST_F(CameraHidlTest, setPreviewWindowInvalid) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V1_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device1 = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
Return<Status> returnStatus = device1->setPreviewWindow(nullptr);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OPERATION_NOT_SUPPORTED, returnStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start and stop preview checking whether it gets enabled in between.
|
|
TEST_F(CameraHidlTest, startStopPreview) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
|
|
startPreview(device1);
|
|
|
|
Return<bool> returnBoolStatus = device1->previewEnabled();
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_TRUE(returnBoolStatus);
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start preview without active preview window. Preview should start as soon
|
|
// as a valid active window gets configured.
|
|
TEST_F(CameraHidlTest, startStopPreviewDelayed) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
Return<Status> returnStatus = device1->setPreviewWindow(nullptr);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
startPreview(device1);
|
|
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
|
|
// Preview should get enabled now
|
|
Return<bool> returnBoolStatus = device1->previewEnabled();
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_TRUE(returnBoolStatus);
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that image capture behaves as expected along with preview callbacks.
|
|
TEST_F(CameraHidlTest, takePicture) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
|
|
}
|
|
|
|
enableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
|
|
startPreview(device1);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
waitForFrameLocked(DataCallbackMsg::PREVIEW_FRAME, l);
|
|
}
|
|
|
|
disableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
|
|
enableMsgType((unsigned int)DataCallbackMsg::COMPRESSED_IMAGE, device1);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
|
|
}
|
|
|
|
Return<Status> returnStatus = device1->takePicture();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
waitForFrameLocked(DataCallbackMsg::COMPRESSED_IMAGE, l);
|
|
}
|
|
|
|
disableMsgType((unsigned int)DataCallbackMsg::COMPRESSED_IMAGE, device1);
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Image capture should fail in case preview didn't get enabled first.
|
|
TEST_F(CameraHidlTest, takePictureFail) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
Return<Status> returnStatus = device1->takePicture();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_NE(Status::OK, returnStatus);
|
|
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that image capture can be cancelled.
|
|
TEST_F(CameraHidlTest, cancelPicture) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
startPreview(device1);
|
|
|
|
Return<Status> returnStatus = device1->takePicture();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
returnStatus = device1->cancelPicture();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Image capture cancel is a no-op when image capture is not running.
|
|
TEST_F(CameraHidlTest, cancelPictureNOP) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
startPreview(device1);
|
|
|
|
Return<Status> returnStatus = device1->cancelPicture();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test basic video recording.
|
|
TEST_F(CameraHidlTest, startStopRecording) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
|
|
}
|
|
|
|
enableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
|
|
startPreview(device1);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
waitForFrameLocked(DataCallbackMsg::PREVIEW_FRAME, l);
|
|
mDataMessageTypeReceived = DataCallbackMsg::RAW_IMAGE_NOTIFY;
|
|
mVideoBufferIndex = UINT32_MAX;
|
|
}
|
|
|
|
disableMsgType((unsigned int)DataCallbackMsg::PREVIEW_FRAME, device1);
|
|
|
|
bool videoMetaEnabled = false;
|
|
Return<Status> returnStatus = device1->storeMetaDataInBuffers(true);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
// It is allowed for devices to not support this feature
|
|
ASSERT_TRUE((Status::OK == returnStatus) ||
|
|
(Status::OPERATION_NOT_SUPPORTED == returnStatus));
|
|
if (Status::OK == returnStatus) {
|
|
videoMetaEnabled = true;
|
|
}
|
|
|
|
enableMsgType((unsigned int)DataCallbackMsg::VIDEO_FRAME, device1);
|
|
Return<bool> returnBoolStatus = device1->recordingEnabled();
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_FALSE(returnBoolStatus);
|
|
|
|
returnStatus = device1->startRecording();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
waitForFrameLocked(DataCallbackMsg::VIDEO_FRAME, l);
|
|
ASSERT_NE(UINT32_MAX, mVideoBufferIndex);
|
|
disableMsgType((unsigned int)DataCallbackMsg::VIDEO_FRAME, device1);
|
|
}
|
|
|
|
returnBoolStatus = device1->recordingEnabled();
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_TRUE(returnBoolStatus);
|
|
|
|
Return<void> ret;
|
|
if (videoMetaEnabled) {
|
|
ret = device1->releaseRecordingFrameHandle(mVideoData, mVideoBufferIndex,
|
|
mVideoNativeHandle);
|
|
ASSERT_TRUE(ret.isOk());
|
|
} else {
|
|
ret = device1->releaseRecordingFrame(mVideoData, mVideoBufferIndex);
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
ret = device1->stopRecording();
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// It shouldn't be possible to start recording without enabling preview first.
|
|
TEST_F(CameraHidlTest, startRecordingFail) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
Return<bool> returnBoolStatus = device1->recordingEnabled();
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_FALSE(returnBoolStatus);
|
|
|
|
Return<Status> returnStatus = device1->startRecording();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_NE(Status::OK, returnStatus);
|
|
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check autofocus support if available.
|
|
TEST_F(CameraHidlTest, autoFocus) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<const char*> focusModes = {CameraParameters::FOCUS_MODE_AUTO,
|
|
CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE,
|
|
CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO};
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
CameraParameters cameraParams;
|
|
getParameters(device1, &cameraParams /*out*/);
|
|
|
|
if (Status::OK !=
|
|
isAutoFocusModeAvailable(cameraParams, CameraParameters::FOCUS_MODE_AUTO)) {
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
startPreview(device1);
|
|
enableMsgType((unsigned int)NotifyCallbackMsg::FOCUS, device1);
|
|
|
|
for (auto& iter : focusModes) {
|
|
if (Status::OK != isAutoFocusModeAvailable(cameraParams, iter)) {
|
|
continue;
|
|
}
|
|
|
|
cameraParams.set(CameraParameters::KEY_FOCUS_MODE, iter);
|
|
setParameters(device1, cameraParams);
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mNotifyMessage = NotifyCallbackMsg::ERROR;
|
|
}
|
|
|
|
Return<Status> returnStatus = device1->autoFocus();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (NotifyCallbackMsg::FOCUS != mNotifyMessage) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kAutoFocusTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
|
|
}
|
|
}
|
|
}
|
|
|
|
disableMsgType((unsigned int)NotifyCallbackMsg::FOCUS, device1);
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// In case autofocus is supported verify that it can be cancelled.
|
|
TEST_F(CameraHidlTest, cancelAutoFocus) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
CameraParameters cameraParams;
|
|
getParameters(device1, &cameraParams /*out*/);
|
|
|
|
if (Status::OK !=
|
|
isAutoFocusModeAvailable(cameraParams, CameraParameters::FOCUS_MODE_AUTO)) {
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
// It should be fine to call before preview starts.
|
|
ASSERT_EQ(Status::OK, device1->cancelAutoFocus());
|
|
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
startPreview(device1);
|
|
|
|
// It should be fine to call after preview starts too.
|
|
Return<Status> returnStatus = device1->cancelAutoFocus();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
returnStatus = device1->autoFocus();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
returnStatus = device1->cancelAutoFocus();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether face detection is available and try to enable&disable.
|
|
TEST_F(CameraHidlTest, sendCommandFaceDetection) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
CameraParameters cameraParams;
|
|
getParameters(device1, &cameraParams /*out*/);
|
|
|
|
int32_t hwFaces = cameraParams.getInt(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW);
|
|
int32_t swFaces = cameraParams.getInt(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW);
|
|
if ((0 >= hwFaces) && (0 >= swFaces)) {
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
startPreview(device1);
|
|
|
|
if (0 < hwFaces) {
|
|
Return<Status> returnStatus = device1->sendCommand(
|
|
CommandType::START_FACE_DETECTION, CAMERA_FACE_DETECTION_HW, 0);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
// TODO(epeev) : Enable and check for face notifications
|
|
returnStatus = device1->sendCommand(CommandType::STOP_FACE_DETECTION,
|
|
CAMERA_FACE_DETECTION_HW, 0);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
}
|
|
|
|
if (0 < swFaces) {
|
|
Return<Status> returnStatus = device1->sendCommand(
|
|
CommandType::START_FACE_DETECTION, CAMERA_FACE_DETECTION_SW, 0);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
// TODO(epeev) : Enable and check for face notifications
|
|
returnStatus = device1->sendCommand(CommandType::STOP_FACE_DETECTION,
|
|
CAMERA_FACE_DETECTION_SW, 0);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
}
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether smooth zoom is available and try to enable&disable.
|
|
TEST_F(CameraHidlTest, sendCommandSmoothZoom) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
CameraParameters cameraParams;
|
|
getParameters(device1, &cameraParams /*out*/);
|
|
|
|
const char* smoothZoomStr =
|
|
cameraParams.get(CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED);
|
|
bool smoothZoomSupported =
|
|
((nullptr != smoothZoomStr) && (strcmp(smoothZoomStr, CameraParameters::TRUE) == 0))
|
|
? true
|
|
: false;
|
|
if (!smoothZoomSupported) {
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
int32_t maxZoom = cameraParams.getInt(CameraParameters::KEY_MAX_ZOOM);
|
|
ASSERT_TRUE(0 < maxZoom);
|
|
|
|
sp<BufferItemConsumer> bufferItemConsumer;
|
|
sp<BufferItemHander> bufferHandler;
|
|
setupPreviewWindow(device1, &bufferItemConsumer /*out*/, &bufferHandler /*out*/);
|
|
startPreview(device1);
|
|
setParameters(device1, cameraParams);
|
|
|
|
Return<Status> returnStatus =
|
|
device1->sendCommand(CommandType::START_SMOOTH_ZOOM, maxZoom, 0);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
// TODO(epeev) : Enable and check for face notifications
|
|
returnStatus = device1->sendCommand(CommandType::STOP_SMOOTH_ZOOM, 0, 0);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
stopPreviewAndClose(device1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Basic sanity tests related to camera parameters.
|
|
TEST_F(CameraHidlTest, getSetParameters) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
CameraParameters cameraParams;
|
|
getParameters(device1, &cameraParams /*out*/);
|
|
|
|
int32_t width, height;
|
|
cameraParams.getPictureSize(&width, &height);
|
|
ASSERT_TRUE((0 < width) && (0 < height));
|
|
cameraParams.getPreviewSize(&width, &height);
|
|
ASSERT_TRUE((0 < width) && (0 < height));
|
|
int32_t minFps, maxFps;
|
|
cameraParams.getPreviewFpsRange(&minFps, &maxFps);
|
|
ASSERT_TRUE((0 < minFps) && (0 < maxFps));
|
|
ASSERT_NE(nullptr, cameraParams.getPreviewFormat());
|
|
ASSERT_NE(nullptr, cameraParams.getPictureFormat());
|
|
ASSERT_TRUE(
|
|
strcmp(CameraParameters::PIXEL_FORMAT_JPEG, cameraParams.getPictureFormat()) == 0);
|
|
|
|
const char* flashMode = cameraParams.get(CameraParameters::KEY_FLASH_MODE);
|
|
ASSERT_TRUE((nullptr == flashMode) ||
|
|
(strcmp(CameraParameters::FLASH_MODE_OFF, flashMode) == 0));
|
|
|
|
const char* wbMode = cameraParams.get(CameraParameters::KEY_WHITE_BALANCE);
|
|
ASSERT_TRUE((nullptr == wbMode) ||
|
|
(strcmp(CameraParameters::WHITE_BALANCE_AUTO, wbMode) == 0));
|
|
|
|
const char* effect = cameraParams.get(CameraParameters::KEY_EFFECT);
|
|
ASSERT_TRUE((nullptr == effect) ||
|
|
(strcmp(CameraParameters::EFFECT_NONE, effect) == 0));
|
|
|
|
::android::Vector<Size> previewSizes;
|
|
cameraParams.getSupportedPreviewSizes(previewSizes);
|
|
ASSERT_FALSE(previewSizes.empty());
|
|
::android::Vector<Size> pictureSizes;
|
|
cameraParams.getSupportedPictureSizes(pictureSizes);
|
|
ASSERT_FALSE(pictureSizes.empty());
|
|
const char* previewFormats =
|
|
cameraParams.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
|
|
ASSERT_NE(nullptr, previewFormats);
|
|
::android::String8 previewFormatsString(previewFormats);
|
|
ASSERT_TRUE(previewFormatsString.contains(CameraParameters::PIXEL_FORMAT_YUV420SP));
|
|
ASSERT_NE(nullptr, cameraParams.get(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS));
|
|
ASSERT_NE(nullptr,
|
|
cameraParams.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES));
|
|
const char* focusModes = cameraParams.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES);
|
|
ASSERT_NE(nullptr, focusModes);
|
|
::android::String8 focusModesString(focusModes);
|
|
const char* focusMode = cameraParams.get(CameraParameters::KEY_FOCUS_MODE);
|
|
ASSERT_NE(nullptr, focusMode);
|
|
// Auto focus mode should be default
|
|
if (focusModesString.contains(CameraParameters::FOCUS_MODE_AUTO)) {
|
|
ASSERT_TRUE(strcmp(CameraParameters::FOCUS_MODE_AUTO, focusMode) == 0);
|
|
}
|
|
ASSERT_TRUE(0 < cameraParams.getInt(CameraParameters::KEY_FOCAL_LENGTH));
|
|
int32_t horizontalViewAngle =
|
|
cameraParams.getInt(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE);
|
|
ASSERT_TRUE((0 < horizontalViewAngle) && (360 >= horizontalViewAngle));
|
|
int32_t verticalViewAngle =
|
|
cameraParams.getInt(CameraParameters::KEY_VERTICAL_VIEW_ANGLE);
|
|
ASSERT_TRUE((0 < verticalViewAngle) && (360 >= verticalViewAngle));
|
|
int32_t jpegQuality = cameraParams.getInt(CameraParameters::KEY_JPEG_QUALITY);
|
|
ASSERT_TRUE((1 <= jpegQuality) && (100 >= jpegQuality));
|
|
int32_t jpegThumbQuality =
|
|
cameraParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY);
|
|
ASSERT_TRUE((1 <= jpegThumbQuality) && (100 >= jpegThumbQuality));
|
|
|
|
cameraParams.setPictureSize(pictureSizes[0].width, pictureSizes[0].height);
|
|
cameraParams.setPreviewSize(previewSizes[0].width, previewSizes[0].height);
|
|
|
|
setParameters(device1, cameraParams);
|
|
getParameters(device1, &cameraParams /*out*/);
|
|
|
|
cameraParams.getPictureSize(&width, &height);
|
|
ASSERT_TRUE((pictureSizes[0].width == width) && (pictureSizes[0].height == height));
|
|
cameraParams.getPreviewSize(&width, &height);
|
|
ASSERT_TRUE((previewSizes[0].width == width) && (previewSizes[0].height == height));
|
|
|
|
Return<void> ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that the static camera characteristics can be retrieved
|
|
// successfully.
|
|
TEST_F(CameraHidlTest, getCameraCharacteristics) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
|
|
ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) {
|
|
verifyCameraCharacteristics(status, chars);
|
|
verifyMonochromeCharacteristics(chars, deviceVersion);
|
|
verifyRecommendedConfigs(chars);
|
|
verifyLogicalCameraMetadata(name, device3_x, chars, deviceVersion,
|
|
cameraDeviceNames);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
//getPhysicalCameraCharacteristics will fail for publicly
|
|
//advertised camera IDs.
|
|
if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
|
|
auto castResult = device::V3_5::ICameraDevice::castFrom(device3_x);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice>
|
|
device3_5 = castResult;
|
|
ASSERT_NE(device3_5, nullptr);
|
|
|
|
std::string version, cameraId;
|
|
ASSERT_TRUE(::matchDeviceName(name, mProviderType, &version, &cameraId));
|
|
Return<void> ret = device3_5->getPhysicalCameraCharacteristics(cameraId,
|
|
[&](auto status, const auto& chars) {
|
|
ASSERT_TRUE(Status::ILLEGAL_ARGUMENT == status);
|
|
ASSERT_EQ(0, chars.size());
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
//Not applicable
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//In case it is supported verify that torch can be enabled.
|
|
//Check for corresponding toch callbacks as well.
|
|
TEST_F(CameraHidlTest, setTorchMode) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
bool torchControlSupported = false;
|
|
Return<void> ret;
|
|
|
|
ret = mProvider->isSetTorchModeSupported([&](auto status, bool support) {
|
|
ALOGI("isSetTorchModeSupported returns status:%d supported:%d", (int)status, support);
|
|
ASSERT_EQ(Status::OK, status);
|
|
torchControlSupported = support;
|
|
});
|
|
|
|
sp<TorchProviderCb> cb = new TorchProviderCb(this);
|
|
Return<Status> returnStatus = mProvider->setCallback(cb);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
|
|
ALOGI("setTorchMode: Testing camera device %s", name.c_str());
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
|
|
returnStatus = device3_x->setTorchMode(TorchMode::ON);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
if (!torchControlSupported) {
|
|
ASSERT_EQ(Status::METHOD_NOT_SUPPORTED, returnStatus);
|
|
} else {
|
|
ASSERT_TRUE(returnStatus == Status::OK ||
|
|
returnStatus == Status::OPERATION_NOT_SUPPORTED);
|
|
if (returnStatus == Status::OK) {
|
|
{
|
|
std::unique_lock<std::mutex> l(mTorchLock);
|
|
while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kTorchTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout));
|
|
}
|
|
ASSERT_EQ(TorchModeStatus::AVAILABLE_ON, mTorchStatus);
|
|
mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
|
|
}
|
|
|
|
returnStatus = device3_x->setTorchMode(TorchMode::OFF);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mTorchLock);
|
|
while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kTorchTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout));
|
|
}
|
|
ASSERT_EQ(TorchModeStatus::AVAILABLE_OFF, mTorchStatus);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
ALOGI("dumpState: Testing camera device %s", name.c_str());
|
|
ret = mProvider->getCameraDeviceInterface_V1_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device1 = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
|
|
returnStatus = device1->setTorchMode(TorchMode::ON);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
if (!torchControlSupported) {
|
|
ASSERT_EQ(Status::METHOD_NOT_SUPPORTED, returnStatus);
|
|
} else {
|
|
ASSERT_TRUE(returnStatus == Status::OK ||
|
|
returnStatus == Status::OPERATION_NOT_SUPPORTED);
|
|
if (returnStatus == Status::OK) {
|
|
{
|
|
std::unique_lock<std::mutex> l(mTorchLock);
|
|
while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kTorchTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l,
|
|
timeout));
|
|
}
|
|
ASSERT_EQ(TorchModeStatus::AVAILABLE_ON, mTorchStatus);
|
|
mTorchStatus = TorchModeStatus::NOT_AVAILABLE;
|
|
}
|
|
|
|
returnStatus = device1->setTorchMode(TorchMode::OFF);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mTorchLock);
|
|
while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kTorchTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l,
|
|
timeout));
|
|
}
|
|
ASSERT_EQ(TorchModeStatus::AVAILABLE_OFF, mTorchStatus);
|
|
}
|
|
}
|
|
}
|
|
ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
returnStatus = mProvider->setCallback(nullptr);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
}
|
|
|
|
// Check dump functionality.
|
|
TEST_F(CameraHidlTest, dumpState) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
Return<void> ret;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
::android::sp<ICameraDevice> device3_x;
|
|
ALOGI("dumpState: Testing camera device %s", name.c_str());
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
native_handle_t* raw_handle = native_handle_create(1, 0);
|
|
raw_handle->data[0] = open(kDumpOutput, O_RDWR);
|
|
ASSERT_GE(raw_handle->data[0], 0);
|
|
hidl_handle handle = raw_handle;
|
|
ret = device3_x->dumpState(handle);
|
|
ASSERT_TRUE(ret.isOk());
|
|
close(raw_handle->data[0]);
|
|
native_handle_delete(raw_handle);
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
::android::sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
ALOGI("dumpState: Testing camera device %s", name.c_str());
|
|
ret = mProvider->getCameraDeviceInterface_V1_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device1 = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
native_handle_t* raw_handle = native_handle_create(1, 0);
|
|
raw_handle->data[0] = open(kDumpOutput, O_RDWR);
|
|
ASSERT_GE(raw_handle->data[0], 0);
|
|
hidl_handle handle = raw_handle;
|
|
Return<Status> returnStatus = device1->dumpState(handle);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
close(raw_handle->data[0]);
|
|
native_handle_delete(raw_handle);
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Open, dumpStates, then close
|
|
TEST_F(CameraHidlTest, openClose) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
Return<void> ret;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
|
|
ALOGI("openClose: Testing camera device %s", name.c_str());
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
|
|
sp<ICameraDeviceSession> session;
|
|
ret = device3_x->open(cb, [&](auto status, const auto& newSession) {
|
|
ALOGI("device::open returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(newSession, nullptr);
|
|
session = newSession;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
// Ensure that a device labeling itself as 3.3/3.4 can have its session interface
|
|
// cast the 3.3/3.4 interface, and that lower versions can't be cast to it.
|
|
sp<device::V3_3::ICameraDeviceSession> sessionV3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> sessionV3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> sessionV3_5;
|
|
castSession(session, deviceVersion, &sessionV3_3, &sessionV3_4, &sessionV3_5);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
|
|
ASSERT_TRUE(sessionV3_5.get() != nullptr);
|
|
} else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
|
|
ASSERT_TRUE(sessionV3_4.get() != nullptr);
|
|
} else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_3) {
|
|
ASSERT_TRUE(sessionV3_3.get() != nullptr);
|
|
} else { //V3_2
|
|
ASSERT_TRUE(sessionV3_3.get() == nullptr);
|
|
ASSERT_TRUE(sessionV3_4.get() == nullptr);
|
|
ASSERT_TRUE(sessionV3_5.get() == nullptr);
|
|
}
|
|
native_handle_t* raw_handle = native_handle_create(1, 0);
|
|
raw_handle->data[0] = open(kDumpOutput, O_RDWR);
|
|
ASSERT_GE(raw_handle->data[0], 0);
|
|
hidl_handle handle = raw_handle;
|
|
ret = device3_x->dumpState(handle);
|
|
ASSERT_TRUE(ret.isOk());
|
|
close(raw_handle->data[0]);
|
|
native_handle_delete(raw_handle);
|
|
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
// TODO: test all session API calls return INTERNAL_ERROR after close
|
|
// TODO: keep a wp copy here and verify session cannot be promoted out of this scope
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> device1;
|
|
openCameraDevice(name, mProvider, &device1 /*out*/);
|
|
ASSERT_NE(nullptr, device1.get());
|
|
|
|
native_handle_t* raw_handle = native_handle_create(1, 0);
|
|
raw_handle->data[0] = open(kDumpOutput, O_RDWR);
|
|
ASSERT_GE(raw_handle->data[0], 0);
|
|
hidl_handle handle = raw_handle;
|
|
Return<Status> returnStatus = device1->dumpState(handle);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
close(raw_handle->data[0]);
|
|
native_handle_delete(raw_handle);
|
|
|
|
ret = device1->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether all common default request settings can be sucessfully
|
|
// constructed.
|
|
TEST_F(CameraHidlTest, constructDefaultRequestSettings) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5:
|
|
case CAMERA_DEVICE_API_VERSION_3_4:
|
|
case CAMERA_DEVICE_API_VERSION_3_3:
|
|
case CAMERA_DEVICE_API_VERSION_3_2: {
|
|
::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
|
|
Return<void> ret;
|
|
ALOGI("constructDefaultRequestSettings: Testing camera device %s", name.c_str());
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(
|
|
name, [&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
sp<EmptyDeviceCb> cb = new EmptyDeviceCb;
|
|
sp<ICameraDeviceSession> session;
|
|
ret = device3_x->open(cb, [&](auto status, const auto& newSession) {
|
|
ALOGI("device::open returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(newSession, nullptr);
|
|
session = newSession;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
for (uint32_t t = (uint32_t)RequestTemplate::PREVIEW;
|
|
t <= (uint32_t)RequestTemplate::MANUAL; t++) {
|
|
RequestTemplate reqTemplate = (RequestTemplate)t;
|
|
ret =
|
|
session->constructDefaultRequestSettings(
|
|
reqTemplate, [&](auto status, const auto& req) {
|
|
ALOGI("constructDefaultRequestSettings returns status:%d",
|
|
(int)status);
|
|
if (reqTemplate == RequestTemplate::ZERO_SHUTTER_LAG ||
|
|
reqTemplate == RequestTemplate::MANUAL) {
|
|
// optional templates
|
|
ASSERT_TRUE((status == Status::OK) ||
|
|
(status == Status::ILLEGAL_ARGUMENT));
|
|
} else {
|
|
ASSERT_EQ(Status::OK, status);
|
|
}
|
|
|
|
if (status == Status::OK) {
|
|
const camera_metadata_t* metadata =
|
|
(camera_metadata_t*) req.data();
|
|
size_t expectedSize = req.size();
|
|
int result = validate_camera_metadata_structure(
|
|
metadata, &expectedSize);
|
|
ASSERT_TRUE((result == 0) ||
|
|
(result == CAMERA_METADATA_VALIDATION_SHIFTED));
|
|
size_t entryCount =
|
|
get_camera_metadata_entry_count(metadata);
|
|
// TODO: we can do better than 0 here. Need to check how many required
|
|
// request keys we've defined for each template
|
|
ASSERT_GT(entryCount, 0u);
|
|
ALOGI("template %u metadata entry count is %zu",
|
|
t, entryCount);
|
|
} else {
|
|
ASSERT_EQ(0u, req.size());
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
break;
|
|
case CAMERA_DEVICE_API_VERSION_1_0: {
|
|
//Not applicable
|
|
}
|
|
break;
|
|
default: {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Verify that all supported stream formats and sizes can be configured
|
|
// successfully.
|
|
TEST_F(CameraHidlTest, configureStreamsAvailableOutputs) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputStreams;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<device::V3_2::ICameraDevice> cameraDevice;
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
openEmptyDeviceSession(name, mProvider,
|
|
&session /*out*/, &staticMeta /*out*/, &cameraDevice /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
|
|
|
|
outputStreams.clear();
|
|
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
|
|
ASSERT_NE(0u, outputStreams.size());
|
|
|
|
uint32_t jpegBufferSize = 0;
|
|
ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
|
|
ASSERT_NE(0u, jpegBufferSize);
|
|
|
|
int32_t streamId = 0;
|
|
uint32_t streamConfigCounter = 0;
|
|
for (auto& it : outputStreams) {
|
|
V3_2::Stream stream3_2;
|
|
V3_2::DataspaceFlags dataspaceFlag = 0;
|
|
switch (static_cast<PixelFormat>(it.format)) {
|
|
case PixelFormat::BLOB:
|
|
dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
|
|
break;
|
|
case PixelFormat::Y16:
|
|
dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
|
|
break;
|
|
default:
|
|
dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
|
|
}
|
|
stream3_2 = {streamId,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(it.width),
|
|
static_cast<uint32_t>(it.height),
|
|
static_cast<PixelFormat>(it.format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
dataspaceFlag,
|
|
StreamRotation::ROTATION_0};
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
bool expectStreamCombQuery = (isLogicalMultiCamera(staticMeta) == Status::OK);
|
|
verifyStreamCombination(cameraDevice3_5, config3_4,
|
|
/*expectedStatus*/ true, expectStreamCombQuery);
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[streamId](Status s, HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].id, streamId);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
streamId++;
|
|
}
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Check for correct handling of invalid/incorrect configuration parameters.
|
|
TEST_F(CameraHidlTest, configureStreamsInvalidOutputs) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputStreams;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<device::V3_2::ICameraDevice> cameraDevice;
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
|
|
&cameraDevice /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
|
|
|
|
outputStreams.clear();
|
|
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams));
|
|
ASSERT_NE(0u, outputStreams.size());
|
|
|
|
uint32_t jpegBufferSize = 0;
|
|
ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
|
|
ASSERT_NE(0u, jpegBufferSize);
|
|
|
|
int32_t streamId = 0;
|
|
V3_2::Stream stream3_2 = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(0),
|
|
static_cast<uint32_t>(0),
|
|
static_cast<PixelFormat>(outputStreams[0].format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
uint32_t streamConfigCounter = 0;
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams = {stream3_2};
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
verifyStreamCombination(cameraDevice3_5, config3_4, /*expectedStatus*/ false,
|
|
/*expectStreamCombQuery*/false);
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
} else if(session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
stream3_2 = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(UINT32_MAX),
|
|
static_cast<uint32_t>(UINT32_MAX),
|
|
static_cast<PixelFormat>(outputStreams[0].format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
streams[0] = stream3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5, [](Status s,
|
|
device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if(session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4, [](Status s,
|
|
device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if(session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2, [](Status s,
|
|
device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2, [](Status s,
|
|
HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
for (auto& it : outputStreams) {
|
|
stream3_2 = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(it.width),
|
|
static_cast<uint32_t>(it.height),
|
|
static_cast<PixelFormat>(UINT32_MAX),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
streams[0] = stream3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if(session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if(session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
stream3_2 = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(it.width),
|
|
static_cast<uint32_t>(it.height),
|
|
static_cast<PixelFormat>(it.format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
static_cast<StreamRotation>(UINT32_MAX)};
|
|
streams[0] = stream3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if(session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if(session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Check whether all supported ZSL output stream combinations can be
|
|
// configured successfully.
|
|
TEST_F(CameraHidlTest, configureStreamsZSLInputOutputs) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> inputStreams;
|
|
std::vector<AvailableZSLInputOutput> inputOutputMap;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<device::V3_2::ICameraDevice> cameraDevice;
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
|
|
&cameraDevice /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
|
|
|
|
Status rc = isZSLModeAvailable(staticMeta);
|
|
if (Status::METHOD_NOT_SUPPORTED == rc) {
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
ASSERT_EQ(Status::OK, rc);
|
|
|
|
inputStreams.clear();
|
|
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, inputStreams));
|
|
ASSERT_NE(0u, inputStreams.size());
|
|
|
|
inputOutputMap.clear();
|
|
ASSERT_EQ(Status::OK, getZSLInputOutputMap(staticMeta, inputOutputMap));
|
|
ASSERT_NE(0u, inputOutputMap.size());
|
|
|
|
bool supportMonoY8 = false;
|
|
if (Status::OK == isMonochromeCamera(staticMeta)) {
|
|
for (auto& it : inputStreams) {
|
|
if (it.format == static_cast<uint32_t>(PixelFormat::Y8)) {
|
|
supportMonoY8 = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t jpegBufferSize = 0;
|
|
ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
|
|
ASSERT_NE(0u, jpegBufferSize);
|
|
|
|
int32_t streamId = 0;
|
|
bool hasPrivToY8 = false, hasY8ToY8 = false, hasY8ToBlob = false;
|
|
uint32_t streamConfigCounter = 0;
|
|
for (auto& inputIter : inputOutputMap) {
|
|
AvailableStream input;
|
|
ASSERT_EQ(Status::OK, findLargestSize(inputStreams, inputIter.inputFormat,
|
|
input));
|
|
ASSERT_NE(0u, inputStreams.size());
|
|
|
|
if (inputIter.inputFormat == static_cast<uint32_t>(PixelFormat::IMPLEMENTATION_DEFINED)
|
|
&& inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::Y8)) {
|
|
hasPrivToY8 = true;
|
|
} else if (inputIter.inputFormat == static_cast<uint32_t>(PixelFormat::Y8)) {
|
|
if (inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::BLOB)) {
|
|
hasY8ToBlob = true;
|
|
} else if (inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::Y8)) {
|
|
hasY8ToY8 = true;
|
|
}
|
|
}
|
|
AvailableStream outputThreshold = {INT32_MAX, INT32_MAX,
|
|
inputIter.outputFormat};
|
|
std::vector<AvailableStream> outputStreams;
|
|
ASSERT_EQ(Status::OK,
|
|
getAvailableOutputStreams(staticMeta, outputStreams,
|
|
&outputThreshold));
|
|
for (auto& outputIter : outputStreams) {
|
|
V3_2::Stream zslStream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(input.width),
|
|
static_cast<uint32_t>(input.height),
|
|
static_cast<PixelFormat>(input.format),
|
|
GRALLOC_USAGE_HW_CAMERA_ZSL,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
V3_2::Stream inputStream = {streamId++,
|
|
StreamType::INPUT,
|
|
static_cast<uint32_t>(input.width),
|
|
static_cast<uint32_t>(input.height),
|
|
static_cast<PixelFormat>(input.format),
|
|
0,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
V3_2::Stream outputStream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(outputIter.width),
|
|
static_cast<uint32_t>(outputIter.height),
|
|
static_cast<PixelFormat>(outputIter.format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams = {inputStream, zslStream,
|
|
outputStream};
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
verifyStreamCombination(cameraDevice3_5, config3_4,
|
|
/*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(3u, halConfig.streams.size());
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(3u, halConfig.streams.size());
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(3u, halConfig.streams.size());
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(3u, halConfig.streams.size());
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
if (supportMonoY8) {
|
|
if (Status::OK == isZSLModeAvailable(staticMeta, PRIV_REPROCESS)) {
|
|
ASSERT_TRUE(hasPrivToY8);
|
|
}
|
|
if (Status::OK == isZSLModeAvailable(staticMeta, YUV_REPROCESS)) {
|
|
ASSERT_TRUE(hasY8ToY8);
|
|
ASSERT_TRUE(hasY8ToBlob);
|
|
}
|
|
}
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Check whether session parameters are supported. If Hal support for them
|
|
// exist, then try to configure a preview stream using them.
|
|
TEST_F(CameraHidlTest, configureStreamsWithSessionParameters) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
} else if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_4) {
|
|
continue;
|
|
}
|
|
|
|
camera_metadata_t* staticMetaBuffer;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
|
|
ASSERT_NE(session3_4, nullptr);
|
|
} else {
|
|
ASSERT_NE(session3_5, nullptr);
|
|
}
|
|
|
|
std::unordered_set<int32_t> availableSessionKeys;
|
|
auto rc = getSupportedKeys(staticMetaBuffer, ANDROID_REQUEST_AVAILABLE_SESSION_KEYS,
|
|
&availableSessionKeys);
|
|
ASSERT_TRUE(Status::OK == rc);
|
|
if (availableSessionKeys.empty()) {
|
|
free_camera_metadata(staticMetaBuffer);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata previewRequestSettings;
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams,
|
|
modifiedSessionParams;
|
|
constructFilteredSettings(session, availableSessionKeys, RequestTemplate::PREVIEW,
|
|
&previewRequestSettings, &sessionParams);
|
|
if (sessionParams.isEmpty()) {
|
|
free_camera_metadata(staticMetaBuffer);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
outputPreviewStreams.clear();
|
|
|
|
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMetaBuffer, outputPreviewStreams,
|
|
&previewThreshold));
|
|
ASSERT_NE(0u, outputPreviewStreams.size());
|
|
|
|
V3_4::Stream previewStream;
|
|
previewStream.v3_2 = {0,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(outputPreviewStreams[0].width),
|
|
static_cast<uint32_t>(outputPreviewStreams[0].height),
|
|
static_cast<PixelFormat>(outputPreviewStreams[0].format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
previewStream.bufferSize = 0;
|
|
::android::hardware::hidl_vec<V3_4::Stream> streams = {previewStream};
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config;
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
config.streams = streams;
|
|
config.operationMode = StreamConfigurationMode::NORMAL_MODE;
|
|
modifiedSessionParams = sessionParams;
|
|
auto sessionParamsBuffer = sessionParams.release();
|
|
config.sessionParams.setToExternal(reinterpret_cast<uint8_t *> (sessionParamsBuffer),
|
|
get_camera_metadata_size(sessionParamsBuffer));
|
|
config3_5.v3_4 = config;
|
|
config3_5.streamConfigCounter = 0;
|
|
if (session3_5 != nullptr) {
|
|
bool newSessionParamsAvailable = false;
|
|
for (const auto& it : availableSessionKeys) {
|
|
if (modifiedSessionParams.exists(it)) {
|
|
modifiedSessionParams.erase(it);
|
|
newSessionParamsAvailable = true;
|
|
break;
|
|
}
|
|
}
|
|
if (newSessionParamsAvailable) {
|
|
auto modifiedSessionParamsBuffer = modifiedSessionParams.release();
|
|
verifySessionReconfigurationQuery(session3_5, sessionParamsBuffer,
|
|
modifiedSessionParamsBuffer);
|
|
modifiedSessionParams.acquire(modifiedSessionParamsBuffer);
|
|
}
|
|
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
});
|
|
} else {
|
|
ret = session3_4->configureStreams_3_4(config,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
});
|
|
}
|
|
sessionParams.acquire(sessionParamsBuffer);
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
free_camera_metadata(staticMetaBuffer);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Verify that all supported preview + still capture stream combinations
|
|
// can be configured successfully.
|
|
TEST_F(CameraHidlTest, configureStreamsPreviewStillOutputs) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputBlobStreams;
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
AvailableStream blobThreshold = {INT32_MAX, INT32_MAX,
|
|
static_cast<int32_t>(PixelFormat::BLOB)};
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<device::V3_2::ICameraDevice> cameraDevice;
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
|
|
&cameraDevice /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
|
|
|
|
// Check if camera support depth only
|
|
if (isDepthOnly(staticMeta)) {
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
outputBlobStreams.clear();
|
|
ASSERT_EQ(Status::OK,
|
|
getAvailableOutputStreams(staticMeta, outputBlobStreams,
|
|
&blobThreshold));
|
|
ASSERT_NE(0u, outputBlobStreams.size());
|
|
|
|
outputPreviewStreams.clear();
|
|
ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputPreviewStreams,
|
|
&previewThreshold));
|
|
ASSERT_NE(0u, outputPreviewStreams.size());
|
|
|
|
uint32_t jpegBufferSize = 0;
|
|
ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
|
|
ASSERT_NE(0u, jpegBufferSize);
|
|
|
|
int32_t streamId = 0;
|
|
uint32_t streamConfigCounter = 0;
|
|
for (auto& blobIter : outputBlobStreams) {
|
|
for (auto& previewIter : outputPreviewStreams) {
|
|
V3_2::Stream previewStream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(previewIter.width),
|
|
static_cast<uint32_t>(previewIter.height),
|
|
static_cast<PixelFormat>(previewIter.format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
V3_2::Stream blobStream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(blobIter.width),
|
|
static_cast<uint32_t>(blobIter.height),
|
|
static_cast<PixelFormat>(blobIter.format),
|
|
GRALLOC1_CONSUMER_USAGE_CPU_READ,
|
|
static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF),
|
|
StreamRotation::ROTATION_0};
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams = {previewStream,
|
|
blobStream};
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
verifyStreamCombination(cameraDevice3_5, config3_4,
|
|
/*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// In case constrained mode is supported, test whether it can be
|
|
// configured. Additionally check for common invalid inputs when
|
|
// using this mode.
|
|
TEST_F(CameraHidlTest, configureStreamsConstrainedOutputs) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<device::V3_2::ICameraDevice> cameraDevice;
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
|
|
&cameraDevice /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
|
|
|
|
Status rc = isConstrainedModeAvailable(staticMeta);
|
|
if (Status::METHOD_NOT_SUPPORTED == rc) {
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
ASSERT_EQ(Status::OK, rc);
|
|
|
|
AvailableStream hfrStream;
|
|
rc = pickConstrainedModeSize(staticMeta, hfrStream);
|
|
ASSERT_EQ(Status::OK, rc);
|
|
|
|
int32_t streamId = 0;
|
|
uint32_t streamConfigCounter = 0;
|
|
V3_2::Stream stream = {streamId,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(hfrStream.width),
|
|
static_cast<uint32_t>(hfrStream.height),
|
|
static_cast<PixelFormat>(hfrStream.format),
|
|
GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams = {stream};
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
|
|
&config3_2, &config3_4, &config3_5);
|
|
if (session3_5 != nullptr) {
|
|
verifyStreamCombination(cameraDevice3_5, config3_4,
|
|
/*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].v3_3.v3_2.id, streamId);
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[streamId](Status s, device::V3_3::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].v3_2.id, streamId);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[streamId](Status s, HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
ASSERT_EQ(halConfig.streams[0].id, streamId);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
stream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(0),
|
|
static_cast<uint32_t>(0),
|
|
static_cast<PixelFormat>(hfrStream.format),
|
|
GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
streams[0] = stream;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
|
|
&config3_2, &config3_4, &config3_5);
|
|
if (session3_5 != nullptr) {
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration) {
|
|
ASSERT_TRUE((Status::ILLEGAL_ARGUMENT == s) ||
|
|
(Status::INTERNAL_ERROR == s));
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
stream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(UINT32_MAX),
|
|
static_cast<uint32_t>(UINT32_MAX),
|
|
static_cast<PixelFormat>(hfrStream.format),
|
|
GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
streams[0] = stream;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
|
|
&config3_2, &config3_4, &config3_5);
|
|
if (session3_5 != nullptr) {
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
stream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(hfrStream.width),
|
|
static_cast<uint32_t>(hfrStream.height),
|
|
static_cast<PixelFormat>(UINT32_MAX),
|
|
GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
streams[0] = stream;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE,
|
|
&config3_2, &config3_4, &config3_5);
|
|
if (session3_5 != nullptr) {
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, s);
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Verify that all supported video + snapshot stream combinations can
|
|
// be configured successfully.
|
|
TEST_F(CameraHidlTest, configureStreamsVideoStillOutputs) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputBlobStreams;
|
|
std::vector<AvailableStream> outputVideoStreams;
|
|
AvailableStream videoThreshold = {kMaxVideoWidth, kMaxVideoHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
AvailableStream blobThreshold = {kMaxVideoWidth, kMaxVideoHeight,
|
|
static_cast<int32_t>(PixelFormat::BLOB)};
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<device::V3_2::ICameraDevice> cameraDevice;
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
|
|
&cameraDevice /*out*/);
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
|
|
|
|
// Check if camera support depth only
|
|
if (isDepthOnly(staticMeta)) {
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
outputBlobStreams.clear();
|
|
ASSERT_EQ(Status::OK,
|
|
getAvailableOutputStreams(staticMeta, outputBlobStreams,
|
|
&blobThreshold));
|
|
ASSERT_NE(0u, outputBlobStreams.size());
|
|
|
|
outputVideoStreams.clear();
|
|
ASSERT_EQ(Status::OK,
|
|
getAvailableOutputStreams(staticMeta, outputVideoStreams,
|
|
&videoThreshold));
|
|
ASSERT_NE(0u, outputVideoStreams.size());
|
|
|
|
uint32_t jpegBufferSize = 0;
|
|
ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
|
|
ASSERT_NE(0u, jpegBufferSize);
|
|
|
|
int32_t streamId = 0;
|
|
uint32_t streamConfigCounter = 0;
|
|
for (auto& blobIter : outputBlobStreams) {
|
|
for (auto& videoIter : outputVideoStreams) {
|
|
V3_2::Stream videoStream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(videoIter.width),
|
|
static_cast<uint32_t>(videoIter.height),
|
|
static_cast<PixelFormat>(videoIter.format),
|
|
GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
|
|
0,
|
|
StreamRotation::ROTATION_0};
|
|
V3_2::Stream blobStream = {streamId++,
|
|
StreamType::OUTPUT,
|
|
static_cast<uint32_t>(blobIter.width),
|
|
static_cast<uint32_t>(blobIter.height),
|
|
static_cast<PixelFormat>(blobIter.format),
|
|
GRALLOC1_CONSUMER_USAGE_CPU_READ,
|
|
static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF),
|
|
StreamRotation::ROTATION_0};
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams = {videoStream, blobStream};
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
verifyStreamCombination(cameraDevice3_5, config3_4,
|
|
/*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
|
|
config3_5.streamConfigCounter = streamConfigCounter++;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[](Status s, device::V3_3::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
} else {
|
|
ret = session->configureStreams(config3_2,
|
|
[](Status s, HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(2u, halConfig.streams.size());
|
|
});
|
|
}
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Generate and verify a camera capture request
|
|
TEST_F(CameraHidlTest, processCaptureRequestPreview) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
uint64_t bufferId = 1;
|
|
uint32_t frameNumber = 1;
|
|
::android::hardware::hidl_vec<uint8_t> settings;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
V3_2::Stream previewStream;
|
|
HalStreamConfiguration halStreamConfig;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<DeviceCb> cb;
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
|
|
&previewStream /*out*/, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/,
|
|
&partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);
|
|
|
|
std::shared_ptr<ResultMetadataQueue> resultQueue;
|
|
auto resultQueueRet =
|
|
session->getCaptureResultMetadataQueue(
|
|
[&resultQueue](const auto& descriptor) {
|
|
resultQueue = std::make_shared<ResultMetadataQueue>(
|
|
descriptor);
|
|
if (!resultQueue->isValid() ||
|
|
resultQueue->availableToWrite() <= 0) {
|
|
ALOGE("%s: HAL returns empty result metadata fmq,"
|
|
" not use it", __func__);
|
|
resultQueue = nullptr;
|
|
// Don't use the queue onwards.
|
|
}
|
|
});
|
|
ASSERT_TRUE(resultQueueRet.isOk());
|
|
|
|
InFlightRequest inflightReq = {1, false, supportsPartialResults,
|
|
partialResultCount, resultQueue};
|
|
|
|
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
|
|
Return<void> ret;
|
|
ret = session->constructDefaultRequestSettings(reqTemplate,
|
|
[&](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
settings = req;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
hidl_handle buffer_handle;
|
|
StreamBuffer outputBuffer;
|
|
if (useHalBufManager) {
|
|
outputBuffer = {halStreamConfig.streams[0].id,
|
|
/*bufferId*/ 0,
|
|
buffer_handle,
|
|
BufferStatus::OK,
|
|
nullptr,
|
|
nullptr};
|
|
} else {
|
|
allocateGraphicBuffer(previewStream.width, previewStream.height,
|
|
android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
|
|
halStreamConfig.streams[0].consumerUsage),
|
|
halStreamConfig.streams[0].overrideFormat, &buffer_handle);
|
|
outputBuffer = {halStreamConfig.streams[0].id,
|
|
bufferId,
|
|
buffer_handle,
|
|
BufferStatus::OK,
|
|
nullptr,
|
|
nullptr};
|
|
}
|
|
::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {outputBuffer};
|
|
StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
|
|
nullptr};
|
|
CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
|
|
emptyInputBuffer, outputBuffers};
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mInflightMap.clear();
|
|
mInflightMap.add(frameNumber, &inflightReq);
|
|
}
|
|
|
|
Status status = Status::INTERNAL_ERROR;
|
|
uint32_t numRequestProcessed = 0;
|
|
hidl_vec<BufferCache> cachesToRemove;
|
|
Return<void> returnStatus = session->processCaptureRequest(
|
|
{request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
|
|
uint32_t n) {
|
|
status = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_EQ(numRequestProcessed, 1u);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (!inflightReq.errorCodeValid &&
|
|
((0 < inflightReq.numBuffersLeft) ||
|
|
(!inflightReq.haveResultMetadata))) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout,
|
|
mResultCondition.wait_until(l, timeout));
|
|
}
|
|
|
|
ASSERT_FALSE(inflightReq.errorCodeValid);
|
|
ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
|
|
ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId);
|
|
|
|
request.frameNumber++;
|
|
// Empty settings should be supported after the first call
|
|
// for repeating requests.
|
|
request.settings.setToExternal(nullptr, 0, true);
|
|
// The buffer has been registered to HAL by bufferId, so per
|
|
// API contract we should send a null handle for this buffer
|
|
request.outputBuffers[0].buffer = nullptr;
|
|
mInflightMap.clear();
|
|
inflightReq = {1, false, supportsPartialResults, partialResultCount,
|
|
resultQueue};
|
|
mInflightMap.add(request.frameNumber, &inflightReq);
|
|
}
|
|
|
|
returnStatus = session->processCaptureRequest(
|
|
{request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
|
|
uint32_t n) {
|
|
status = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_EQ(numRequestProcessed, 1u);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (!inflightReq.errorCodeValid &&
|
|
((0 < inflightReq.numBuffersLeft) ||
|
|
(!inflightReq.haveResultMetadata))) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout,
|
|
mResultCondition.wait_until(l, timeout));
|
|
}
|
|
|
|
ASSERT_FALSE(inflightReq.errorCodeValid);
|
|
ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
|
|
ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId);
|
|
}
|
|
|
|
if (useHalBufManager) {
|
|
verifyBuffersReturned(session, deviceVersion, previewStream.id, cb);
|
|
}
|
|
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Generate and verify a multi-camera capture request
|
|
TEST_F(CameraHidlTest, processMultiCaptureRequestPreview) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::YCBCR_420_888)};
|
|
uint64_t bufferId = 1;
|
|
uint32_t frameNumber = 1;
|
|
::android::hardware::hidl_vec<uint8_t> settings;
|
|
::android::hardware::hidl_vec<uint8_t> emptySettings;
|
|
hidl_string invalidPhysicalId = "-1";
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_5) {
|
|
continue;
|
|
}
|
|
std::string version, deviceId;
|
|
ASSERT_TRUE(::matchDeviceName(name, mProviderType, &version, &deviceId));
|
|
camera_metadata_t* staticMeta;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/);
|
|
|
|
Status rc = isLogicalMultiCamera(staticMeta);
|
|
if (Status::METHOD_NOT_SUPPORTED == rc) {
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
std::unordered_set<std::string> physicalIds;
|
|
rc = getPhysicalCameraIds(staticMeta, &physicalIds);
|
|
ASSERT_TRUE(Status::OK == rc);
|
|
ASSERT_TRUE(physicalIds.size() > 1);
|
|
|
|
std::unordered_set<int32_t> physicalRequestKeyIDs;
|
|
rc = getSupportedKeys(staticMeta,
|
|
ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS, &physicalRequestKeyIDs);
|
|
ASSERT_TRUE(Status::OK == rc);
|
|
if (physicalRequestKeyIDs.empty()) {
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
// The logical camera doesn't support any individual physical requests.
|
|
continue;
|
|
}
|
|
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata defaultPreviewSettings;
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata filteredSettings;
|
|
constructFilteredSettings(session, physicalRequestKeyIDs, RequestTemplate::PREVIEW,
|
|
&defaultPreviewSettings, &filteredSettings);
|
|
if (filteredSettings.isEmpty()) {
|
|
// No physical device settings in default request.
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
const camera_metadata_t *settingsBuffer = defaultPreviewSettings.getAndLock();
|
|
settings.setToExternal(
|
|
reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (settingsBuffer)),
|
|
get_camera_metadata_size(settingsBuffer));
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
// Leave only 2 physical devices in the id set.
|
|
auto it = physicalIds.begin();
|
|
string physicalDeviceId = *it; it++;
|
|
physicalIds.erase(++it, physicalIds.end());
|
|
ASSERT_EQ(physicalIds.size(), 2u);
|
|
|
|
V3_4::HalStreamConfiguration halStreamConfig;
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
V3_2::Stream previewStream;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
sp<DeviceCb> cb;
|
|
configurePreviewStreams3_4(name, deviceVersion, mProvider, &previewThreshold, physicalIds,
|
|
&session3_4, &session3_5, &previewStream, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/, &partialResultCount /*out*/,
|
|
&useHalBufManager /*out*/, &cb /*out*/, 0 /*streamConfigCounter*/,
|
|
true /*allowUnsupport*/);
|
|
if (session3_5 == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
std::shared_ptr<ResultMetadataQueue> resultQueue;
|
|
auto resultQueueRet =
|
|
session3_4->getCaptureResultMetadataQueue(
|
|
[&resultQueue](const auto& descriptor) {
|
|
resultQueue = std::make_shared<ResultMetadataQueue>(
|
|
descriptor);
|
|
if (!resultQueue->isValid() ||
|
|
resultQueue->availableToWrite() <= 0) {
|
|
ALOGE("%s: HAL returns empty result metadata fmq,"
|
|
" not use it", __func__);
|
|
resultQueue = nullptr;
|
|
// Don't use the queue onwards.
|
|
}
|
|
});
|
|
ASSERT_TRUE(resultQueueRet.isOk());
|
|
|
|
InFlightRequest inflightReq = {static_cast<ssize_t> (halStreamConfig.streams.size()), false,
|
|
supportsPartialResults, partialResultCount, resultQueue};
|
|
|
|
std::vector<hidl_handle> graphicBuffers;
|
|
graphicBuffers.reserve(halStreamConfig.streams.size());
|
|
::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
|
|
outputBuffers.resize(halStreamConfig.streams.size());
|
|
size_t k = 0;
|
|
for (const auto& halStream : halStreamConfig.streams) {
|
|
hidl_handle buffer_handle;
|
|
if (useHalBufManager) {
|
|
outputBuffers[k] = {halStream.v3_3.v3_2.id, /*bufferId*/0, buffer_handle,
|
|
BufferStatus::OK, nullptr, nullptr};
|
|
} else {
|
|
allocateGraphicBuffer(previewStream.width, previewStream.height,
|
|
android_convertGralloc1To0Usage(halStream.v3_3.v3_2.producerUsage,
|
|
halStream.v3_3.v3_2.consumerUsage),
|
|
halStream.v3_3.v3_2.overrideFormat, &buffer_handle);
|
|
graphicBuffers.push_back(buffer_handle);
|
|
outputBuffers[k] = {halStream.v3_3.v3_2.id, bufferId, buffer_handle,
|
|
BufferStatus::OK, nullptr, nullptr};
|
|
bufferId++;
|
|
}
|
|
k++;
|
|
}
|
|
hidl_vec<V3_4::PhysicalCameraSetting> camSettings(1);
|
|
const camera_metadata_t *filteredSettingsBuffer = filteredSettings.getAndLock();
|
|
camSettings[0].settings.setToExternal(
|
|
reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (
|
|
filteredSettingsBuffer)),
|
|
get_camera_metadata_size(filteredSettingsBuffer));
|
|
camSettings[0].fmqSettingsSize = 0;
|
|
camSettings[0].physicalCameraId = physicalDeviceId;
|
|
|
|
StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
|
|
V3_4::CaptureRequest request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
|
|
emptyInputBuffer, outputBuffers}, camSettings};
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mInflightMap.clear();
|
|
mInflightMap.add(frameNumber, &inflightReq);
|
|
}
|
|
|
|
Status stat = Status::INTERNAL_ERROR;
|
|
uint32_t numRequestProcessed = 0;
|
|
hidl_vec<BufferCache> cachesToRemove;
|
|
Return<void> returnStatus = session3_4->processCaptureRequest_3_4(
|
|
{request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
|
|
stat = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, stat);
|
|
ASSERT_EQ(numRequestProcessed, 1u);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (!inflightReq.errorCodeValid &&
|
|
((0 < inflightReq.numBuffersLeft) ||
|
|
(!inflightReq.haveResultMetadata))) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout,
|
|
mResultCondition.wait_until(l, timeout));
|
|
}
|
|
|
|
ASSERT_FALSE(inflightReq.errorCodeValid);
|
|
ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
|
|
|
|
request.v3_2.frameNumber++;
|
|
// Empty settings should be supported after the first call
|
|
// for repeating requests.
|
|
request.v3_2.settings.setToExternal(nullptr, 0, true);
|
|
request.physicalCameraSettings[0].settings.setToExternal(nullptr, 0, true);
|
|
// The buffer has been registered to HAL by bufferId, so per
|
|
// API contract we should send a null handle for this buffer
|
|
request.v3_2.outputBuffers[0].buffer = nullptr;
|
|
mInflightMap.clear();
|
|
inflightReq = {static_cast<ssize_t> (physicalIds.size()), false,
|
|
supportsPartialResults, partialResultCount, resultQueue};
|
|
mInflightMap.add(request.v3_2.frameNumber, &inflightReq);
|
|
}
|
|
|
|
returnStatus = session3_4->processCaptureRequest_3_4(
|
|
{request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
|
|
stat = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, stat);
|
|
ASSERT_EQ(numRequestProcessed, 1u);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (!inflightReq.errorCodeValid &&
|
|
((0 < inflightReq.numBuffersLeft) ||
|
|
(!inflightReq.haveResultMetadata))) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout,
|
|
mResultCondition.wait_until(l, timeout));
|
|
}
|
|
|
|
ASSERT_FALSE(inflightReq.errorCodeValid);
|
|
ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
|
|
}
|
|
|
|
// Invalid physical camera id should fail process requests
|
|
frameNumber++;
|
|
camSettings[0].physicalCameraId = invalidPhysicalId;
|
|
camSettings[0].settings = settings;
|
|
request = {{frameNumber, 0 /* fmqSettingsSize */, settings,
|
|
emptyInputBuffer, outputBuffers}, camSettings};
|
|
returnStatus = session3_4->processCaptureRequest_3_4(
|
|
{request}, cachesToRemove, [&stat, &numRequestProcessed](auto s, uint32_t n) {
|
|
stat = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, stat);
|
|
|
|
defaultPreviewSettings.unlock(settingsBuffer);
|
|
filteredSettings.unlock(filteredSettingsBuffer);
|
|
|
|
if (useHalBufManager) {
|
|
hidl_vec<int32_t> streamIds(halStreamConfig.streams.size());
|
|
for (size_t i = 0; i < streamIds.size(); i++) {
|
|
streamIds[i] = halStreamConfig.streams[i].v3_3.v3_2.id;
|
|
}
|
|
verifyBuffersReturned(session3_4, streamIds, cb);
|
|
}
|
|
|
|
ret = session3_4->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Generate and verify a burst containing alternating sensor sensitivity values
|
|
TEST_F(CameraHidlTest, processCaptureRequestBurstISO) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
uint64_t bufferId = 1;
|
|
uint32_t frameNumber = 1;
|
|
float isoTol = .03f;
|
|
::android::hardware::hidl_vec<uint8_t> settings;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
camera_metadata_t* staticMetaBuffer;
|
|
Return<void> ret;
|
|
sp<ICameraDeviceSession> session;
|
|
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
|
|
::android::hardware::camera::common::V1_0::helper::CameraMetadata staticMeta(
|
|
staticMetaBuffer);
|
|
|
|
camera_metadata_entry_t hwLevel = staticMeta.find(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
|
|
ASSERT_TRUE(0 < hwLevel.count);
|
|
if (ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED == hwLevel.data.u8[0] ||
|
|
ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL == hwLevel.data.u8[0]) {
|
|
//Limited/External devices can skip this test
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
continue;
|
|
}
|
|
|
|
camera_metadata_entry_t isoRange = staticMeta.find(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE);
|
|
ASSERT_EQ(isoRange.count, 2u);
|
|
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
V3_2::Stream previewStream;
|
|
HalStreamConfiguration halStreamConfig;
|
|
sp<DeviceCb> cb;
|
|
configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold,
|
|
&session /*out*/, &previewStream /*out*/, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/, &partialResultCount /*out*/,
|
|
&useHalBufManager /*out*/, &cb /*out*/);
|
|
std::shared_ptr<ResultMetadataQueue> resultQueue;
|
|
|
|
auto resultQueueRet = session->getCaptureResultMetadataQueue(
|
|
[&resultQueue](const auto& descriptor) {
|
|
resultQueue = std::make_shared<ResultMetadataQueue>(descriptor);
|
|
if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) {
|
|
ALOGE("%s: HAL returns empty result metadata fmq,"
|
|
" not use it", __func__);
|
|
resultQueue = nullptr;
|
|
// Don't use the queue onwards.
|
|
}
|
|
});
|
|
ASSERT_TRUE(resultQueueRet.isOk());
|
|
ASSERT_NE(nullptr, resultQueue);
|
|
|
|
ret = session->constructDefaultRequestSettings(RequestTemplate::PREVIEW,
|
|
[&](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
settings = req; });
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
::android::hardware::camera::common::V1_0::helper::CameraMetadata requestMeta;
|
|
StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
|
|
hidl_handle buffers[kBurstFrameCount];
|
|
StreamBuffer outputBuffers[kBurstFrameCount];
|
|
CaptureRequest requests[kBurstFrameCount];
|
|
InFlightRequest inflightReqs[kBurstFrameCount];
|
|
int32_t isoValues[kBurstFrameCount];
|
|
hidl_vec<uint8_t> requestSettings[kBurstFrameCount];
|
|
for (uint32_t i = 0; i < kBurstFrameCount; i++) {
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
|
|
isoValues[i] = ((i % 2) == 0) ? isoRange.data.i32[0] : isoRange.data.i32[1];
|
|
if (useHalBufManager) {
|
|
outputBuffers[i] = {halStreamConfig.streams[0].id, /*bufferId*/0,
|
|
nullptr, BufferStatus::OK, nullptr, nullptr};
|
|
} else {
|
|
allocateGraphicBuffer(previewStream.width, previewStream.height,
|
|
android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
|
|
halStreamConfig.streams[0].consumerUsage),
|
|
halStreamConfig.streams[0].overrideFormat, &buffers[i]);
|
|
outputBuffers[i] = {halStreamConfig.streams[0].id, bufferId + i,
|
|
buffers[i], BufferStatus::OK, nullptr, nullptr};
|
|
}
|
|
|
|
requestMeta.append(reinterpret_cast<camera_metadata_t *> (settings.data()));
|
|
|
|
// Disable all 3A routines
|
|
uint8_t mode = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF);
|
|
ASSERT_EQ(::android::OK, requestMeta.update(ANDROID_CONTROL_MODE, &mode, 1));
|
|
ASSERT_EQ(::android::OK, requestMeta.update(ANDROID_SENSOR_SENSITIVITY, &isoValues[i],
|
|
1));
|
|
camera_metadata_t *metaBuffer = requestMeta.release();
|
|
requestSettings[i].setToExternal(reinterpret_cast<uint8_t *> (metaBuffer),
|
|
get_camera_metadata_size(metaBuffer), true);
|
|
|
|
requests[i] = {frameNumber + i, 0 /* fmqSettingsSize */, requestSettings[i],
|
|
emptyInputBuffer, {outputBuffers[i]}};
|
|
|
|
inflightReqs[i] = {1, false, supportsPartialResults, partialResultCount, resultQueue};
|
|
mInflightMap.add(frameNumber + i, &inflightReqs[i]);
|
|
}
|
|
|
|
Status status = Status::INTERNAL_ERROR;
|
|
uint32_t numRequestProcessed = 0;
|
|
hidl_vec<BufferCache> cachesToRemove;
|
|
hidl_vec<CaptureRequest> burstRequest;
|
|
burstRequest.setToExternal(requests, kBurstFrameCount);
|
|
Return<void> returnStatus = session->processCaptureRequest(burstRequest, cachesToRemove,
|
|
[&status, &numRequestProcessed] (auto s, uint32_t n) {
|
|
status = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_EQ(numRequestProcessed, kBurstFrameCount);
|
|
|
|
for (size_t i = 0; i < kBurstFrameCount; i++) {
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (!inflightReqs[i].errorCodeValid && ((0 < inflightReqs[i].numBuffersLeft) ||
|
|
(!inflightReqs[i].haveResultMetadata))) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
|
|
}
|
|
|
|
ASSERT_FALSE(inflightReqs[i].errorCodeValid);
|
|
ASSERT_NE(inflightReqs[i].resultOutputBuffers.size(), 0u);
|
|
ASSERT_EQ(previewStream.id, inflightReqs[i].resultOutputBuffers[0].streamId);
|
|
ASSERT_FALSE(inflightReqs[i].collectedResult.isEmpty());
|
|
ASSERT_TRUE(inflightReqs[i].collectedResult.exists(ANDROID_SENSOR_SENSITIVITY));
|
|
camera_metadata_entry_t isoResult = inflightReqs[i].collectedResult.find(
|
|
ANDROID_SENSOR_SENSITIVITY);
|
|
ASSERT_TRUE(std::abs(isoResult.data.i32[0] - isoValues[i]) <=
|
|
std::round(isoValues[i]*isoTol));
|
|
}
|
|
|
|
if (useHalBufManager) {
|
|
verifyBuffersReturned(session, deviceVersion, previewStream.id, cb);
|
|
}
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Test whether an incorrect capture request with missing settings will
|
|
// be reported correctly.
|
|
TEST_F(CameraHidlTest, processCaptureRequestInvalidSinglePreview) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
uint64_t bufferId = 1;
|
|
uint32_t frameNumber = 1;
|
|
::android::hardware::hidl_vec<uint8_t> settings;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
V3_2::Stream previewStream;
|
|
HalStreamConfiguration halStreamConfig;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<DeviceCb> cb;
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
|
|
&previewStream /*out*/, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/,
|
|
&partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);
|
|
|
|
hidl_handle buffer_handle;
|
|
|
|
if (useHalBufManager) {
|
|
bufferId = 0;
|
|
} else {
|
|
allocateGraphicBuffer(previewStream.width, previewStream.height,
|
|
android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
|
|
halStreamConfig.streams[0].consumerUsage),
|
|
halStreamConfig.streams[0].overrideFormat, &buffer_handle);
|
|
}
|
|
|
|
StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
|
|
bufferId,
|
|
buffer_handle,
|
|
BufferStatus::OK,
|
|
nullptr,
|
|
nullptr};
|
|
::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {outputBuffer};
|
|
StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
|
|
nullptr};
|
|
CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
|
|
emptyInputBuffer, outputBuffers};
|
|
|
|
// Settings were not correctly initialized, we should fail here
|
|
Status status = Status::OK;
|
|
uint32_t numRequestProcessed = 0;
|
|
hidl_vec<BufferCache> cachesToRemove;
|
|
Return<void> ret = session->processCaptureRequest(
|
|
{request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
|
|
uint32_t n) {
|
|
status = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
|
|
ASSERT_EQ(numRequestProcessed, 0u);
|
|
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Check whether an invalid capture request with missing output buffers
|
|
// will be reported correctly.
|
|
TEST_F(CameraHidlTest, processCaptureRequestInvalidBuffer) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputBlobStreams;
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
uint32_t frameNumber = 1;
|
|
::android::hardware::hidl_vec<uint8_t> settings;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
V3_2::Stream previewStream;
|
|
HalStreamConfiguration halStreamConfig;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<DeviceCb> cb;
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
|
|
&previewStream /*out*/, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/,
|
|
&partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);
|
|
|
|
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
|
|
Return<void> ret;
|
|
ret = session->constructDefaultRequestSettings(reqTemplate,
|
|
[&](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
settings = req;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
::android::hardware::hidl_vec<StreamBuffer> emptyOutputBuffers;
|
|
StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr,
|
|
nullptr};
|
|
CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
|
|
emptyInputBuffer, emptyOutputBuffers};
|
|
|
|
// Output buffers are missing, we should fail here
|
|
Status status = Status::OK;
|
|
uint32_t numRequestProcessed = 0;
|
|
hidl_vec<BufferCache> cachesToRemove;
|
|
ret = session->processCaptureRequest(
|
|
{request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
|
|
uint32_t n) {
|
|
status = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
|
|
ASSERT_EQ(numRequestProcessed, 0u);
|
|
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Generate, trigger and flush a preview request
|
|
TEST_F(CameraHidlTest, flushPreviewRequest) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
uint64_t bufferId = 1;
|
|
uint32_t frameNumber = 1;
|
|
::android::hardware::hidl_vec<uint8_t> settings;
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
V3_2::Stream previewStream;
|
|
HalStreamConfiguration halStreamConfig;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<DeviceCb> cb;
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
|
|
&previewStream /*out*/, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/,
|
|
&partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);
|
|
|
|
std::shared_ptr<ResultMetadataQueue> resultQueue;
|
|
auto resultQueueRet =
|
|
session->getCaptureResultMetadataQueue(
|
|
[&resultQueue](const auto& descriptor) {
|
|
resultQueue = std::make_shared<ResultMetadataQueue>(
|
|
descriptor);
|
|
if (!resultQueue->isValid() ||
|
|
resultQueue->availableToWrite() <= 0) {
|
|
ALOGE("%s: HAL returns empty result metadata fmq,"
|
|
" not use it", __func__);
|
|
resultQueue = nullptr;
|
|
// Don't use the queue onwards.
|
|
}
|
|
});
|
|
ASSERT_TRUE(resultQueueRet.isOk());
|
|
|
|
InFlightRequest inflightReq = {1, false, supportsPartialResults,
|
|
partialResultCount, resultQueue};
|
|
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
|
|
Return<void> ret;
|
|
ret = session->constructDefaultRequestSettings(reqTemplate,
|
|
[&](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
settings = req;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
hidl_handle buffer_handle;
|
|
if (useHalBufManager) {
|
|
bufferId = 0;
|
|
} else {
|
|
allocateGraphicBuffer(previewStream.width, previewStream.height,
|
|
android_convertGralloc1To0Usage(halStreamConfig.streams[0].producerUsage,
|
|
halStreamConfig.streams[0].consumerUsage),
|
|
halStreamConfig.streams[0].overrideFormat, &buffer_handle);
|
|
}
|
|
|
|
StreamBuffer outputBuffer = {halStreamConfig.streams[0].id,
|
|
bufferId,
|
|
buffer_handle,
|
|
BufferStatus::OK,
|
|
nullptr,
|
|
nullptr};
|
|
::android::hardware::hidl_vec<StreamBuffer> outputBuffers = {outputBuffer};
|
|
const StreamBuffer emptyInputBuffer = {-1, 0, nullptr,
|
|
BufferStatus::ERROR, nullptr, nullptr};
|
|
CaptureRequest request = {frameNumber, 0 /* fmqSettingsSize */, settings,
|
|
emptyInputBuffer, outputBuffers};
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
mInflightMap.clear();
|
|
mInflightMap.add(frameNumber, &inflightReq);
|
|
}
|
|
|
|
Status status = Status::INTERNAL_ERROR;
|
|
uint32_t numRequestProcessed = 0;
|
|
hidl_vec<BufferCache> cachesToRemove;
|
|
ret = session->processCaptureRequest(
|
|
{request}, cachesToRemove, [&status, &numRequestProcessed](auto s,
|
|
uint32_t n) {
|
|
status = s;
|
|
numRequestProcessed = n;
|
|
});
|
|
|
|
ASSERT_TRUE(ret.isOk());
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_EQ(numRequestProcessed, 1u);
|
|
// Flush before waiting for request to complete.
|
|
Return<Status> returnStatus = session->flush();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
while (!inflightReq.errorCodeValid &&
|
|
((0 < inflightReq.numBuffersLeft) ||
|
|
(!inflightReq.haveResultMetadata))) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l,
|
|
timeout));
|
|
}
|
|
|
|
if (!inflightReq.errorCodeValid) {
|
|
ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
|
|
ASSERT_EQ(previewStream.id, inflightReq.resultOutputBuffers[0].streamId);
|
|
} else {
|
|
switch (inflightReq.errorCode) {
|
|
case ErrorCode::ERROR_REQUEST:
|
|
case ErrorCode::ERROR_RESULT:
|
|
case ErrorCode::ERROR_BUFFER:
|
|
// Expected
|
|
break;
|
|
case ErrorCode::ERROR_DEVICE:
|
|
default:
|
|
FAIL() << "Unexpected error:"
|
|
<< static_cast<uint32_t>(inflightReq.errorCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (useHalBufManager) {
|
|
verifyBuffersReturned(session, deviceVersion, previewStream.id, cb);
|
|
}
|
|
|
|
ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Verify that camera flushes correctly without any pending requests.
|
|
TEST_F(CameraHidlTest, flushEmpty) {
|
|
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
|
|
for (const auto& name : cameraDeviceNames) {
|
|
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
|
|
continue;
|
|
} else if (deviceVersion <= 0) {
|
|
ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
|
|
ADD_FAILURE();
|
|
return;
|
|
}
|
|
|
|
V3_2::Stream previewStream;
|
|
HalStreamConfiguration halStreamConfig;
|
|
sp<ICameraDeviceSession> session;
|
|
sp<DeviceCb> cb;
|
|
bool supportsPartialResults = false;
|
|
bool useHalBufManager = false;
|
|
uint32_t partialResultCount = 0;
|
|
configurePreviewStream(name, deviceVersion, mProvider, &previewThreshold, &session /*out*/,
|
|
&previewStream /*out*/, &halStreamConfig /*out*/,
|
|
&supportsPartialResults /*out*/,
|
|
&partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);
|
|
|
|
Return<Status> returnStatus = session->flush();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> l(mLock);
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::milliseconds(kEmptyFlushTimeoutMSec);
|
|
ASSERT_EQ(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
|
|
}
|
|
|
|
Return<void> ret = session->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Test camera provider@2.5 notify method
|
|
TEST_F(CameraHidlTest, providerDeviceStateNotification) {
|
|
|
|
notifyDeviceState(provider::V2_5::DeviceState::BACK_COVERED);
|
|
notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
|
|
}
|
|
|
|
// Retrieve all valid output stream resolutions from the camera
|
|
// static characteristics.
|
|
Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta,
|
|
std::vector<AvailableStream> &outputStreams,
|
|
const AvailableStream *threshold) {
|
|
AvailableStream depthPreviewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
|
|
static_cast<int32_t>(PixelFormat::Y16)};
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry scalarEntry;
|
|
camera_metadata_ro_entry depthEntry;
|
|
int foundScalar = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &scalarEntry);
|
|
int foundDepth = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, &depthEntry);
|
|
if ((0 != foundScalar || (0 != (scalarEntry.count % 4))) &&
|
|
(0 != foundDepth || (0 != (depthEntry.count % 4)))) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
if(foundScalar == 0 && (0 == (scalarEntry.count % 4))) {
|
|
fillOutputStreams(&scalarEntry, outputStreams, threshold,
|
|
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
|
|
}
|
|
|
|
if(foundDepth == 0 && (0 == (depthEntry.count % 4))) {
|
|
fillOutputStreams(&depthEntry, outputStreams, &depthPreviewThreshold,
|
|
ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT);
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
void CameraHidlTest::fillOutputStreams(camera_metadata_ro_entry_t* entry,
|
|
std::vector<AvailableStream>& outputStreams, const AvailableStream* threshold,
|
|
const int32_t availableConfigOutputTag) {
|
|
for (size_t i = 0; i < entry->count; i+=4) {
|
|
if (availableConfigOutputTag == entry->data.i32[i + 3]) {
|
|
if(nullptr == threshold) {
|
|
AvailableStream s = {entry->data.i32[i+1],
|
|
entry->data.i32[i+2], entry->data.i32[i]};
|
|
outputStreams.push_back(s);
|
|
} else {
|
|
if ((threshold->format == entry->data.i32[i]) &&
|
|
(threshold->width >= entry->data.i32[i+1]) &&
|
|
(threshold->height >= entry->data.i32[i+2])) {
|
|
AvailableStream s = {entry->data.i32[i+1],
|
|
entry->data.i32[i+2], threshold->format};
|
|
outputStreams.push_back(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get max jpeg buffer size in android.jpeg.maxSize
|
|
Status CameraHidlTest::getJpegBufferSize(camera_metadata_t *staticMeta, uint32_t* outBufSize) {
|
|
if (nullptr == staticMeta || nullptr == outBufSize) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_JPEG_MAX_SIZE, &entry);
|
|
if ((0 != rc) || (1 != entry.count)) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
*outBufSize = static_cast<uint32_t>(entry.data.i32[0]);
|
|
return Status::OK;
|
|
}
|
|
|
|
// Check if the camera device has logical multi-camera capability.
|
|
Status CameraHidlTest::isLogicalMultiCamera(const camera_metadata_t *staticMeta) {
|
|
Status ret = Status::METHOD_NOT_SUPPORTED;
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
|
if (0 != rc) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA == entry.data.u8[i]) {
|
|
ret = Status::OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Generate a list of physical camera ids backing a logical multi-camera.
|
|
Status CameraHidlTest::getPhysicalCameraIds(const camera_metadata_t *staticMeta,
|
|
std::unordered_set<std::string> *physicalIds) {
|
|
if ((nullptr == staticMeta) || (nullptr == physicalIds)) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS,
|
|
&entry);
|
|
if (0 != rc) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
const uint8_t* ids = entry.data.u8;
|
|
size_t start = 0;
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
if (ids[i] == '\0') {
|
|
if (start != i) {
|
|
std::string currentId(reinterpret_cast<const char *> (ids + start));
|
|
physicalIds->emplace(currentId);
|
|
}
|
|
start = i + 1;
|
|
}
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
// Generate a set of suported camera key ids.
|
|
Status CameraHidlTest::getSupportedKeys(camera_metadata_t *staticMeta,
|
|
uint32_t tagId, std::unordered_set<int32_t> *requestIDs) {
|
|
if ((nullptr == staticMeta) || (nullptr == requestIDs)) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta, tagId, &entry);
|
|
if ((0 != rc) || (entry.count == 0)) {
|
|
return Status::OK;
|
|
}
|
|
|
|
requestIDs->insert(entry.data.i32, entry.data.i32 + entry.count);
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
void CameraHidlTest::constructFilteredSettings(const sp<ICameraDeviceSession>& session,
|
|
const std::unordered_set<int32_t>& availableKeys, RequestTemplate reqTemplate,
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata* defaultSettings,
|
|
android::hardware::camera::common::V1_0::helper::CameraMetadata* filteredSettings) {
|
|
ASSERT_NE(defaultSettings, nullptr);
|
|
ASSERT_NE(filteredSettings, nullptr);
|
|
|
|
auto ret = session->constructDefaultRequestSettings(reqTemplate,
|
|
[&defaultSettings] (auto status, const auto& req) mutable {
|
|
ASSERT_EQ(Status::OK, status);
|
|
|
|
const camera_metadata_t *metadata = reinterpret_cast<const camera_metadata_t*> (
|
|
req.data());
|
|
size_t expectedSize = req.size();
|
|
int result = validate_camera_metadata_structure(metadata, &expectedSize);
|
|
ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));
|
|
|
|
size_t entryCount = get_camera_metadata_entry_count(metadata);
|
|
ASSERT_GT(entryCount, 0u);
|
|
*defaultSettings = metadata;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
const android::hardware::camera::common::V1_0::helper::CameraMetadata &constSettings =
|
|
*defaultSettings;
|
|
for (const auto& keyIt : availableKeys) {
|
|
camera_metadata_ro_entry entry = constSettings.find(keyIt);
|
|
if (entry.count > 0) {
|
|
filteredSettings->update(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if constrained mode is supported by using the static
|
|
// camera characteristics.
|
|
Status CameraHidlTest::isConstrainedModeAvailable(camera_metadata_t *staticMeta) {
|
|
Status ret = Status::METHOD_NOT_SUPPORTED;
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
|
if (0 != rc) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO ==
|
|
entry.data.u8[i]) {
|
|
ret = Status::OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Pick the largest supported HFR mode from the static camera
|
|
// characteristics.
|
|
Status CameraHidlTest::pickConstrainedModeSize(camera_metadata_t *staticMeta,
|
|
AvailableStream &hfrStream) {
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS, &entry);
|
|
if (0 != rc) {
|
|
return Status::METHOD_NOT_SUPPORTED;
|
|
} else if (0 != (entry.count % 5)) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
hfrStream = {0, 0,
|
|
static_cast<uint32_t>(PixelFormat::IMPLEMENTATION_DEFINED)};
|
|
for (size_t i = 0; i < entry.count; i+=5) {
|
|
int32_t w = entry.data.i32[i];
|
|
int32_t h = entry.data.i32[i+1];
|
|
if ((hfrStream.width * hfrStream.height) < (w *h)) {
|
|
hfrStream.width = w;
|
|
hfrStream.height = h;
|
|
}
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
// Check whether ZSL is available using the static camera
|
|
// characteristics.
|
|
Status CameraHidlTest::isZSLModeAvailable(const camera_metadata_t *staticMeta) {
|
|
if (Status::OK == isZSLModeAvailable(staticMeta, PRIV_REPROCESS)) {
|
|
return Status::OK;
|
|
} else {
|
|
return isZSLModeAvailable(staticMeta, YUV_REPROCESS);
|
|
}
|
|
}
|
|
|
|
Status CameraHidlTest::isZSLModeAvailable(const camera_metadata_t *staticMeta,
|
|
ReprocessType reprocType) {
|
|
|
|
Status ret = Status::METHOD_NOT_SUPPORTED;
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
|
if (0 != rc) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
if ((reprocType == PRIV_REPROCESS &&
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING == entry.data.u8[i]) ||
|
|
(reprocType == YUV_REPROCESS &&
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING == entry.data.u8[i])) {
|
|
ret = Status::OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Check whether this is a monochrome camera using the static camera characteristics.
|
|
Status CameraHidlTest::isMonochromeCamera(const camera_metadata_t *staticMeta) {
|
|
Status ret = Status::METHOD_NOT_SUPPORTED;
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
|
if (0 != rc) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME == entry.data.u8[i]) {
|
|
ret = Status::OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Retrieve the reprocess input-output format map from the static
|
|
// camera characteristics.
|
|
Status CameraHidlTest::getZSLInputOutputMap(camera_metadata_t *staticMeta,
|
|
std::vector<AvailableZSLInputOutput> &inputOutputMap) {
|
|
if (nullptr == staticMeta) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP, &entry);
|
|
if ((0 != rc) || (0 >= entry.count)) {
|
|
return Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
const int32_t* contents = &entry.data.i32[0];
|
|
for (size_t i = 0; i < entry.count; ) {
|
|
int32_t inputFormat = contents[i++];
|
|
int32_t length = contents[i++];
|
|
for (int32_t j = 0; j < length; j++) {
|
|
int32_t outputFormat = contents[i+j];
|
|
AvailableZSLInputOutput zslEntry = {inputFormat, outputFormat};
|
|
inputOutputMap.push_back(zslEntry);
|
|
}
|
|
i += length;
|
|
}
|
|
|
|
return Status::OK;
|
|
}
|
|
|
|
// Search for the largest stream size for a given format.
|
|
Status CameraHidlTest::findLargestSize(
|
|
const std::vector<AvailableStream> &streamSizes, int32_t format,
|
|
AvailableStream &result) {
|
|
result = {0, 0, 0};
|
|
for (auto &iter : streamSizes) {
|
|
if (format == iter.format) {
|
|
if ((result.width * result.height) < (iter.width * iter.height)) {
|
|
result = iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (result.format == format) ? Status::OK : Status::ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
// Check whether the camera device supports specific focus mode.
|
|
Status CameraHidlTest::isAutoFocusModeAvailable(
|
|
CameraParameters &cameraParams,
|
|
const char *mode) {
|
|
::android::String8 focusModes(cameraParams.get(
|
|
CameraParameters::KEY_SUPPORTED_FOCUS_MODES));
|
|
if (focusModes.contains(mode)) {
|
|
return Status::OK;
|
|
}
|
|
|
|
return Status::METHOD_NOT_SUPPORTED;
|
|
}
|
|
|
|
void CameraHidlTest::createStreamConfiguration(
|
|
const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
|
|
StreamConfigurationMode configMode,
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2 /*out*/,
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration *config3_4 /*out*/,
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration *config3_5 /*out*/,
|
|
uint32_t jpegBufferSize) {
|
|
ASSERT_NE(nullptr, config3_2);
|
|
ASSERT_NE(nullptr, config3_4);
|
|
ASSERT_NE(nullptr, config3_5);
|
|
|
|
::android::hardware::hidl_vec<V3_4::Stream> streams3_4(streams3_2.size());
|
|
size_t idx = 0;
|
|
for (auto& stream3_2 : streams3_2) {
|
|
V3_4::Stream stream;
|
|
stream.v3_2 = stream3_2;
|
|
stream.bufferSize = 0;
|
|
if (stream3_2.format == PixelFormat::BLOB &&
|
|
stream3_2.dataSpace == static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF)) {
|
|
stream.bufferSize = jpegBufferSize;
|
|
}
|
|
streams3_4[idx++] = stream;
|
|
}
|
|
// Caller is responsible to fill in non-zero config3_5->streamConfigCounter after this returns
|
|
*config3_5 = {{streams3_4, configMode, {}}, 0};
|
|
*config3_4 = config3_5->v3_4;
|
|
*config3_2 = {streams3_2, configMode};
|
|
}
|
|
|
|
// Configure multiple preview streams using different physical ids.
|
|
void CameraHidlTest::configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
|
|
sp<ICameraProvider> provider,
|
|
const AvailableStream *previewThreshold,
|
|
const std::unordered_set<std::string>& physicalIds,
|
|
sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
|
|
sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
|
|
V3_2::Stream *previewStream /*out*/,
|
|
device::V3_4::HalStreamConfiguration *halStreamConfig /*out*/,
|
|
bool *supportsPartialResults /*out*/,
|
|
uint32_t *partialResultCount /*out*/,
|
|
bool *useHalBufManager /*out*/,
|
|
sp<DeviceCb> *outCb /*out*/,
|
|
uint32_t streamConfigCounter,
|
|
bool allowUnsupport) {
|
|
ASSERT_NE(nullptr, session3_4);
|
|
ASSERT_NE(nullptr, session3_5);
|
|
ASSERT_NE(nullptr, halStreamConfig);
|
|
ASSERT_NE(nullptr, previewStream);
|
|
ASSERT_NE(nullptr, supportsPartialResults);
|
|
ASSERT_NE(nullptr, partialResultCount);
|
|
ASSERT_NE(nullptr, useHalBufManager);
|
|
ASSERT_NE(nullptr, outCb);
|
|
ASSERT_FALSE(physicalIds.empty());
|
|
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
::android::sp<ICameraDevice> device3_x;
|
|
ALOGI("configureStreams: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = provider->getCameraDeviceInterface_V3_x(
|
|
name,
|
|
[&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d",
|
|
(int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
camera_metadata_t *staticMeta;
|
|
ret = device3_x->getCameraCharacteristics([&] (Status s,
|
|
CameraMetadata metadata) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
staticMeta = clone_camera_metadata(
|
|
reinterpret_cast<const camera_metadata_t*>(metadata.data()));
|
|
ASSERT_NE(nullptr, staticMeta);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
camera_metadata_ro_entry entry;
|
|
auto status = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
|
|
if ((0 == status) && (entry.count > 0)) {
|
|
*partialResultCount = entry.data.i32[0];
|
|
*supportsPartialResults = (*partialResultCount > 1);
|
|
}
|
|
|
|
sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
|
|
sp<ICameraDeviceSession> session;
|
|
ret = device3_x->open(
|
|
cb,
|
|
[&session](auto status, const auto& newSession) {
|
|
ALOGI("device::open returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(newSession, nullptr);
|
|
session = newSession;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
*outCb = cb;
|
|
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
castSession(session, deviceVersion, &session3_3, session3_4, session3_5);
|
|
ASSERT_NE(nullptr, (*session3_4).get());
|
|
|
|
*useHalBufManager = false;
|
|
status = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
|
|
if ((0 == status) && (entry.count == 1)) {
|
|
*useHalBufManager = (entry.data.u8[0] ==
|
|
ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
|
|
}
|
|
|
|
outputPreviewStreams.clear();
|
|
auto rc = getAvailableOutputStreams(staticMeta,
|
|
outputPreviewStreams, previewThreshold);
|
|
free_camera_metadata(staticMeta);
|
|
ASSERT_EQ(Status::OK, rc);
|
|
ASSERT_FALSE(outputPreviewStreams.empty());
|
|
|
|
::android::hardware::hidl_vec<V3_4::Stream> streams3_4(physicalIds.size());
|
|
int32_t streamId = 0;
|
|
for (auto const& physicalId : physicalIds) {
|
|
V3_4::Stream stream3_4 = {{streamId, StreamType::OUTPUT,
|
|
static_cast<uint32_t> (outputPreviewStreams[0].width),
|
|
static_cast<uint32_t> (outputPreviewStreams[0].height),
|
|
static_cast<PixelFormat> (outputPreviewStreams[0].format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, 0, StreamRotation::ROTATION_0},
|
|
physicalId.c_str(), /*bufferSize*/ 0};
|
|
streams3_4[streamId++] = stream3_4;
|
|
}
|
|
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
config3_4 = {streams3_4, StreamConfigurationMode::NORMAL_MODE, {}};
|
|
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
|
|
ret = (*session3_4)->constructDefaultRequestSettings(reqTemplate,
|
|
[&config3_4](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
config3_4.sessionParams = req;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ASSERT_TRUE(!allowUnsupport || deviceVersion == CAMERA_DEVICE_API_VERSION_3_5);
|
|
if (allowUnsupport) {
|
|
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
|
|
castDevice(device3_x, deviceVersion, &cameraDevice3_5);
|
|
|
|
bool supported = false;
|
|
ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
|
|
[&supported](Status s, bool combStatus) {
|
|
ASSERT_TRUE((Status::OK == s) ||
|
|
(Status::METHOD_NOT_SUPPORTED == s));
|
|
if (Status::OK == s) {
|
|
supported = combStatus;
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
// If stream combination is not supported, return null session.
|
|
if (!supported) {
|
|
*session3_5 = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (*session3_5 != nullptr) {
|
|
config3_5.v3_4 = config3_4;
|
|
config3_5.streamConfigCounter = streamConfigCounter;
|
|
ret = (*session3_5)->configureStreams_3_5(config3_5,
|
|
[&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(physicalIds.size(), halConfig.streams.size());
|
|
*halStreamConfig = halConfig;
|
|
if (*useHalBufManager) {
|
|
hidl_vec<V3_2::Stream> streams(physicalIds.size());
|
|
hidl_vec<V3_2::HalStream> halStreams(physicalIds.size());
|
|
for (size_t i = 0; i < physicalIds.size(); i++) {
|
|
streams[i] = streams3_4[i].v3_2;
|
|
halStreams[i] = halConfig.streams[i].v3_3.v3_2;
|
|
}
|
|
cb->setCurrentStreamConfig(streams, halStreams);
|
|
}
|
|
});
|
|
} else {
|
|
ret = (*session3_4)->configureStreams_3_4(config3_4,
|
|
[&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(physicalIds.size(), halConfig.streams.size());
|
|
*halStreamConfig = halConfig;
|
|
});
|
|
}
|
|
*previewStream = streams3_4[0].v3_2;
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
bool CameraHidlTest::isDepthOnly(camera_metadata_t* staticMeta) {
|
|
camera_metadata_ro_entry scalarEntry;
|
|
camera_metadata_ro_entry depthEntry;
|
|
|
|
int rc = find_camera_metadata_ro_entry(
|
|
staticMeta, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &scalarEntry);
|
|
if (rc == 0) {
|
|
for (uint32_t i = 0; i < scalarEntry.count; i++) {
|
|
if (scalarEntry.data.u8[i] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < scalarEntry.count; i++) {
|
|
if (scalarEntry.data.u8[i] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) {
|
|
|
|
rc = find_camera_metadata_ro_entry(
|
|
staticMeta, ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, &depthEntry);
|
|
size_t i = 0;
|
|
if (rc == 0 && depthEntry.data.i32[i] == static_cast<int32_t>(PixelFormat::Y16)) {
|
|
// only Depth16 format is supported now
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Open a device session and configure a preview stream.
|
|
void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t deviceVersion,
|
|
sp<ICameraProvider> provider,
|
|
const AvailableStream *previewThreshold,
|
|
sp<ICameraDeviceSession> *session /*out*/,
|
|
V3_2::Stream *previewStream /*out*/,
|
|
HalStreamConfiguration *halStreamConfig /*out*/,
|
|
bool *supportsPartialResults /*out*/,
|
|
uint32_t *partialResultCount /*out*/,
|
|
bool *useHalBufManager /*out*/,
|
|
sp<DeviceCb> *outCb /*out*/,
|
|
uint32_t streamConfigCounter) {
|
|
ASSERT_NE(nullptr, session);
|
|
ASSERT_NE(nullptr, previewStream);
|
|
ASSERT_NE(nullptr, halStreamConfig);
|
|
ASSERT_NE(nullptr, supportsPartialResults);
|
|
ASSERT_NE(nullptr, partialResultCount);
|
|
ASSERT_NE(nullptr, useHalBufManager);
|
|
ASSERT_NE(nullptr, outCb);
|
|
|
|
std::vector<AvailableStream> outputPreviewStreams;
|
|
::android::sp<ICameraDevice> device3_x;
|
|
ALOGI("configureStreams: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = provider->getCameraDeviceInterface_V3_x(
|
|
name,
|
|
[&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d",
|
|
(int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
camera_metadata_t *staticMeta;
|
|
ret = device3_x->getCameraCharacteristics([&] (Status s,
|
|
CameraMetadata metadata) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
staticMeta = clone_camera_metadata(
|
|
reinterpret_cast<const camera_metadata_t*>(metadata.data()));
|
|
ASSERT_NE(nullptr, staticMeta);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
camera_metadata_ro_entry entry;
|
|
auto status = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
|
|
if ((0 == status) && (entry.count > 0)) {
|
|
*partialResultCount = entry.data.i32[0];
|
|
*supportsPartialResults = (*partialResultCount > 1);
|
|
}
|
|
|
|
sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
|
|
ret = device3_x->open(
|
|
cb,
|
|
[&](auto status, const auto& newSession) {
|
|
ALOGI("device::open returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(newSession, nullptr);
|
|
*session = newSession;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
*outCb = cb;
|
|
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
castSession(*session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
|
|
*useHalBufManager = false;
|
|
status = find_camera_metadata_ro_entry(staticMeta,
|
|
ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
|
|
if ((0 == status) && (entry.count == 1)) {
|
|
*useHalBufManager = (entry.data.u8[0] ==
|
|
ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
|
|
}
|
|
|
|
outputPreviewStreams.clear();
|
|
auto rc = getAvailableOutputStreams(staticMeta,
|
|
outputPreviewStreams, previewThreshold);
|
|
|
|
uint32_t jpegBufferSize = 0;
|
|
ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize));
|
|
ASSERT_NE(0u, jpegBufferSize);
|
|
|
|
free_camera_metadata(staticMeta);
|
|
ASSERT_EQ(Status::OK, rc);
|
|
ASSERT_FALSE(outputPreviewStreams.empty());
|
|
|
|
V3_2::DataspaceFlags dataspaceFlag = 0;
|
|
switch (static_cast<PixelFormat>(outputPreviewStreams[0].format)) {
|
|
case PixelFormat::Y16:
|
|
dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
|
|
break;
|
|
default:
|
|
dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
|
|
}
|
|
|
|
V3_2::Stream stream3_2 = {0, StreamType::OUTPUT,
|
|
static_cast<uint32_t> (outputPreviewStreams[0].width),
|
|
static_cast<uint32_t> (outputPreviewStreams[0].height),
|
|
static_cast<PixelFormat> (outputPreviewStreams[0].format),
|
|
GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, dataspaceFlag, StreamRotation::ROTATION_0};
|
|
::android::hardware::hidl_vec<V3_2::Stream> streams3_2 = {stream3_2};
|
|
::android::hardware::camera::device::V3_2::StreamConfiguration config3_2;
|
|
::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
|
|
::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
|
|
createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
|
|
&config3_2, &config3_4, &config3_5, jpegBufferSize);
|
|
if (session3_5 != nullptr) {
|
|
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
|
|
ret = session3_5->constructDefaultRequestSettings(reqTemplate,
|
|
[&config3_5](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
config3_5.v3_4.sessionParams = req;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
config3_5.streamConfigCounter = streamConfigCounter;
|
|
ret = session3_5->configureStreams_3_5(config3_5,
|
|
[&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
halStreamConfig->streams.resize(1);
|
|
halStreamConfig->streams[0] = halConfig.streams[0].v3_3.v3_2;
|
|
if (*useHalBufManager) {
|
|
hidl_vec<V3_2::Stream> streams(1);
|
|
hidl_vec<V3_2::HalStream> halStreams(1);
|
|
streams[0] = stream3_2;
|
|
halStreams[0] = halConfig.streams[0].v3_3.v3_2;
|
|
cb->setCurrentStreamConfig(streams, halStreams);
|
|
}
|
|
});
|
|
} else if (session3_4 != nullptr) {
|
|
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
|
|
ret = session3_4->constructDefaultRequestSettings(reqTemplate,
|
|
[&config3_4](auto status, const auto& req) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
config3_4.sessionParams = req;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
ret = session3_4->configureStreams_3_4(config3_4,
|
|
[&] (Status s, device::V3_4::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
halStreamConfig->streams.resize(halConfig.streams.size());
|
|
for (size_t i = 0; i < halConfig.streams.size(); i++) {
|
|
halStreamConfig->streams[i] = halConfig.streams[i].v3_3.v3_2;
|
|
}
|
|
});
|
|
} else if (session3_3 != nullptr) {
|
|
ret = session3_3->configureStreams_3_3(config3_2,
|
|
[&] (Status s, device::V3_3::HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
halStreamConfig->streams.resize(halConfig.streams.size());
|
|
for (size_t i = 0; i < halConfig.streams.size(); i++) {
|
|
halStreamConfig->streams[i] = halConfig.streams[i].v3_2;
|
|
}
|
|
});
|
|
} else {
|
|
ret = (*session)->configureStreams(config3_2,
|
|
[&] (Status s, HalStreamConfiguration halConfig) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
ASSERT_EQ(1u, halConfig.streams.size());
|
|
*halStreamConfig = halConfig;
|
|
});
|
|
}
|
|
*previewStream = stream3_2;
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice> &device,
|
|
int32_t deviceVersion, sp<device::V3_5::ICameraDevice> *device3_5/*out*/) {
|
|
ASSERT_NE(nullptr, device3_5);
|
|
if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
|
|
auto castResult = device::V3_5::ICameraDevice::castFrom(device);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
*device3_5 = castResult;
|
|
}
|
|
}
|
|
|
|
//Cast camera provider to corresponding version if available
|
|
void CameraHidlTest::castProvider(const sp<ICameraProvider> &provider,
|
|
sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/) {
|
|
ASSERT_NE(nullptr, provider2_5);
|
|
auto castResult = provider::V2_5::ICameraProvider::castFrom(provider);
|
|
if (castResult.isOk()) {
|
|
*provider2_5 = castResult;
|
|
}
|
|
}
|
|
|
|
//Cast camera device session to corresponding version
|
|
void CameraHidlTest::castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
|
|
sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
|
|
sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
|
|
sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/) {
|
|
ASSERT_NE(nullptr, session3_3);
|
|
ASSERT_NE(nullptr, session3_4);
|
|
ASSERT_NE(nullptr, session3_5);
|
|
|
|
switch (deviceVersion) {
|
|
case CAMERA_DEVICE_API_VERSION_3_5: {
|
|
auto castResult = device::V3_5::ICameraDeviceSession::castFrom(session);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
*session3_5 = castResult;
|
|
}
|
|
[[fallthrough]];
|
|
case CAMERA_DEVICE_API_VERSION_3_4: {
|
|
auto castResult = device::V3_4::ICameraDeviceSession::castFrom(session);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
*session3_4 = castResult;
|
|
}
|
|
[[fallthrough]];
|
|
case CAMERA_DEVICE_API_VERSION_3_3: {
|
|
auto castResult = device::V3_3::ICameraDeviceSession::castFrom(session);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
*session3_3 = castResult;
|
|
break;
|
|
}
|
|
default:
|
|
//no-op
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
|
|
const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
|
|
bool expectedStatus, bool expectMethodSupported) {
|
|
if (cameraDevice3_5.get() != nullptr) {
|
|
auto ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
|
|
[expectedStatus, expectMethodSupported] (Status s, bool combStatus) {
|
|
ASSERT_TRUE((Status::OK == s) ||
|
|
(!expectMethodSupported && Status::METHOD_NOT_SUPPORTED == s));
|
|
if (Status::OK == s) {
|
|
ASSERT_TRUE(combStatus == expectedStatus);
|
|
}
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
// Verify logical camera static metadata
|
|
void CameraHidlTest::verifyLogicalCameraMetadata(const std::string& cameraName,
|
|
const ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice>& device,
|
|
const CameraMetadata &chars, int deviceVersion,
|
|
const hidl_vec<hidl_string>& deviceNames) {
|
|
const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
|
|
ASSERT_NE(nullptr, metadata);
|
|
|
|
Status rc = isLogicalMultiCamera(metadata);
|
|
ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
|
|
if (Status::METHOD_NOT_SUPPORTED == rc) {
|
|
return;
|
|
}
|
|
|
|
std::string version, cameraId;
|
|
ASSERT_TRUE(::matchDeviceName(cameraName, mProviderType, &version, &cameraId));
|
|
std::unordered_set<std::string> physicalIds;
|
|
ASSERT_TRUE(Status::OK == getPhysicalCameraIds(metadata, &physicalIds));
|
|
for (auto physicalId : physicalIds) {
|
|
ASSERT_NE(physicalId, cameraId);
|
|
bool isPublicId = false;
|
|
for (auto& deviceName : deviceNames) {
|
|
std::string publicVersion, publicId;
|
|
ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, &publicVersion, &publicId));
|
|
if (physicalId == publicId) {
|
|
isPublicId = true;
|
|
break;
|
|
}
|
|
}
|
|
if (isPublicId) {
|
|
continue;
|
|
}
|
|
|
|
ASSERT_TRUE(deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5);
|
|
auto castResult = device::V3_5::ICameraDevice::castFrom(device);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
::android::sp<::android::hardware::camera::device::V3_5::ICameraDevice> device3_5 =
|
|
castResult;
|
|
ASSERT_NE(device3_5, nullptr);
|
|
|
|
// Check camera characteristics for hidden camera id
|
|
Return<void> ret = device3_5->getPhysicalCameraCharacteristics(physicalId,
|
|
[&](auto status, const auto& chars) {
|
|
verifyCameraCharacteristics(status, chars);
|
|
verifyMonochromeCharacteristics(chars, deviceVersion);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
// Check calling getCameraDeviceInterface_V3_x() on hidden camera id returns
|
|
// ILLEGAL_ARGUMENT.
|
|
std::stringstream s;
|
|
s << "device@" << version << "/" << mProviderType << "/" << physicalId;
|
|
hidl_string fullPhysicalId(s.str());
|
|
ret = mProvider->getCameraDeviceInterface_V3_x(fullPhysicalId,
|
|
[&](auto status, const auto& device3_x) {
|
|
ASSERT_EQ(Status::ILLEGAL_ARGUMENT, status);
|
|
ASSERT_EQ(device3_x, nullptr);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
// Make sure ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID is available in
|
|
// result keys.
|
|
if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
|
|
camera_metadata_ro_entry entry;
|
|
int retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
ASSERT_NE(std::find(entry.data.i32, entry.data.i32 + entry.count,
|
|
static_cast<int32_t>(
|
|
CameraMetadataTag::ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID)),
|
|
entry.data.i32 + entry.count);
|
|
} else {
|
|
ADD_FAILURE() << "Get camera availableResultKeys failed!";
|
|
}
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifyCameraCharacteristics(Status status, const CameraMetadata& chars) {
|
|
ASSERT_EQ(Status::OK, status);
|
|
const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
|
|
size_t expectedSize = chars.size();
|
|
int result = validate_camera_metadata_structure(metadata, &expectedSize);
|
|
ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));
|
|
size_t entryCount = get_camera_metadata_entry_count(metadata);
|
|
// TODO: we can do better than 0 here. Need to check how many required
|
|
// characteristics keys we've defined.
|
|
ASSERT_GT(entryCount, 0u);
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
uint8_t hardwareLevel = entry.data.u8[0];
|
|
ASSERT_TRUE(
|
|
hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED ||
|
|
hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
|
|
hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3 ||
|
|
hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
|
|
} else {
|
|
ADD_FAILURE() << "Get camera hardware level failed!";
|
|
}
|
|
|
|
entry.count = 0;
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION, &entry);
|
|
if ((0 == retcode) || (entry.count > 0)) {
|
|
ADD_FAILURE() << "ANDROID_REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION "
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS, &entry);
|
|
if ((0 == retcode) || (entry.count > 0)) {
|
|
ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS"
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS, &entry);
|
|
if ((0 == retcode) || (entry.count > 0)) {
|
|
ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS"
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS, &entry);
|
|
if ((0 == retcode) || (entry.count > 0)) {
|
|
ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS"
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, &entry);
|
|
if (0 == retcode || entry.count > 0) {
|
|
ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS "
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS, &entry);
|
|
if (0 == retcode || entry.count > 0) {
|
|
ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS "
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS, &entry);
|
|
if (0 == retcode || entry.count > 0) {
|
|
ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS "
|
|
<< " per API contract should never be set by Hal!";
|
|
}
|
|
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_HEIC_INFO_SUPPORTED, &entry);
|
|
if (0 == retcode && entry.count > 0) {
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT, &entry);
|
|
if (0 == retcode && entry.count > 0) {
|
|
uint8_t maxJpegAppSegmentsCount = entry.data.u8[0];
|
|
ASSERT_TRUE(maxJpegAppSegmentsCount >= 1 &&
|
|
maxJpegAppSegmentsCount <= 16);
|
|
} else {
|
|
ADD_FAILURE() << "Get Heic maxJpegAppSegmentsCount failed!";
|
|
}
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifyMonochromeCharacteristics(const CameraMetadata& chars,
|
|
int deviceVersion) {
|
|
const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
|
|
Status rc = isMonochromeCamera(metadata);
|
|
if (Status::METHOD_NOT_SUPPORTED == rc) {
|
|
return;
|
|
}
|
|
ASSERT_EQ(Status::OK, rc);
|
|
|
|
camera_metadata_ro_entry entry;
|
|
// Check capabilities
|
|
int retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
ASSERT_EQ(std::find(entry.data.u8, entry.data.u8 + entry.count,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING),
|
|
entry.data.u8 + entry.count);
|
|
if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_5) {
|
|
ASSERT_EQ(std::find(entry.data.u8, entry.data.u8 + entry.count,
|
|
ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW),
|
|
entry.data.u8 + entry.count);
|
|
}
|
|
}
|
|
|
|
if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_5) {
|
|
// Check Cfa
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, &entry);
|
|
if ((0 == retcode) && (entry.count == 1)) {
|
|
ASSERT_TRUE(entry.data.i32[0] == static_cast<int32_t>(
|
|
CameraMetadataEnumAndroidSensorInfoColorFilterArrangement::ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO)
|
|
|| entry.data.i32[0] == static_cast<int32_t>(
|
|
CameraMetadataEnumAndroidSensorInfoColorFilterArrangement::ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR));
|
|
}
|
|
|
|
// Check availableRequestKeys
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_MODE);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_TRANSFORM);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_GAINS);
|
|
}
|
|
} else {
|
|
ADD_FAILURE() << "Get camera availableRequestKeys failed!";
|
|
}
|
|
|
|
// Check availableResultKeys
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_GREEN_SPLIT);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_MODE);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_TRANSFORM);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_COLOR_CORRECTION_GAINS);
|
|
}
|
|
} else {
|
|
ADD_FAILURE() << "Get camera availableResultKeys failed!";
|
|
}
|
|
|
|
// Check availableCharacteristicKeys
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
for (size_t i = 0; i < entry.count; i++) {
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_COLOR_TRANSFORM1);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_COLOR_TRANSFORM2);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_FORWARD_MATRIX1);
|
|
ASSERT_NE(entry.data.i32[i], ANDROID_SENSOR_FORWARD_MATRIX2);
|
|
}
|
|
} else {
|
|
ADD_FAILURE() << "Get camera availableResultKeys failed!";
|
|
}
|
|
|
|
// Check blackLevelPattern
|
|
retcode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_SENSOR_BLACK_LEVEL_PATTERN, &entry);
|
|
if ((0 == retcode) && (entry.count > 0)) {
|
|
ASSERT_EQ(entry.count, 4);
|
|
for (size_t i = 1; i < entry.count; i++) {
|
|
ASSERT_EQ(entry.data.i32[i], entry.data.i32[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifyMonochromeCameraResult(
|
|
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& metadata) {
|
|
camera_metadata_ro_entry entry;
|
|
|
|
// Check tags that are not applicable for monochrome camera
|
|
ASSERT_FALSE(metadata.exists(ANDROID_SENSOR_GREEN_SPLIT));
|
|
ASSERT_FALSE(metadata.exists(ANDROID_SENSOR_NEUTRAL_COLOR_POINT));
|
|
ASSERT_FALSE(metadata.exists(ANDROID_COLOR_CORRECTION_MODE));
|
|
ASSERT_FALSE(metadata.exists(ANDROID_COLOR_CORRECTION_TRANSFORM));
|
|
ASSERT_FALSE(metadata.exists(ANDROID_COLOR_CORRECTION_GAINS));
|
|
|
|
// Check dynamicBlackLevel
|
|
entry = metadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
|
|
if (entry.count > 0) {
|
|
ASSERT_EQ(entry.count, 4);
|
|
for (size_t i = 1; i < entry.count; i++) {
|
|
ASSERT_FLOAT_EQ(entry.data.f[i], entry.data.f[0]);
|
|
}
|
|
}
|
|
|
|
// Check noiseProfile
|
|
entry = metadata.find(ANDROID_SENSOR_NOISE_PROFILE);
|
|
if (entry.count > 0) {
|
|
ASSERT_EQ(entry.count, 2);
|
|
}
|
|
|
|
// Check lensShadingMap
|
|
entry = metadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
|
|
if (entry.count > 0) {
|
|
ASSERT_EQ(entry.count % 4, 0);
|
|
for (size_t i = 0; i < entry.count/4; i++) {
|
|
ASSERT_FLOAT_EQ(entry.data.f[i*4+1], entry.data.f[i*4]);
|
|
ASSERT_FLOAT_EQ(entry.data.f[i*4+2], entry.data.f[i*4]);
|
|
ASSERT_FLOAT_EQ(entry.data.f[i*4+3], entry.data.f[i*4]);
|
|
}
|
|
}
|
|
|
|
// Check tonemapCurve
|
|
camera_metadata_ro_entry curveRed = metadata.find(ANDROID_TONEMAP_CURVE_RED);
|
|
camera_metadata_ro_entry curveGreen = metadata.find(ANDROID_TONEMAP_CURVE_GREEN);
|
|
camera_metadata_ro_entry curveBlue = metadata.find(ANDROID_TONEMAP_CURVE_BLUE);
|
|
if (curveRed.count > 0 && curveGreen.count > 0 && curveBlue.count > 0) {
|
|
ASSERT_EQ(curveRed.count, curveGreen.count);
|
|
ASSERT_EQ(curveRed.count, curveBlue.count);
|
|
for (size_t i = 0; i < curveRed.count; i++) {
|
|
ASSERT_FLOAT_EQ(curveGreen.data.f[i], curveRed.data.f[i]);
|
|
ASSERT_FLOAT_EQ(curveBlue.data.f[i], curveRed.data.f[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifyBuffersReturned(
|
|
sp<device::V3_2::ICameraDeviceSession> session,
|
|
int deviceVersion, int32_t streamId,
|
|
sp<DeviceCb> cb, uint32_t streamConfigCounter) {
|
|
sp<device::V3_3::ICameraDeviceSession> session3_3;
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4;
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5;
|
|
castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
|
|
ASSERT_NE(nullptr, session3_5.get());
|
|
|
|
hidl_vec<int32_t> streamIds(1);
|
|
streamIds[0] = streamId;
|
|
session3_5->signalStreamFlush(streamIds, /*streamConfigCounter*/streamConfigCounter);
|
|
cb->waitForBuffersReturned();
|
|
}
|
|
|
|
void CameraHidlTest::verifyBuffersReturned(
|
|
sp<device::V3_4::ICameraDeviceSession> session3_4,
|
|
hidl_vec<int32_t> streamIds, sp<DeviceCb> cb, uint32_t streamConfigCounter) {
|
|
auto castResult = device::V3_5::ICameraDeviceSession::castFrom(session3_4);
|
|
ASSERT_TRUE(castResult.isOk());
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5 = castResult;
|
|
ASSERT_NE(nullptr, session3_5.get());
|
|
|
|
session3_5->signalStreamFlush(streamIds, /*streamConfigCounter*/streamConfigCounter);
|
|
cb->waitForBuffersReturned();
|
|
}
|
|
|
|
void CameraHidlTest::verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
|
|
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata) {
|
|
std::unordered_set<std::string> physicalIds;
|
|
Status rc = getPhysicalCameraIds(staticMetadata, &physicalIds);
|
|
ASSERT_TRUE(Status::OK == rc);
|
|
ASSERT_TRUE(physicalIds.size() > 1);
|
|
|
|
camera_metadata_ro_entry entry;
|
|
// Check mainPhysicalId
|
|
entry = resultMetadata.find(ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID);
|
|
if (entry.count > 0) {
|
|
std::string mainPhysicalId(reinterpret_cast<const char *>(entry.data.u8));
|
|
ASSERT_NE(physicalIds.find(mainPhysicalId), physicalIds.end());
|
|
} else {
|
|
ADD_FAILURE() << "Get LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID failed!";
|
|
}
|
|
}
|
|
|
|
// Open a device session with empty callbacks and return static metadata.
|
|
void CameraHidlTest::openEmptyDeviceSession(const std::string &name, sp<ICameraProvider> provider,
|
|
sp<ICameraDeviceSession> *session /*out*/, camera_metadata_t **staticMeta /*out*/,
|
|
::android::sp<ICameraDevice> *cameraDevice /*out*/) {
|
|
ASSERT_NE(nullptr, session);
|
|
ASSERT_NE(nullptr, staticMeta);
|
|
|
|
::android::sp<ICameraDevice> device3_x;
|
|
ALOGI("configureStreams: Testing camera device %s", name.c_str());
|
|
Return<void> ret;
|
|
ret = provider->getCameraDeviceInterface_V3_x(
|
|
name,
|
|
[&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V3_x returns status:%d",
|
|
(int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
device3_x = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
if (cameraDevice != nullptr) {
|
|
*cameraDevice = device3_x;
|
|
}
|
|
|
|
sp<EmptyDeviceCb> cb = new EmptyDeviceCb();
|
|
ret = device3_x->open(cb, [&](auto status, const auto& newSession) {
|
|
ALOGI("device::open returns status:%d", (int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(newSession, nullptr);
|
|
*session = newSession;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = device3_x->getCameraCharacteristics([&] (Status s,
|
|
CameraMetadata metadata) {
|
|
ASSERT_EQ(Status::OK, s);
|
|
*staticMeta = clone_camera_metadata(
|
|
reinterpret_cast<const camera_metadata_t*>(metadata.data()));
|
|
ASSERT_NE(nullptr, *staticMeta);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
void CameraHidlTest::notifyDeviceState(provider::V2_5::DeviceState newState) {
|
|
if (mProvider2_5.get() == nullptr) return;
|
|
|
|
mProvider2_5->notifyDeviceStateChange(
|
|
static_cast<hidl_bitfield<provider::V2_5::DeviceState>>(newState));
|
|
}
|
|
|
|
// Open a particular camera device.
|
|
void CameraHidlTest::openCameraDevice(const std::string &name,
|
|
sp<ICameraProvider> provider,
|
|
sp<::android::hardware::camera::device::V1_0::ICameraDevice> *device1 /*out*/) {
|
|
ASSERT_TRUE(nullptr != device1);
|
|
|
|
Return<void> ret;
|
|
ret = provider->getCameraDeviceInterface_V1_x(
|
|
name,
|
|
[&](auto status, const auto& device) {
|
|
ALOGI("getCameraDeviceInterface_V1_x returns status:%d",
|
|
(int)status);
|
|
ASSERT_EQ(Status::OK, status);
|
|
ASSERT_NE(device, nullptr);
|
|
*device1 = device;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
sp<Camera1DeviceCb> deviceCb = new Camera1DeviceCb(this);
|
|
Return<Status> returnStatus = (*device1)->open(deviceCb);
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
}
|
|
|
|
// Initialize and configure a preview window.
|
|
void CameraHidlTest::setupPreviewWindow(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
|
|
sp<BufferItemConsumer> *bufferItemConsumer /*out*/,
|
|
sp<BufferItemHander> *bufferHandler /*out*/) {
|
|
ASSERT_NE(nullptr, device.get());
|
|
ASSERT_NE(nullptr, bufferItemConsumer);
|
|
ASSERT_NE(nullptr, bufferHandler);
|
|
|
|
sp<IGraphicBufferProducer> producer;
|
|
sp<IGraphicBufferConsumer> consumer;
|
|
BufferQueue::createBufferQueue(&producer, &consumer);
|
|
*bufferItemConsumer = new BufferItemConsumer(consumer,
|
|
GraphicBuffer::USAGE_HW_TEXTURE); //Use GLConsumer default usage flags
|
|
ASSERT_NE(nullptr, (*bufferItemConsumer).get());
|
|
*bufferHandler = new BufferItemHander(*bufferItemConsumer);
|
|
ASSERT_NE(nullptr, (*bufferHandler).get());
|
|
(*bufferItemConsumer)->setFrameAvailableListener(*bufferHandler);
|
|
sp<Surface> surface = new Surface(producer);
|
|
sp<PreviewWindowCb> previewCb = new PreviewWindowCb(surface);
|
|
|
|
auto rc = device->setPreviewWindow(previewCb);
|
|
ASSERT_TRUE(rc.isOk());
|
|
ASSERT_EQ(Status::OK, rc);
|
|
}
|
|
|
|
// Stop camera preview and close camera.
|
|
void CameraHidlTest::stopPreviewAndClose(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
|
|
Return<void> ret = device->stopPreview();
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = device->close();
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
// Enable a specific camera message type.
|
|
void CameraHidlTest::enableMsgType(unsigned int msgType,
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
|
|
Return<void> ret = device->enableMsgType(msgType);
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
Return<bool> returnBoolStatus = device->msgTypeEnabled(msgType);
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_TRUE(returnBoolStatus);
|
|
}
|
|
|
|
// Disable a specific camera message type.
|
|
void CameraHidlTest::disableMsgType(unsigned int msgType,
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
|
|
Return<void> ret = device->disableMsgType(msgType);
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
Return<bool> returnBoolStatus = device->msgTypeEnabled(msgType);
|
|
ASSERT_TRUE(returnBoolStatus.isOk());
|
|
ASSERT_FALSE(returnBoolStatus);
|
|
}
|
|
|
|
// Wait until a specific frame notification arrives.
|
|
void CameraHidlTest::waitForFrameLocked(DataCallbackMsg msgFrame,
|
|
std::unique_lock<std::mutex> &l) {
|
|
while (msgFrame != mDataMessageTypeReceived) {
|
|
auto timeout = std::chrono::system_clock::now() +
|
|
std::chrono::seconds(kStreamBufferTimeoutSec);
|
|
ASSERT_NE(std::cv_status::timeout,
|
|
mResultCondition.wait_until(l, timeout));
|
|
}
|
|
}
|
|
|
|
// Start preview on a particular camera device
|
|
void CameraHidlTest::startPreview(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device) {
|
|
Return<Status> returnStatus = device->startPreview();
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
}
|
|
|
|
// Retrieve camera parameters.
|
|
void CameraHidlTest::getParameters(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
|
|
CameraParameters *cameraParams /*out*/) {
|
|
ASSERT_NE(nullptr, cameraParams);
|
|
|
|
Return<void> ret;
|
|
ret = device->getParameters([&] (const ::android::hardware::hidl_string& params) {
|
|
ASSERT_FALSE(params.empty());
|
|
::android::String8 paramString(params.c_str());
|
|
(*cameraParams).unflatten(paramString);
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
|
|
// Set camera parameters.
|
|
void CameraHidlTest::setParameters(
|
|
const sp<::android::hardware::camera::device::V1_0::ICameraDevice> &device,
|
|
const CameraParameters &cameraParams) {
|
|
Return<Status> returnStatus = device->setParameters(
|
|
cameraParams.flatten().string());
|
|
ASSERT_TRUE(returnStatus.isOk());
|
|
ASSERT_EQ(Status::OK, returnStatus);
|
|
}
|
|
|
|
void CameraHidlTest::allocateGraphicBuffer(uint32_t width, uint32_t height, uint64_t usage,
|
|
PixelFormat format, hidl_handle *buffer_handle /*out*/) {
|
|
ASSERT_NE(buffer_handle, nullptr);
|
|
|
|
sp<android::hardware::graphics::allocator::V2_0::IAllocator> allocator =
|
|
android::hardware::graphics::allocator::V2_0::IAllocator::getService();
|
|
sp<android::hardware::graphics::allocator::V3_0::IAllocator> allocatorV3 =
|
|
android::hardware::graphics::allocator::V3_0::IAllocator::getService();
|
|
|
|
sp<android::hardware::graphics::mapper::V3_0::IMapper> mapperV3 =
|
|
android::hardware::graphics::mapper::V3_0::IMapper::getService();
|
|
sp<android::hardware::graphics::mapper::V2_0::IMapper> mapper =
|
|
android::hardware::graphics::mapper::V2_0::IMapper::getService();
|
|
::android::hardware::hidl_vec<uint32_t> descriptor;
|
|
if (mapperV3 != nullptr && allocatorV3 != nullptr) {
|
|
android::hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo descriptorInfo {};
|
|
descriptorInfo.width = width;
|
|
descriptorInfo.height = height;
|
|
descriptorInfo.layerCount = 1;
|
|
descriptorInfo.format =
|
|
static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(format);
|
|
descriptorInfo.usage = usage;
|
|
|
|
auto ret = mapperV3->createDescriptor(
|
|
descriptorInfo, [&descriptor](android::hardware::graphics::mapper::V3_0::Error err,
|
|
::android::hardware::hidl_vec<uint32_t> desc) {
|
|
ASSERT_EQ(err, android::hardware::graphics::mapper::V3_0::Error::NONE);
|
|
descriptor = desc;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = allocatorV3->allocate(descriptor, 1u,
|
|
[&](android::hardware::graphics::mapper::V3_0::Error err, uint32_t /*stride*/,
|
|
const ::android::hardware::hidl_vec<::android::hardware::hidl_handle>& buffers) {
|
|
ASSERT_EQ(android::hardware::graphics::mapper::V3_0::Error::NONE, err);
|
|
ASSERT_EQ(buffers.size(), 1u);
|
|
*buffer_handle = buffers[0];
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
} else {
|
|
ASSERT_NE(mapper.get(), nullptr);
|
|
ASSERT_NE(allocator.get(), nullptr);
|
|
android::hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo descriptorInfo {};
|
|
descriptorInfo.width = width;
|
|
descriptorInfo.height = height;
|
|
descriptorInfo.layerCount = 1;
|
|
descriptorInfo.format = format;
|
|
descriptorInfo.usage = usage;
|
|
|
|
auto ret = mapper->createDescriptor(
|
|
descriptorInfo, [&descriptor](android::hardware::graphics::mapper::V2_0::Error err,
|
|
::android::hardware::hidl_vec<uint32_t> desc) {
|
|
ASSERT_EQ(err, android::hardware::graphics::mapper::V2_0::Error::NONE);
|
|
descriptor = desc;
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
|
|
ret = allocator->allocate(descriptor, 1u,
|
|
[&](android::hardware::graphics::mapper::V2_0::Error err, uint32_t /*stride*/,
|
|
const ::android::hardware::hidl_vec<::android::hardware::hidl_handle>& buffers) {
|
|
ASSERT_EQ(android::hardware::graphics::mapper::V2_0::Error::NONE, err);
|
|
ASSERT_EQ(buffers.size(), 1u);
|
|
*buffer_handle = buffers[0];
|
|
});
|
|
ASSERT_TRUE(ret.isOk());
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifyRecommendedConfigs(const CameraMetadata& chars) {
|
|
size_t CONFIG_ENTRY_SIZE = 5;
|
|
size_t CONFIG_ENTRY_TYPE_OFFSET = 3;
|
|
size_t CONFIG_ENTRY_BITFIELD_OFFSET = 4;
|
|
uint32_t maxPublicUsecase =
|
|
ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END;
|
|
uint32_t vendorUsecaseStart =
|
|
ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VENDOR_START;
|
|
uint32_t usecaseMask = (1 << vendorUsecaseStart) - 1;
|
|
usecaseMask &= ~((1 << maxPublicUsecase) - 1);
|
|
|
|
const camera_metadata_t* metadata = reinterpret_cast<const camera_metadata_t*> (chars.data());
|
|
|
|
camera_metadata_ro_entry recommendedConfigsEntry, recommendedDepthConfigsEntry, ioMapEntry;
|
|
recommendedConfigsEntry.count = recommendedDepthConfigsEntry.count = ioMapEntry.count = 0;
|
|
int retCode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS, &recommendedConfigsEntry);
|
|
int depthRetCode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS,
|
|
&recommendedDepthConfigsEntry);
|
|
int ioRetCode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP, &ioMapEntry);
|
|
if ((0 != retCode) && (0 != depthRetCode)) {
|
|
//In case both regular and depth recommended configurations are absent,
|
|
//I/O should be absent as well.
|
|
ASSERT_NE(ioRetCode, 0);
|
|
return;
|
|
}
|
|
|
|
camera_metadata_ro_entry availableKeysEntry;
|
|
retCode = find_camera_metadata_ro_entry(metadata,
|
|
ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, &availableKeysEntry);
|
|
ASSERT_TRUE((0 == retCode) && (availableKeysEntry.count > 0));
|
|
std::vector<int32_t> availableKeys;
|
|
availableKeys.reserve(availableKeysEntry.count);
|
|
availableKeys.insert(availableKeys.end(), availableKeysEntry.data.i32,
|
|
availableKeysEntry.data.i32 + availableKeysEntry.count);
|
|
|
|
if (recommendedConfigsEntry.count > 0) {
|
|
ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
|
|
ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS),
|
|
availableKeys.end());
|
|
ASSERT_EQ((recommendedConfigsEntry.count % CONFIG_ENTRY_SIZE), 0);
|
|
for (size_t i = 0; i < recommendedConfigsEntry.count; i += CONFIG_ENTRY_SIZE) {
|
|
int32_t entryType =
|
|
recommendedConfigsEntry.data.i32[i + CONFIG_ENTRY_TYPE_OFFSET];
|
|
uint32_t bitfield =
|
|
recommendedConfigsEntry.data.i32[i + CONFIG_ENTRY_BITFIELD_OFFSET];
|
|
ASSERT_TRUE((entryType ==
|
|
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) ||
|
|
(entryType ==
|
|
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT));
|
|
ASSERT_TRUE((bitfield & usecaseMask) == 0);
|
|
}
|
|
}
|
|
|
|
if (recommendedDepthConfigsEntry.count > 0) {
|
|
ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
|
|
ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS),
|
|
availableKeys.end());
|
|
ASSERT_EQ((recommendedDepthConfigsEntry.count % CONFIG_ENTRY_SIZE), 0);
|
|
for (size_t i = 0; i < recommendedDepthConfigsEntry.count; i += CONFIG_ENTRY_SIZE) {
|
|
int32_t entryType =
|
|
recommendedDepthConfigsEntry.data.i32[i + CONFIG_ENTRY_TYPE_OFFSET];
|
|
uint32_t bitfield =
|
|
recommendedDepthConfigsEntry.data.i32[i + CONFIG_ENTRY_BITFIELD_OFFSET];
|
|
ASSERT_TRUE((entryType ==
|
|
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) ||
|
|
(entryType ==
|
|
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT));
|
|
ASSERT_TRUE((bitfield & usecaseMask) == 0);
|
|
}
|
|
|
|
if (recommendedConfigsEntry.count == 0) {
|
|
//In case regular recommended configurations are absent but suggested depth
|
|
//configurations are present, I/O should be absent.
|
|
ASSERT_NE(ioRetCode, 0);
|
|
}
|
|
}
|
|
|
|
if ((ioRetCode == 0) && (ioMapEntry.count > 0)) {
|
|
ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
|
|
ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP),
|
|
availableKeys.end());
|
|
ASSERT_EQ(isZSLModeAvailable(metadata), Status::OK);
|
|
}
|
|
}
|
|
|
|
void CameraHidlTest::verifySessionReconfigurationQuery(
|
|
sp<device::V3_5::ICameraDeviceSession> session3_5, camera_metadata* oldSessionParams,
|
|
camera_metadata* newSessionParams) {
|
|
ASSERT_NE(nullptr, session3_5.get());
|
|
ASSERT_NE(nullptr, oldSessionParams);
|
|
ASSERT_NE(nullptr, newSessionParams);
|
|
|
|
android::hardware::hidl_vec<uint8_t> oldParams, newParams;
|
|
oldParams.setToExternal(reinterpret_cast<uint8_t*>(oldSessionParams),
|
|
get_camera_metadata_size(oldSessionParams));
|
|
newParams.setToExternal(reinterpret_cast<uint8_t*>(newSessionParams),
|
|
get_camera_metadata_size(newSessionParams));
|
|
android::hardware::camera::common::V1_0::Status callStatus;
|
|
auto hidlCb = [&callStatus] (android::hardware::camera::common::V1_0::Status s,
|
|
bool /*requiredFlag*/) {
|
|
callStatus = s;
|
|
};
|
|
auto ret = session3_5->isReconfigurationRequired(oldParams, newParams, hidlCb);
|
|
ASSERT_TRUE(ret.isOk());
|
|
switch (callStatus) {
|
|
case android::hardware::camera::common::V1_0::Status::OK:
|
|
case android::hardware::camera::common::V1_0::Status::METHOD_NOT_SUPPORTED:
|
|
break;
|
|
case android::hardware::camera::common::V1_0::Status::INTERNAL_ERROR:
|
|
default:
|
|
ADD_FAILURE() << "Query calllback failed";
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
::testing::AddGlobalTestEnvironment(CameraHidlEnvironment::Instance());
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
CameraHidlEnvironment::Instance()->init(&argc, argv);
|
|
int status = RUN_ALL_TESTS();
|
|
ALOGI("Test result = %d", status);
|
|
return status;
|
|
}
|