| /* |
| * 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 <condition_variable> |
| #include <list> |
| #include <mutex> |
| #include <regex> |
| #include <string> |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #include <inttypes.h> |
| |
| #include <CameraMetadata.h> |
| #include <CameraParameters.h> |
| #include <HandleImporter.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.3/ICameraDeviceSession.h> |
| #include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h> |
| #include <android/hardware/camera/device/3.4/ICameraDeviceSession.h> |
| #include <android/hardware/camera/device/3.5/ICameraDevice.h> |
| #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h> |
| #include <android/hardware/camera/device/3.5/ICameraDeviceSession.h> |
| #include <android/hardware/camera/device/3.6/ICameraDevice.h> |
| #include <android/hardware/camera/device/3.6/ICameraDeviceSession.h> |
| #include <android/hardware/camera/device/3.7/ICameraDevice.h> |
| #include <android/hardware/camera/device/3.7/ICameraDeviceSession.h> |
| #include <android/hardware/camera/device/3.7/ICameraInjectionSession.h> |
| #include <android/hardware/camera/metadata/3.4/types.h> |
| #include <android/hardware/camera/provider/2.4/ICameraProvider.h> |
| #include <android/hardware/camera/provider/2.5/ICameraProvider.h> |
| #include <android/hardware/camera/provider/2.6/ICameraProvider.h> |
| #include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h> |
| #include <android/hardware/camera/provider/2.7/ICameraProvider.h> |
| #include <android/hidl/manager/1.0/IServiceManager.h> |
| #include <binder/MemoryHeapBase.h> |
| #include <cutils/properties.h> |
| #include <fmq/MessageQueue.h> |
| #include <grallocusage/GrallocUsageConversion.h> |
| #include <gtest/gtest.h> |
| #include <gui/BufferItemConsumer.h> |
| #include <gui/BufferQueue.h> |
| #include <gui/Surface.h> |
| #include <hardware/gralloc.h> |
| #include <hardware/gralloc1.h> |
| #include <hidl/GtestPrinter.h> |
| #include <hidl/ServiceManagement.h> |
| #include <log/log.h> |
| #include <system/camera.h> |
| #include <system/camera_metadata.h> |
| #include <ui/GraphicBuffer.h> |
| #include <ui/GraphicBufferAllocator.h> |
| #include <ui/GraphicBufferMapper.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> |
| |
| using namespace ::android::hardware::camera::device; |
| using ::android::BufferItemConsumer; |
| using ::android::BufferQueue; |
| using ::android::GraphicBuffer; |
| using ::android::IGraphicBufferConsumer; |
| using ::android::IGraphicBufferProducer; |
| using ::android::sp; |
| using ::android::Surface; |
| using ::android::wp; |
| using ::android::hardware::hidl_bitfield; |
| using ::android::hardware::hidl_handle; |
| using ::android::hardware::hidl_string; |
| using ::android::hardware::hidl_vec; |
| using ::android::hardware::kSynchronizedReadWrite; |
| using ::android::hardware::MessageQueue; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| using ::android::hardware::camera::common::V1_0::CameraDeviceStatus; |
| using ::android::hardware::camera::common::V1_0::Status; |
| 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::HandleImporter; |
| using ::android::hardware::camera::common::V1_0::helper::Size; |
| using ::android::hardware::camera::device::V1_0::CameraFacing; |
| using ::android::hardware::camera::device::V1_0::CameraFrameMetadata; |
| using ::android::hardware::camera::device::V1_0::CommandType; |
| using ::android::hardware::camera::device::V1_0::DataCallbackMsg; |
| using ::android::hardware::camera::device::V1_0::FrameCallbackFlag; |
| using ::android::hardware::camera::device::V1_0::HandleTimestampMessage; |
| using ::android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback; |
| using ::android::hardware::camera::device::V1_0::NotifyCallbackMsg; |
| using ::android::hardware::camera::device::V3_2::BufferCache; |
| using ::android::hardware::camera::device::V3_2::BufferStatus; |
| using ::android::hardware::camera::device::V3_2::CameraMetadata; |
| using ::android::hardware::camera::device::V3_2::CaptureRequest; |
| using ::android::hardware::camera::device::V3_2::CaptureResult; |
| using ::android::hardware::camera::device::V3_2::ErrorCode; |
| using ::android::hardware::camera::device::V3_2::ErrorMsg; |
| using ::android::hardware::camera::device::V3_2::HalStreamConfiguration; |
| using ::android::hardware::camera::device::V3_2::ICameraDevice; |
| using ::android::hardware::camera::device::V3_2::ICameraDeviceSession; |
| using ::android::hardware::camera::device::V3_2::MsgType; |
| using ::android::hardware::camera::device::V3_2::NotifyMsg; |
| using ::android::hardware::camera::device::V3_2::RequestTemplate; |
| using ::android::hardware::camera::device::V3_2::StreamBuffer; |
| using ::android::hardware::camera::device::V3_2::StreamConfiguration; |
| using ::android::hardware::camera::device::V3_2::StreamConfigurationMode; |
| using ::android::hardware::camera::device::V3_2::StreamRotation; |
| using ::android::hardware::camera::device::V3_2::StreamType; |
| using ::android::hardware::camera::device::V3_4::PhysicalCameraMetadata; |
| using ::android::hardware::camera::metadata::V3_4:: |
| CameraMetadataEnumAndroidSensorInfoColorFilterArrangement; |
| using ::android::hardware::camera::metadata::V3_4::CameraMetadataTag; |
| using ::android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode; |
| using ::android::hardware::camera::provider::V2_4::ICameraProvider; |
| using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback; |
| using ::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination; |
| 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::hidl::allocator::V1_0::IAllocator; |
| using ::android::hidl::memory::V1_0::IMemory; |
| 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 kMaxStillWidth = 2048; |
| const uint32_t kMaxStillHeight = 1536; |
| 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 RecordingRateSizePair { |
| int32_t recordingRate; |
| int32_t width; |
| int32_t height; |
| |
| bool operator==(const RecordingRateSizePair &p) const{ |
| return p.recordingRate == recordingRate && |
| p.width == width && |
| p.height == height; |
| } |
| }; |
| |
| struct RecordingRateSizePairHasher { |
| size_t operator()(const RecordingRateSizePair& p) const { |
| std::size_t p1 = std::hash<int32_t>()(p.recordingRate); |
| std::size_t p2 = std::hash<int32_t>()(p.width); |
| std::size_t p3 = std::hash<int32_t>()(p.height); |
| return p1 ^ p2 ^ p3; |
| } |
| }; |
| |
| struct AvailableZSLInputOutput { |
| int32_t inputFormat; |
| int32_t outputFormat; |
| }; |
| |
| enum ReprocessType { |
| PRIV_REPROCESS, |
| YUV_REPROCESS, |
| }; |
| |
| enum SystemCameraKind { |
| /** |
| * These camera devices are visible to all apps and system components alike |
| */ |
| PUBLIC = 0, |
| |
| /** |
| * These camera devices are visible only to processes having the |
| * android.permission.SYSTEM_CAMERA permission. They are not exposed to 3P |
| * apps. |
| */ |
| SYSTEM_ONLY_CAMERA, |
| |
| /** |
| * These camera devices are visible only to HAL clients (that try to connect |
| * on a hwbinder thread). |
| */ |
| HIDDEN_SECURE_CAMERA |
| }; |
| |
| const static std::vector<int64_t> kMandatoryUseCases = { |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT, |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW, |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE, |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD, |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL, |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL |
| }; |
| |
| namespace { |
| // "device@<version>/legacy/<id>" |
| const char* kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)"; |
| const int CAMERA_DEVICE_API_VERSION_3_7 = 0x307; |
| const int CAMERA_DEVICE_API_VERSION_3_6 = 0x306; |
| 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_7 = "3.7"; |
| const char* kHAL3_6 = "3.6"; |
| 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.c_str()); |
| 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 getCameraDeviceVersionAndId(const hidl_string& deviceName, |
| const hidl_string &providerType, std::string* id) { |
| std::string version; |
| bool match = matchDeviceName(deviceName, providerType, &version, id); |
| if (!match) { |
| return -1; |
| } |
| |
| if (version.compare(kHAL3_7) == 0) { |
| return CAMERA_DEVICE_API_VERSION_3_7; |
| } else if (version.compare(kHAL3_6) == 0) { |
| return CAMERA_DEVICE_API_VERSION_3_6; |
| } else 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; |
| } |
| |
| int getCameraDeviceVersion(const hidl_string& deviceName, |
| const hidl_string &providerType) { |
| return getCameraDeviceVersionAndId(deviceName, providerType, nullptr); |
| } |
| |
| 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; |
| } |
| } |
| |
| 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::TestWithParam<std::string> { |
| public: |
| virtual void SetUp() override { |
| std::string service_name = GetParam(); |
| ALOGI("get service with name: %s", service_name.c_str()); |
| mProvider = ICameraProvider::getService(service_name); |
| |
| ASSERT_NE(mProvider, nullptr); |
| |
| uint32_t id; |
| ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id)); |
| |
| castProvider(mProvider, &mProvider2_5, &mProvider2_6, &mProvider2_7); |
| notifyDeviceState(provider::V2_5::DeviceState::NORMAL); |
| } |
| virtual void TearDown() override {} |
| |
| hidl_vec<hidl_string> getCameraDeviceNames(sp<ICameraProvider> provider, |
| bool addSecureOnly = false); |
| |
| bool isSecureOnly(sp<ICameraProvider> provider, const hidl_string& name); |
| |
| std::map<hidl_string, hidl_string> getCameraDeviceIdToNameMap(sp<ICameraProvider> provider); |
| |
| hidl_vec<hidl_vec<hidl_string>> getConcurrentDeviceCombinations( |
| sp<::android::hardware::camera::provider::V2_6::ICameraProvider>&); |
| |
| 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_4::Stream>& streams, |
| const hidl_vec<V3_2::HalStream>& halStreams); |
| |
| void waitForBuffersReturned(); |
| |
| private: |
| bool processCaptureResultLocked(const CaptureResult& results, |
| hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata); |
| Return<void> notifyHelper(const hidl_vec<NotifyMsg>& msgs, |
| const std::vector<std::pair<bool, nsecs_t>>& readoutTimestamps); |
| |
| 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_4::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*/, |
| sp<provider::V2_6::ICameraProvider>* provider2_6 /*out*/, |
| sp<provider::V2_7::ICameraProvider>* provider2_7 /*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*/, |
| sp<device::V3_6::ICameraDeviceSession>* session3_6 /*out*/, |
| sp<device::V3_7::ICameraDeviceSession>* session3_7 /*out*/); |
| void castInjectionSession( |
| const sp<ICameraDeviceSession>& session, |
| sp<device::V3_7::ICameraInjectionSession>* injectionSession3_7 /*out*/); |
| void castDevice(const sp<device::V3_2::ICameraDevice>& device, int32_t deviceVersion, |
| sp<device::V3_5::ICameraDevice>* device3_5 /*out*/, |
| sp<device::V3_7::ICameraDevice>* device3_7 /*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, |
| ::android::hardware::camera::device::V3_7::StreamConfiguration* config3_7, |
| uint32_t jpegBufferSize = 0); |
| |
| void configureOfflineStillStream(const std::string &name, int32_t deviceVersion, |
| sp<ICameraProvider> provider, |
| const AvailableStream *threshold, |
| sp<device::V3_6::ICameraDeviceSession> *session/*out*/, |
| V3_2::Stream *stream /*out*/, |
| device::V3_6::HalStreamConfiguration *halStreamConfig /*out*/, |
| bool *supportsPartialResults /*out*/, |
| uint32_t *partialResultCount /*out*/, |
| sp<DeviceCb> *outCb /*out*/, |
| uint32_t *jpegBufferSize /*out*/, |
| bool *useHalBufManager /*out*/); |
| void configureStreams3_7(const std::string& name, int32_t deviceVersion, |
| sp<ICameraProvider> provider, PixelFormat format, |
| sp<device::V3_7::ICameraDeviceSession>* session3_7 /*out*/, |
| V3_2::Stream* previewStream /*out*/, |
| device::V3_6::HalStreamConfiguration* halStreamConfig /*out*/, |
| bool* supportsPartialResults /*out*/, |
| uint32_t* partialResultCount /*out*/, bool* useHalBufManager /*out*/, |
| sp<DeviceCb>* outCb /*out*/, uint32_t streamConfigCounter, |
| bool maxResolution); |
| |
| 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 configureSingleStream(const std::string& name, int32_t deviceVersion, |
| sp<ICameraProvider> provider, |
| const AvailableStream* previewThreshold, uint64_t bufferUsage, |
| RequestTemplate reqTemplate, |
| 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 verifyLogicalOrUltraHighResCameraMetadata( |
| 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 verifyExtendedSceneModeCharacteristics(const camera_metadata_t* metadata); |
| void verifyZoomCharacteristics(const camera_metadata_t* metadata); |
| void verifyStreamUseCaseCharacteristics(const camera_metadata_t* metadata); |
| 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_7::ICameraDevice> cameraDevice3_7, |
| const ::android::hardware::camera::device::V3_7::StreamConfiguration& config3_7, |
| 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 verifyBuffersReturned(sp<device::V3_7::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); |
| |
| void verifyRequestTemplate(const camera_metadata_t* metadata, RequestTemplate requestTemplate); |
| static void overrideRotateAndCrop(::android::hardware::hidl_vec<uint8_t> *settings /*in/out*/); |
| |
| static bool isDepthOnly(const camera_metadata_t* staticMeta); |
| |
| static bool isUltraHighResolution(const camera_metadata_t* staticMeta); |
| |
| static Status getAvailableOutputStreams(const camera_metadata_t* staticMeta, |
| std::vector<AvailableStream>& outputStreams, |
| const AvailableStream* threshold = nullptr, |
| bool maxResolution = false); |
| |
| static Status getMaxOutputSizeForFormat(const camera_metadata_t* staticMeta, PixelFormat format, |
| Size* size, bool maxResolution = false); |
| |
| static Status getMandatoryConcurrentStreams(const camera_metadata_t* staticMeta, |
| std::vector<AvailableStream>* outputStreams); |
| |
| 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 bool isTorchStrengthControlSupported(const camera_metadata_t *staticMeta); |
| static Status isOfflineSessionSupported(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); |
| static Status getSystemCameraKind(const camera_metadata_t* staticMeta, |
| SystemCameraKind* systemCameraKind); |
| static void getMultiResolutionStreamConfigurations( |
| camera_metadata_ro_entry* multiResStreamConfigs, |
| camera_metadata_ro_entry* streamConfigs, |
| camera_metadata_ro_entry* maxResolutionStreamConfigs, |
| const camera_metadata_t* staticMetadata); |
| void getPrivacyTestPatternModes( |
| const camera_metadata_t* staticMetadata, |
| std::unordered_set<int32_t>* privacyTestPatternModes/*out*/); |
| |
| static V3_2::DataspaceFlags getDataspace(PixelFormat format); |
| |
| void processCaptureRequestInternal(uint64_t bufferusage, RequestTemplate reqTemplate, |
| bool useSecureOnlyCameras); |
| |
| // Used by switchToOffline where a new result queue is created for offline reqs |
| void updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue); |
| |
| protected: |
| |
| // In-flight queue for tracking completion of capture requests. |
| struct InFlightRequest { |
| // Set by notify() SHUTTER call. |
| nsecs_t shutterTimestamp; |
| |
| bool shutterReadoutTimestampValid; |
| nsecs_t shutterReadoutTimestamp; |
| |
| 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. |
| // For physical sub-camera result errors, the Id of the physical stream |
| // for the physical sub-camera. |
| // 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; |
| |
| std::unordered_set<std::string> expectedPhysicalResults; |
| |
| InFlightRequest() : |
| shutterTimestamp(0), |
| shutterReadoutTimestampValid(false), |
| shutterReadoutTimestamp(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), |
| collectedResult(1, 10) {} |
| |
| InFlightRequest(ssize_t numBuffers, bool hasInput, |
| bool partialResults, uint32_t partialCount, |
| std::shared_ptr<ResultMetadataQueue> queue = nullptr) : |
| shutterTimestamp(0), |
| shutterReadoutTimestampValid(false), |
| shutterReadoutTimestamp(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), |
| collectedResult(1, 10) {} |
| |
| InFlightRequest(ssize_t numBuffers, bool hasInput, |
| bool partialResults, uint32_t partialCount, |
| const std::unordered_set<std::string>& extraPhysicalResult, |
| std::shared_ptr<ResultMetadataQueue> queue = nullptr) : |
| shutterTimestamp(0), |
| shutterReadoutTimestampValid(false), |
| shutterReadoutTimestamp(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), |
| collectedResult(1, 10), |
| expectedPhysicalResults(extraPhysicalResult) {} |
| }; |
| |
| // 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; |
| sp<::android::hardware::camera::provider::V2_6::ICameraProvider> mProvider2_6; |
| sp<::android::hardware::camera::provider::V2_7::ICameraProvider> mProvider2_7; |
| |
| // Camera provider type. |
| std::string mProviderType; |
| |
| HandleImporter mHandleImporter; |
| }; |
| |
| 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; |
| } |
| |
| // Physical device results are only expected in the last/final |
| // partial result notification. |
| bool expectPhysicalResults = !(request->usePartialResult && |
| (results.partialResult < request->numPartialResults)); |
| if (expectPhysicalResults && |
| (physicalCameraMetadata.size() != request->expectedPhysicalResults.size())) { |
| ALOGE("%s: Frame %d: Returned physical metadata count %zu " |
| "must be equal to expected count %zu", __func__, frameNumber, |
| physicalCameraMetadata.size(), request->expectedPhysicalResults.size()); |
| 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; |
| } |
| |
| // Verify no duplicate tags between partial results |
| const camera_metadata_t* partialMetadata = |
| reinterpret_cast<const camera_metadata_t*>(resultMetadata.data()); |
| const camera_metadata_t* collectedMetadata = request->collectedResult.getAndLock(); |
| camera_metadata_ro_entry_t searchEntry, foundEntry; |
| for (size_t i = 0; i < get_camera_metadata_entry_count(partialMetadata); i++) { |
| if (0 != get_camera_metadata_ro_entry(partialMetadata, i, &searchEntry)) { |
| ADD_FAILURE(); |
| request->collectedResult.unlock(collectedMetadata); |
| return notify; |
| } |
| if (-ENOENT != |
| find_camera_metadata_ro_entry(collectedMetadata, searchEntry.tag, &foundEntry)) { |
| ADD_FAILURE(); |
| request->collectedResult.unlock(collectedMetadata); |
| return notify; |
| } |
| } |
| request->collectedResult.unlock(collectedMetadata); |
| request->collectedResult.append(partialMetadata); |
| |
| 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_4::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].v3_2.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::vector<std::pair<bool, nsecs_t>> readoutTimestamps; |
| readoutTimestamps.resize(messages.size()); |
| for (size_t i = 0; i < messages.size(); i++) { |
| readoutTimestamps[i] = {false, 0}; |
| } |
| |
| return notifyHelper(messages, readoutTimestamps); |
| } |
| |
| Return<void> CameraHidlTest::DeviceCb::notifyHelper( |
| const hidl_vec<NotifyMsg>& messages, |
| const std::vector<std::pair<bool, nsecs_t>>& readoutTimestamps) { |
| std::lock_guard<std::mutex> l(mParent->mLock); |
| |
| for (size_t i = 0; i < messages.size(); i++) { |
| 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 { |
| ssize_t idx = mParent->mInflightMap.indexOfKey( |
| messages[i].msg.error.frameNumber); |
| if (::android::NAME_NOT_FOUND == idx) { |
| ALOGE("%s: Unexpected error frame number! received: %u", |
| __func__, messages[i].msg.error.frameNumber); |
| ADD_FAILURE(); |
| break; |
| } |
| InFlightRequest *r = mParent->mInflightMap.editValueAt(idx); |
| |
| if (ErrorCode::ERROR_RESULT == messages[i].msg.error.errorCode && |
| messages[i].msg.error.errorStreamId != -1) { |
| if (r->haveResultMetadata) { |
| ALOGE("%s: Camera must report physical camera result error before " |
| "the final capture result!", __func__); |
| ADD_FAILURE(); |
| } else { |
| for (size_t j = 0; j < mStreams.size(); j++) { |
| if (mStreams[j].v3_2.id == messages[i].msg.error.errorStreamId) { |
| hidl_string physicalCameraId = mStreams[j].physicalCameraId; |
| bool idExpected = r->expectedPhysicalResults.find( |
| physicalCameraId) != r->expectedPhysicalResults.end(); |
| if (!idExpected) { |
| ALOGE("%s: ERROR_RESULT's error stream's physicalCameraId " |
| "%s must be expected", __func__, |
| physicalCameraId.c_str()); |
| ADD_FAILURE(); |
| } else { |
| r->expectedPhysicalResults.erase(physicalCameraId); |
| } |
| break; |
| } |
| } |
| } |
| } else { |
| r->errorCodeValid = true; |
| r->errorCode = messages[i].msg.error.errorCode; |
| r->errorStreamId = messages[i].msg.error.errorStreamId; |
| } |
| } |
| break; |
| case MsgType::SHUTTER: |
| { |
| ssize_t idx = mParent->mInflightMap.indexOfKey(messages[i].msg.shutter.frameNumber); |
| if (::android::NAME_NOT_FOUND == idx) { |
| ALOGE("%s: Unexpected shutter frame number! received: %u", |
| __func__, messages[i].msg.shutter.frameNumber); |
| ADD_FAILURE(); |
| break; |
| } |
| InFlightRequest *r = mParent->mInflightMap.editValueAt(idx); |
| r->shutterTimestamp = messages[i].msg.shutter.timestamp; |
| r->shutterReadoutTimestampValid = readoutTimestamps[i].first; |
| r->shutterReadoutTimestamp = readoutTimestamps[i].second; |
| } |
| 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].v3_2.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.v3_2.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; |
| uint32_t w = stream.v3_2.width; |
| uint32_t h = stream.v3_2.height; |
| if (stream.v3_2.format == PixelFormat::BLOB) { |
| w = stream.bufferSize; |
| h = 1; |
| } |
| mParent->allocateGraphicBuffer(w, h, |
| android_convertGralloc1To0Usage( |
| halStream.producerUsage, halStream.consumerUsage), |
| halStream.overrideFormat, &buffer_handle); |
| |
| tmpRetBuffers[j] = {stream.v3_2.id, mNextBufferId, buffer_handle, BufferStatus::OK, |
| nullptr, nullptr}; |
| mOutstandingBufferIds[idx].insert(std::make_pair(mNextBufferId++, buffer_handle)); |
| } |
| atLeastOneStreamOk = true; |
| bufRets[i].streamId = stream.v3_2.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::unique_lock<std::mutex> l(mLock); |
| for (const auto& buf : buffers) { |
| bool found = false; |
| for (size_t idx = 0; idx < mOutstandingBufferIds.size(); idx++) { |
| if (mStreams[idx].v3_2.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(); |
| } |
| if (!hasOutstandingBuffersLocked()) { |
| l.unlock(); |
| mFlushedCondition.notify_one(); |
| } |
| return Void(); |
| } |
| |
| std::map<hidl_string, hidl_string> CameraHidlTest::getCameraDeviceIdToNameMap( |
| sp<ICameraProvider> provider) { |
| hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(provider); |
| std::map<hidl_string, hidl_string> idToNameMap; |
| for (auto& name : cameraDeviceNames) { |
| std::string version, cameraId; |
| if (!matchDeviceName(name, mProviderType, &version, &cameraId)) { |
| ADD_FAILURE(); |
| } |
| idToNameMap.insert(std::make_pair(hidl_string(cameraId), name)); |
| } |
| return idToNameMap; |
| } |
| |
| hidl_vec<hidl_string> CameraHidlTest::getCameraDeviceNames(sp<ICameraProvider> provider, |
| bool addSecureOnly) { |
| 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); |
| } |
| } |
| |
| std::vector<hidl_string> retList; |
| for (size_t i = 0; i < cameraDeviceNames.size(); i++) { |
| bool isSecureOnlyCamera = isSecureOnly(mProvider, cameraDeviceNames[i]); |
| if (addSecureOnly) { |
| if (isSecureOnlyCamera) { |
| retList.emplace_back(cameraDeviceNames[i]); |
| } |
| } else if (!isSecureOnlyCamera) { |
| retList.emplace_back(cameraDeviceNames[i]); |
| } |
| } |
| hidl_vec<hidl_string> finalRetList = std::move(retList); |
| return finalRetList; |
| } |
| |
| bool CameraHidlTest::isSecureOnly(sp<ICameraProvider> provider, const hidl_string& name) { |
| Return<void> ret; |
| ::android::sp<ICameraDevice> device3_x; |
| bool retVal = false; |
| if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) { |
| return false; |
| } |
| 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; |
| }); |
| if (!ret.isOk()) { |
| ADD_FAILURE() << "Failed to get camera device interface for " << name; |
| } |
| ret = device3_x->getCameraCharacteristics([&](Status s, CameraMetadata metadata) { |
| ASSERT_EQ(Status::OK, s); |
| camera_metadata_t* chars = (camera_metadata_t*)metadata.data(); |
| SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC; |
| Status status = getSystemCameraKind(chars, &systemCameraKind); |
| ASSERT_EQ(status, Status::OK); |
| if (systemCameraKind == SystemCameraKind::HIDDEN_SECURE_CAMERA) { |
| retVal = true; |
| } |
| }); |
| if (!ret.isOk()) { |
| ADD_FAILURE() << "Failed to get camera characteristics for device " << name; |
| } |
| return retVal; |
| } |
| |
| hidl_vec<hidl_vec<hidl_string>> CameraHidlTest::getConcurrentDeviceCombinations( |
| sp<::android::hardware::camera::provider::V2_6::ICameraProvider>& provider2_6) { |
| hidl_vec<hidl_vec<hidl_string>> combinations; |
| Return<void> ret = provider2_6->getConcurrentStreamingCameraIds( |
| [&combinations](Status concurrentIdStatus, |
| const hidl_vec<hidl_vec<hidl_string>>& cameraDeviceIdCombinations) { |
| ASSERT_EQ(concurrentIdStatus, Status::OK); |
| combinations = cameraDeviceIdCombinations; |
| }); |
| if (!ret.isOk()) { |
| ADD_FAILURE(); |
| } |
| return combinations; |
| } |
| |
| // Test devices with first_api_level >= P does not advertise device@1.0 |
| TEST_P(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_P(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_P(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_P(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_P(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(); |
| } |
| }; |
| |
| struct ProviderCb2_6 |
| : public ::android::hardware::camera::provider::V2_6::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(); |
| } |
| |
| virtual Return<void> physicalCameraDeviceStatusChange( |
| const hidl_string& cameraDeviceName, const hidl_string& physicalCameraDeviceName, |
| CameraDeviceStatus newStatus) override { |
| ALOGI("physical camera device status callback name %s, physical camera name %s," |
| " status %d", |
| cameraDeviceName.c_str(), physicalCameraDeviceName.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); |
| |
| if (mProvider2_6.get() != nullptr) { |
| sp<ProviderCb2_6> cb = new ProviderCb2_6; |
| auto status = mProvider2_6->setCallback(cb); |
| ASSERT_TRUE(status.isOk()); |
| ASSERT_EQ(Status::OK, status); |
| status = mProvider2_6->setCallback(nullptr); |
| ASSERT_TRUE(status.isOk()); |
| ASSERT_EQ(Status::OK, status); |
| } |
| } |
| |
| // Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device |
| TEST_P(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_7: |
| case CAMERA_DEVICE_API_VERSION_3_6: |
| 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 |
| // correct. |
| TEST_P(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_7: |
| case CAMERA_DEVICE_API_VERSION_3_6: |
| 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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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_P(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); |
|