blob: a5369e7b8d507823b679064fe87de6f8eb9a64d2 [file] [log] [blame]
/*
* 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