| /* |
| * Copyright (C) 2022 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. |
| */ |
| #include <gtest/gtest.h> |
| |
| #include <aidl/Vintf.h> |
| #include <aidl/android/hardware/camera/common/VendorTagSection.h> |
| #include <aidl/android/hardware/camera/device/ICameraDevice.h> |
| #include <aidlcommonsupport/NativeHandle.h> |
| #include <camera_aidl_test.h> |
| #include <cutils/properties.h> |
| #include <device_cb.h> |
| #include <empty_device_cb.h> |
| #include <grallocusage/GrallocUsageConversion.h> |
| #include <gtest/gtest.h> |
| #include <hardware/gralloc.h> |
| #include <hardware/gralloc1.h> |
| #include <hidl/GtestPrinter.h> |
| #include <hidl/HidlSupport.h> |
| #include <torch_provider_cb.h> |
| #include <com_android_internal_camera_flags.h> |
| #include <list> |
| |
| using ::aidl::android::hardware::camera::common::CameraDeviceStatus; |
| using ::aidl::android::hardware::camera::common::CameraResourceCost; |
| using ::aidl::android::hardware::camera::common::TorchModeStatus; |
| using ::aidl::android::hardware::camera::common::VendorTagSection; |
| using ::aidl::android::hardware::camera::device::ICameraDevice; |
| using ::aidl::android::hardware::camera::metadata::RequestAvailableColorSpaceProfilesMap; |
| using ::aidl::android::hardware::camera::metadata::RequestAvailableDynamicRangeProfilesMap; |
| using ::aidl::android::hardware::camera::metadata::SensorPixelMode; |
| using ::aidl::android::hardware::camera::provider::CameraIdAndStreamCombination; |
| using ::aidl::android::hardware::camera::provider::BnCameraProviderCallback; |
| |
| using ::ndk::ScopedAStatus; |
| |
| namespace { |
| const int32_t kBurstFrameCount = 10; |
| const uint32_t kMaxStillWidth = 2048; |
| const uint32_t kMaxStillHeight = 1536; |
| |
| const int64_t kEmptyFlushTimeoutMSec = 200; |
| namespace flags = com::android::internal::camera::flags; |
| |
| 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 |
| |
| TEST_P(CameraAidlTest, getCameraIdList) { |
| std::vector<std::string> idList; |
| ScopedAStatus ret = mProvider->getCameraIdList(&idList); |
| ASSERT_TRUE(ret.isOk()); |
| |
| for (size_t i = 0; i < idList.size(); i++) { |
| ALOGI("Camera Id[%zu] is %s", i, idList[i].c_str()); |
| } |
| } |
| |
| // Test if ICameraProvider::getVendorTags returns Status::OK |
| TEST_P(CameraAidlTest, getVendorTags) { |
| std::vector<VendorTagSection> vendorTags; |
| ScopedAStatus ret = mProvider->getVendorTags(&vendorTags); |
| |
| ASSERT_TRUE(ret.isOk()); |
| for (size_t i = 0; i < vendorTags.size(); i++) { |
| ALOGI("Vendor tag section %zu name %s", i, vendorTags[i].sectionName.c_str()); |
| for (auto& tag : vendorTags[i].tags) { |
| ALOGI("Vendor tag id %u name %s type %d", tag.tagId, tag.tagName.c_str(), |
| (int)tag.tagType); |
| } |
| } |
| } |
| |
| // Test if ICameraProvider::setCallback returns Status::OK |
| TEST_P(CameraAidlTest, setCallback) { |
| struct ProviderCb : public BnCameraProviderCallback { |
| ScopedAStatus cameraDeviceStatusChange(const std::string& cameraDeviceName, |
| CameraDeviceStatus newStatus) override { |
| ALOGI("camera device status callback name %s, status %d", cameraDeviceName.c_str(), |
| (int)newStatus); |
| return ScopedAStatus::ok(); |
| } |
| ScopedAStatus torchModeStatusChange(const std::string& cameraDeviceName, |
| TorchModeStatus newStatus) override { |
| ALOGI("Torch mode status callback name %s, status %d", cameraDeviceName.c_str(), |
| (int)newStatus); |
| return ScopedAStatus::ok(); |
| } |
| ScopedAStatus physicalCameraDeviceStatusChange(const std::string& cameraDeviceName, |
| const std::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 ScopedAStatus::ok(); |
| } |
| }; |
| |
| std::shared_ptr<ProviderCb> cb = ndk::SharedRefBase::make<ProviderCb>(); |
| ScopedAStatus ret = mProvider->setCallback(cb); |
| ASSERT_TRUE(ret.isOk()); |
| ret = mProvider->setCallback(nullptr); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| } |
| |
| // Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device |
| TEST_P(CameraAidlTest, getCameraDeviceInterface) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &cameraDevice); |
| ALOGI("getCameraDeviceInterface returns: %d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(cameraDevice, nullptr); |
| } |
| } |
| |
| // Verify that the device resource cost can be retrieved and the values are |
| // correct. |
| TEST_P(CameraAidlTest, getResourceCost) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& deviceName : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| ScopedAStatus ret = mProvider->getCameraDeviceInterface(deviceName, &cameraDevice); |
| ALOGI("getCameraDeviceInterface returns: %d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(cameraDevice, nullptr); |
| |
| CameraResourceCost resourceCost; |
| ret = cameraDevice->getResourceCost(&resourceCost); |
| ALOGI("getResourceCost returns: %d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| |
| 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()); |
| } |
| } |
| } |
| |
| // Validate the integrity of manual flash strength control metadata |
| TEST_P(CameraAidlTest, validateManualFlashStrengthControlKeys) { |
| if (flags::camera_manual_flash_strength_control()) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| for (const auto& name : cameraDeviceNames) { |
| ALOGI("validateManualFlashStrengthControlKeys: Testing camera device %s", name.c_str()); |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| ndk::ScopedAStatus ret = cameraDevice->getCameraCharacteristics(&meta); |
| ASSERT_TRUE(ret.isOk()); |
| const camera_metadata_t* staticMeta = |
| reinterpret_cast<const camera_metadata_t*>(meta.metadata.data()); |
| verifyManualFlashStrengthControlCharacteristics(staticMeta); |
| } |
| } else { |
| ALOGI("validateManualFlashStrengthControlKeys: Test skipped.\n"); |
| GTEST_SKIP(); |
| } |
| } |
| |
| TEST_P(CameraAidlTest, systemCameraTest) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::map<std::string, std::vector<SystemCameraKind>> hiddenPhysicalIdToLogicalMap; |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("systemCameraTest: Testing camera device %s", name.c_str()); |
| ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| CameraMetadata cameraCharacteristics; |
| ret = device->getCameraCharacteristics(&cameraCharacteristics); |
| ASSERT_TRUE(ret.isOk()); |
| |
| const camera_metadata_t* staticMeta = |
| reinterpret_cast<const camera_metadata_t*>(cameraCharacteristics.metadata.data()); |
| Status rc = isLogicalMultiCamera(staticMeta); |
| if (rc == Status::OPERATION_NOT_SUPPORTED) { |
| return; |
| } |
| |
| ASSERT_EQ(rc, Status::OK); |
| std::unordered_set<std::string> physicalIds; |
| ASSERT_EQ(getPhysicalCameraIds(staticMeta, &physicalIds), Status::OK); |
| SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC; |
| Status retStatus = getSystemCameraKind(staticMeta, &systemCameraKind); |
| ASSERT_EQ(retStatus, Status::OK); |
| |
| for (auto physicalId : physicalIds) { |
| bool isPublicId = false; |
| for (auto& deviceName : cameraDeviceNames) { |
| std::string publicVersion, publicId; |
| ASSERT_TRUE(matchDeviceName(deviceName, mProviderType, &publicVersion, &publicId)); |
| if (physicalId == publicId) { |
| isPublicId = true; |
| break; |
| } |
| } |
| |
| // For hidden physical cameras, collect their associated logical cameras |
| // and store the system camera kind. |
| if (!isPublicId) { |
| auto it = hiddenPhysicalIdToLogicalMap.find(physicalId); |
| if (it == hiddenPhysicalIdToLogicalMap.end()) { |
| hiddenPhysicalIdToLogicalMap.insert(std::make_pair( |
| physicalId, std::vector<SystemCameraKind>({systemCameraKind}))); |
| } else { |
| it->second.push_back(systemCameraKind); |
| } |
| } |
| } |
| } |
| |
| // Check that the system camera kind of the logical cameras associated with |
| // each hidden physical camera is the same. |
| for (const auto& it : hiddenPhysicalIdToLogicalMap) { |
| SystemCameraKind neededSystemCameraKind = it.second.front(); |
| for (auto foundSystemCamera : it.second) { |
| ASSERT_EQ(neededSystemCameraKind, foundSystemCamera); |
| } |
| } |
| } |
| |
| // Verify that the static camera characteristics can be retrieved |
| // successfully. |
| TEST_P(CameraAidlTest, getCameraCharacteristics) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str()); |
| ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device); |
| ALOGI("getCameraDeviceInterface returns: %d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| CameraMetadata chars; |
| ret = device->getCameraCharacteristics(&chars); |
| ASSERT_TRUE(ret.isOk()); |
| verifyCameraCharacteristics(chars); |
| verifyMonochromeCharacteristics(chars); |
| verifyRecommendedConfigs(chars); |
| verifyHighSpeedRecordingCharacteristics(name, chars); |
| verifyLogicalOrUltraHighResCameraMetadata(name, device, chars, cameraDeviceNames); |
| |
| ASSERT_TRUE(ret.isOk()); |
| |
| // getPhysicalCameraCharacteristics will fail for publicly |
| // advertised camera IDs. |
| std::string version, cameraId; |
| ASSERT_TRUE(matchDeviceName(name, mProviderType, &version, &cameraId)); |
| CameraMetadata devChars; |
| ret = device->getPhysicalCameraCharacteristics(cameraId, &devChars); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| ASSERT_EQ(0, devChars.metadata.size()); |
| } |
| } |
| |
| TEST_P(CameraAidlTest, getSessionCharacteristics) { |
| if (flags::feature_combination_query()) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("getSessionCharacteristics: Testing camera device %s", name.c_str()); |
| ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device); |
| ALOGI("getCameraDeviceInterface returns: %d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| CameraMetadata meta; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &device /*out*/); |
| |
| std::vector<AvailableStream> outputStreams; |
| camera_metadata_t* staticMeta = |
| reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| outputStreams.clear(); |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams)); |
| ASSERT_NE(0u, outputStreams.size()); |
| |
| AvailableStream sampleStream = outputStreams[0]; |
| |
| int32_t streamId = 0; |
| Stream stream = {streamId, |
| StreamType::OUTPUT, |
| sampleStream.width, |
| sampleStream.height, |
| static_cast<PixelFormat>(sampleStream.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| std::vector<Stream> streams = {stream}; |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config); |
| |
| CameraMetadata camera_chars; |
| ret = device->getCameraCharacteristics(&camera_chars); |
| ASSERT_TRUE(ret.isOk()); |
| |
| CameraMetadata session_chars; |
| ret = device->getSessionCharacteristics(config, &session_chars); |
| ASSERT_TRUE(ret.isOk()); |
| verifySessionCharacteristics(session_chars, camera_chars); |
| } |
| } else { |
| ALOGI("getSessionCharacteristics: Test skipped.\n"); |
| GTEST_SKIP(); |
| } |
| } |
| |
| // Verify that the torch strength level can be set and retrieved successfully. |
| TEST_P(CameraAidlTest, turnOnTorchWithStrengthLevel) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| std::shared_ptr<TorchProviderCb> cb = ndk::SharedRefBase::make<TorchProviderCb>(this); |
| ndk::ScopedAStatus ret = mProvider->setCallback(cb); |
| ASSERT_TRUE(ret.isOk()); |
| |
| for (const auto& name : cameraDeviceNames) { |
| int32_t defaultLevel; |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("%s: Testing camera device %s", __FUNCTION__, name.c_str()); |
| |
| ret = mProvider->getCameraDeviceInterface(name, &device); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| CameraMetadata chars; |
| ret = device->getCameraCharacteristics(&chars); |
| ASSERT_TRUE(ret.isOk()); |
| |
| const camera_metadata_t* staticMeta = |
| reinterpret_cast<const camera_metadata_t*>(chars.metadata.data()); |
| bool torchStrengthControlSupported = isTorchStrengthControlSupported(staticMeta); |
| camera_metadata_ro_entry entry; |
| int rc = find_camera_metadata_ro_entry(staticMeta, |
| ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL, &entry); |
| if (torchStrengthControlSupported) { |
| ASSERT_EQ(rc, 0); |
| ASSERT_GT(entry.count, 0); |
| defaultLevel = *entry.data.i32; |
| ALOGI("Default level is:%d", defaultLevel); |
| } |
| |
| mTorchStatus = TorchModeStatus::NOT_AVAILABLE; |
| ret = device->turnOnTorchWithStrengthLevel(2); |
| ALOGI("turnOnTorchWithStrengthLevel returns status: %d", ret.getServiceSpecificError()); |
| // OPERATION_NOT_SUPPORTED check |
| if (!torchStrengthControlSupported) { |
| ALOGI("Torch strength control not supported."); |
| ASSERT_EQ(static_cast<int32_t>(Status::OPERATION_NOT_SUPPORTED), |
| ret.getServiceSpecificError()); |
| } else { |
| { |
| ASSERT_TRUE(ret.isOk()); |
| std::unique_lock<std::mutex> l(mTorchLock); |
| while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kTorchTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout)); |
| } |
| ASSERT_EQ(TorchModeStatus::AVAILABLE_ON, mTorchStatus); |
| mTorchStatus = TorchModeStatus::NOT_AVAILABLE; |
| } |
| ALOGI("getTorchStrengthLevel: Testing"); |
| int32_t strengthLevel; |
| ret = device->getTorchStrengthLevel(&strengthLevel); |
| ASSERT_TRUE(ret.isOk()); |
| ALOGI("Torch strength level is : %d", strengthLevel); |
| ASSERT_EQ(strengthLevel, 2); |
| |
| // Turn OFF the torch and verify torch strength level is reset to default level. |
| ALOGI("Testing torch strength level reset after turning the torch OFF."); |
| ret = device->setTorchMode(false); |
| ASSERT_TRUE(ret.isOk()); |
| { |
| std::unique_lock<std::mutex> l(mTorchLock); |
| while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kTorchTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout)); |
| } |
| ASSERT_EQ(TorchModeStatus::AVAILABLE_OFF, mTorchStatus); |
| } |
| |
| ret = device->getTorchStrengthLevel(&strengthLevel); |
| ASSERT_TRUE(ret.isOk()); |
| ALOGI("Torch strength level after turning OFF torch is : %d", strengthLevel); |
| ASSERT_EQ(strengthLevel, defaultLevel); |
| } |
| } |
| } |
| |
| // In case it is supported verify that torch can be enabled. |
| // Check for corresponding torch callbacks as well. |
| TEST_P(CameraAidlTest, setTorchMode) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| std::shared_ptr<TorchProviderCb> cb = ndk::SharedRefBase::make<TorchProviderCb>(this); |
| ndk::ScopedAStatus ret = mProvider->setCallback(cb); |
| ALOGI("setCallback returns status: %d", ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(cb, nullptr); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("setTorchMode: Testing camera device %s", name.c_str()); |
| ret = mProvider->getCameraDeviceInterface(name, &device); |
| ALOGI("getCameraDeviceInterface returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| CameraMetadata metadata; |
| ret = device->getCameraCharacteristics(&metadata); |
| ALOGI("getCameraCharacteristics returns status:%d", ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| camera_metadata_t* staticMeta = |
| reinterpret_cast<camera_metadata_t*>(metadata.metadata.data()); |
| bool torchSupported = isTorchSupported(staticMeta); |
| |
| mTorchStatus = TorchModeStatus::NOT_AVAILABLE; |
| ret = device->setTorchMode(true); |
| ALOGI("setTorchMode returns status: %d", ret.getServiceSpecificError()); |
| if (!torchSupported) { |
| ASSERT_EQ(static_cast<int32_t>(Status::OPERATION_NOT_SUPPORTED), |
| ret.getServiceSpecificError()); |
| } else { |
| ASSERT_TRUE(ret.isOk()); |
| { |
| std::unique_lock<std::mutex> l(mTorchLock); |
| while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kTorchTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout)); |
| } |
| ASSERT_EQ(TorchModeStatus::AVAILABLE_ON, mTorchStatus); |
| mTorchStatus = TorchModeStatus::NOT_AVAILABLE; |
| } |
| |
| ret = device->setTorchMode(false); |
| ASSERT_TRUE(ret.isOk()); |
| { |
| std::unique_lock<std::mutex> l(mTorchLock); |
| while (TorchModeStatus::NOT_AVAILABLE == mTorchStatus) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kTorchTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mTorchCond.wait_until(l, timeout)); |
| } |
| ASSERT_EQ(TorchModeStatus::AVAILABLE_OFF, mTorchStatus); |
| } |
| } |
| } |
| } |
| |
| // Check dump functionality. |
| TEST_P(CameraAidlTest, dump) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("dump: Testing camera device %s", name.c_str()); |
| |
| ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device); |
| ALOGI("getCameraDeviceInterface returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| int raw_handle = open(kDumpOutput, O_RDWR); |
| ASSERT_GE(raw_handle, 0); |
| |
| auto retStatus = device->dump(raw_handle, nullptr, 0); |
| ASSERT_EQ(retStatus, ::android::OK); |
| close(raw_handle); |
| } |
| } |
| |
| // Open, dump, then close |
| TEST_P(CameraAidlTest, openClose) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("openClose: Testing camera device %s", name.c_str()); |
| ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device); |
| ALOGI("getCameraDeviceInterface returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| std::shared_ptr<EmptyDeviceCb> cb = ndk::SharedRefBase::make<EmptyDeviceCb>(); |
| |
| ret = device->open(cb, &mSession); |
| ASSERT_TRUE(ret.isOk()); |
| ALOGI("device::open returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_NE(mSession, nullptr); |
| int raw_handle = open(kDumpOutput, O_RDWR); |
| ASSERT_GE(raw_handle, 0); |
| |
| auto retStatus = device->dump(raw_handle, nullptr, 0); |
| ASSERT_EQ(retStatus, ::android::OK); |
| close(raw_handle); |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| // TODO: test all session API calls return INTERNAL_ERROR after close |
| // TODO: keep a wp copy here and verify session cannot be promoted out of this scope |
| } |
| } |
| |
| // Check whether all common default request settings can be successfully |
| // constructed. |
| TEST_P(CameraAidlTest, constructDefaultRequestSettings) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::shared_ptr<ICameraDevice> device; |
| ALOGI("constructDefaultRequestSettings: Testing camera device %s", name.c_str()); |
| ndk::ScopedAStatus ret = mProvider->getCameraDeviceInterface(name, &device); |
| ALOGI("getCameraDeviceInterface returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(device, nullptr); |
| |
| int32_t interfaceVersion; |
| ret = device->getInterfaceVersion(&interfaceVersion); |
| ASSERT_TRUE(ret.isOk()); |
| bool supportFeatureCombinationQuery = |
| (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3); |
| |
| std::shared_ptr<EmptyDeviceCb> cb = ndk::SharedRefBase::make<EmptyDeviceCb>(); |
| ret = device->open(cb, &mSession); |
| ALOGI("device::open returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(mSession, nullptr); |
| |
| for (int32_t t = (int32_t)RequestTemplate::PREVIEW; t <= (int32_t)RequestTemplate::MANUAL; |
| t++) { |
| RequestTemplate reqTemplate = (RequestTemplate)t; |
| CameraMetadata rawMetadata; |
| ret = mSession->constructDefaultRequestSettings(reqTemplate, &rawMetadata); |
| ALOGI("constructDefaultRequestSettings returns status:%d:%d", ret.getExceptionCode(), |
| ret.getServiceSpecificError()); |
| |
| if (reqTemplate == RequestTemplate::ZERO_SHUTTER_LAG || |
| reqTemplate == RequestTemplate::MANUAL) { |
| // optional templates |
| ASSERT_TRUE(ret.isOk() || static_cast<int32_t>(Status::ILLEGAL_ARGUMENT) == |
| ret.getServiceSpecificError()); |
| } else { |
| ASSERT_TRUE(ret.isOk()); |
| } |
| |
| if (ret.isOk()) { |
| validateDefaultRequestMetadata(reqTemplate, rawMetadata); |
| } else { |
| ASSERT_EQ(0u, rawMetadata.metadata.size()); |
| } |
| |
| if (flags::feature_combination_query()) { |
| if (supportFeatureCombinationQuery) { |
| CameraMetadata rawMetadata2; |
| ndk::ScopedAStatus ret2 = |
| device->constructDefaultRequestSettings(reqTemplate, &rawMetadata2); |
| |
| ASSERT_EQ(ret.isOk(), ret2.isOk()); |
| ASSERT_EQ(ret.getStatus(), ret2.getStatus()); |
| |
| ASSERT_EQ(rawMetadata.metadata.size(), rawMetadata2.metadata.size()); |
| if (ret2.isOk()) { |
| validateDefaultRequestMetadata(reqTemplate, rawMetadata2); |
| } |
| } |
| } |
| } |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Verify that all supported stream formats and sizes can be configured |
| // successfully. |
| TEST_P(CameraAidlTest, configureStreamsAvailableOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputStreams; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> device; |
| |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &device /*out*/); |
| |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| outputStreams.clear(); |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams)); |
| ASSERT_NE(0u, outputStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| int32_t streamConfigCounter = 0; |
| for (auto& it : outputStreams) { |
| Stream stream; |
| Dataspace dataspace = getDataspace(static_cast<PixelFormat>(it.format)); |
| stream.id = streamId; |
| stream.streamType = StreamType::OUTPUT; |
| stream.width = it.width; |
| stream.height = it.height; |
| stream.format = static_cast<PixelFormat>(it.format); |
| stream.dataSpace = dataspace; |
| stream.usage = static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER); |
| stream.rotation = StreamRotation::ROTATION_0; |
| stream.dynamicRangeProfile = RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD; |
| stream.useCase = ScalerAvailableStreamUseCases:: |
| ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; |
| stream.colorSpace = static_cast<int>( |
| RequestAvailableColorSpaceProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED); |
| |
| std::vector<Stream> streams = {stream}; |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| verifyStreamCombination(device, config, /*expectedStatus*/ true); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(halConfigs.size(), 1); |
| ASSERT_EQ(halConfigs[0].id, streamId); |
| |
| streamId++; |
| } |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Verify that mandatory concurrent streams and outputs are supported. |
| TEST_P(CameraAidlTest, configureConcurrentStreamsAvailableOutputs) { |
| struct CameraTestInfo { |
| CameraMetadata staticMeta; |
| std::shared_ptr<ICameraDeviceSession> session; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| StreamConfiguration config; |
| }; |
| |
| std::map<std::string, std::string> idToNameMap = getCameraDeviceIdToNameMap(mProvider); |
| std::vector<ConcurrentCameraIdCombination> concurrentDeviceCombinations = |
| getConcurrentDeviceCombinations(mProvider); |
| std::vector<AvailableStream> outputStreams; |
| for (const auto& cameraDeviceIds : concurrentDeviceCombinations) { |
| std::vector<CameraIdAndStreamCombination> cameraIdsAndStreamCombinations; |
| std::vector<CameraTestInfo> cameraTestInfos; |
| for (const auto& id : cameraDeviceIds.combination) { |
| CameraTestInfo cti; |
| auto it = idToNameMap.find(id); |
| ASSERT_TRUE(idToNameMap.end() != it); |
| std::string name = it->second; |
| |
| openEmptyDeviceSession(name, mProvider, &cti.session /*out*/, &cti.staticMeta /*out*/, |
| &cti.cameraDevice /*out*/); |
| |
| outputStreams.clear(); |
| camera_metadata_t* staticMeta = |
| reinterpret_cast<camera_metadata_t*>(cti.staticMeta.metadata.data()); |
| ASSERT_EQ(Status::OK, getMandatoryConcurrentStreams(staticMeta, &outputStreams)); |
| ASSERT_NE(0u, outputStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| std::vector<Stream> streams(outputStreams.size()); |
| size_t j = 0; |
| for (const auto& s : outputStreams) { |
| Stream stream; |
| Dataspace dataspace = getDataspace(static_cast<PixelFormat>(s.format)); |
| stream.id = streamId++; |
| stream.streamType = StreamType::OUTPUT; |
| stream.width = s.width; |
| stream.height = s.height; |
| stream.format = static_cast<PixelFormat>(s.format); |
| stream.usage = static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER); |
| stream.dataSpace = dataspace; |
| stream.rotation = StreamRotation::ROTATION_0; |
| stream.sensorPixelModesUsed = {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}; |
| stream.dynamicRangeProfile = RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD; |
| streams[j] = stream; |
| j++; |
| } |
| |
| // Add the created stream configs to cameraIdsAndStreamCombinations |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &cti.config, |
| jpegBufferSize); |
| |
| cti.config.streamConfigCounter = outputStreams.size(); |
| CameraIdAndStreamCombination cameraIdAndStreamCombination; |
| cameraIdAndStreamCombination.cameraId = id; |
| cameraIdAndStreamCombination.streamConfiguration = cti.config; |
| cameraIdsAndStreamCombinations.push_back(cameraIdAndStreamCombination); |
| cameraTestInfos.push_back(cti); |
| } |
| // Now verify that concurrent streams are supported |
| bool combinationSupported; |
| ndk::ScopedAStatus ret = mProvider->isConcurrentStreamCombinationSupported( |
| cameraIdsAndStreamCombinations, &combinationSupported); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(combinationSupported, true); |
| |
| // Test the stream can actually be configured |
| for (auto& cti : cameraTestInfos) { |
| if (cti.session != nullptr) { |
| verifyStreamCombination(cti.cameraDevice, cti.config, /*expectedStatus*/ true); |
| } |
| |
| if (cti.session != nullptr) { |
| std::vector<HalStream> streamConfigs; |
| ret = cti.session->configureStreams(cti.config, &streamConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(cti.config.streams.size(), streamConfigs.size()); |
| } |
| } |
| |
| for (auto& cti : cameraTestInfos) { |
| ret = cti.session->close(); |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| } |
| |
| // Check for correct handling of invalid/incorrect configuration parameters. |
| TEST_P(CameraAidlTest, configureStreamsInvalidOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputStreams; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| outputStreams.clear(); |
| |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams)); |
| ASSERT_NE(0u, outputStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| Stream stream = {streamId++, |
| StreamType::OUTPUT, |
| static_cast<uint32_t>(0), |
| static_cast<uint32_t>(0), |
| static_cast<PixelFormat>(outputStreams[0].format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| int32_t streamConfigCounter = 0; |
| std::vector<Stream> streams = {stream}; |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ false); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT) == |
| ret.getServiceSpecificError() || |
| static_cast<int32_t>(Status::INTERNAL_ERROR) == ret.getServiceSpecificError()); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| /*width*/ INT32_MAX, |
| /*height*/ INT32_MAX, |
| static_cast<PixelFormat>(outputStreams[0].format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| halConfigs.clear(); |
| ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| |
| for (auto& it : outputStreams) { |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| it.width, |
| it.height, |
| static_cast<PixelFormat>(UINT32_MAX), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| config.streamConfigCounter = streamConfigCounter++; |
| halConfigs.clear(); |
| ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), |
| ret.getServiceSpecificError()); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| it.width, |
| it.height, |
| static_cast<PixelFormat>(it.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| static_cast<StreamRotation>(UINT32_MAX), |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| halConfigs.clear(); |
| ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), |
| ret.getServiceSpecificError()); |
| } |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Check whether all supported ZSL output stream combinations can be |
| // configured successfully. |
| TEST_P(CameraAidlTest, configureStreamsZSLInputOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> inputStreams; |
| std::vector<AvailableZSLInputOutput> inputOutputMap; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| |
| Status rc = isZSLModeAvailable(staticMeta); |
| if (Status::OPERATION_NOT_SUPPORTED == rc) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| ASSERT_EQ(Status::OK, rc); |
| |
| inputStreams.clear(); |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, inputStreams)); |
| ASSERT_NE(0u, inputStreams.size()); |
| |
| inputOutputMap.clear(); |
| ASSERT_EQ(Status::OK, getZSLInputOutputMap(staticMeta, inputOutputMap)); |
| ASSERT_NE(0u, inputOutputMap.size()); |
| |
| bool supportMonoY8 = false; |
| if (Status::OK == isMonochromeCamera(staticMeta)) { |
| for (auto& it : inputStreams) { |
| if (it.format == static_cast<uint32_t>(PixelFormat::Y8)) { |
| supportMonoY8 = true; |
| break; |
| } |
| } |
| } |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| bool hasPrivToY8 = false, hasY8ToY8 = false, hasY8ToBlob = false; |
| uint32_t streamConfigCounter = 0; |
| for (auto& inputIter : inputOutputMap) { |
| AvailableStream input; |
| ASSERT_EQ(Status::OK, findLargestSize(inputStreams, inputIter.inputFormat, input)); |
| ASSERT_NE(0u, inputStreams.size()); |
| |
| if (inputIter.inputFormat == |
| static_cast<uint32_t>(PixelFormat::IMPLEMENTATION_DEFINED) && |
| inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::Y8)) { |
| hasPrivToY8 = true; |
| } else if (inputIter.inputFormat == static_cast<uint32_t>(PixelFormat::Y8)) { |
| if (inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::BLOB)) { |
| hasY8ToBlob = true; |
| } else if (inputIter.outputFormat == static_cast<uint32_t>(PixelFormat::Y8)) { |
| hasY8ToY8 = true; |
| } |
| } |
| AvailableStream outputThreshold = {INT32_MAX, INT32_MAX, inputIter.outputFormat}; |
| std::vector<AvailableStream> outputStreams; |
| ASSERT_EQ(Status::OK, |
| getAvailableOutputStreams(staticMeta, outputStreams, &outputThreshold)); |
| for (auto& outputIter : outputStreams) { |
| Dataspace outputDataSpace = |
| getDataspace(static_cast<PixelFormat>(outputIter.format)); |
| Stream zslStream = { |
| streamId++, |
| StreamType::OUTPUT, |
| input.width, |
| input.height, |
| static_cast<PixelFormat>(input.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC_USAGE_HW_CAMERA_ZSL), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| Stream inputStream = { |
| streamId++, |
| StreamType::INPUT, |
| input.width, |
| input.height, |
| static_cast<PixelFormat>(input.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>(0), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| Stream outputStream = { |
| streamId++, |
| StreamType::OUTPUT, |
| outputIter.width, |
| outputIter.height, |
| static_cast<PixelFormat>(outputIter.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| outputDataSpace, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| std::vector<Stream> streams = {inputStream, zslStream, outputStream}; |
| |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(3u, halConfigs.size()); |
| } |
| } |
| |
| if (supportMonoY8) { |
| if (Status::OK == isZSLModeAvailable(staticMeta, PRIV_REPROCESS)) { |
| ASSERT_TRUE(hasPrivToY8); |
| } |
| if (Status::OK == isZSLModeAvailable(staticMeta, YUV_REPROCESS)) { |
| ASSERT_TRUE(hasY8ToY8); |
| ASSERT_TRUE(hasY8ToBlob); |
| } |
| } |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Check whether session parameters are supported. If Hal support for them |
| // exist, then try to configure a preview stream using them. |
| TEST_P(CameraAidlTest, configureStreamsWithSessionParameters) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputPreviewStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| |
| std::shared_ptr<ICameraDevice> unusedCameraDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &unusedCameraDevice /*out*/); |
| camera_metadata_t* staticMetaBuffer = |
| reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| |
| std::unordered_set<int32_t> availableSessionKeys; |
| auto rc = getSupportedKeys(staticMetaBuffer, ANDROID_REQUEST_AVAILABLE_SESSION_KEYS, |
| &availableSessionKeys); |
| ASSERT_TRUE(Status::OK == rc); |
| if (availableSessionKeys.empty()) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| android::hardware::camera::common::V1_0::helper::CameraMetadata previewRequestSettings; |
| android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams, |
| modifiedSessionParams; |
| constructFilteredSettings(mSession, availableSessionKeys, RequestTemplate::PREVIEW, |
| &previewRequestSettings, &sessionParams); |
| if (sessionParams.isEmpty()) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| outputPreviewStreams.clear(); |
| |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMetaBuffer, outputPreviewStreams, |
| &previewThreshold)); |
| ASSERT_NE(0u, outputPreviewStreams.size()); |
| |
| Stream previewStream = { |
| 0, |
| StreamType::OUTPUT, |
| outputPreviewStreams[0].width, |
| outputPreviewStreams[0].height, |
| static_cast<PixelFormat>(outputPreviewStreams[0].format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| std::vector<Stream> streams = {previewStream}; |
| StreamConfiguration config; |
| |
| config.streams = streams; |
| config.operationMode = StreamConfigurationMode::NORMAL_MODE; |
| modifiedSessionParams = sessionParams; |
| auto sessionParamsBuffer = sessionParams.release(); |
| std::vector<uint8_t> rawSessionParam = |
| std::vector(reinterpret_cast<uint8_t*>(sessionParamsBuffer), |
| reinterpret_cast<uint8_t*>(sessionParamsBuffer) + |
| get_camera_metadata_size(sessionParamsBuffer)); |
| |
| config.sessionParams.metadata = rawSessionParam; |
| config.streamConfigCounter = 0; |
| config.streams = {previewStream}; |
| config.streamConfigCounter = 0; |
| config.multiResolutionInputImage = false; |
| |
| bool newSessionParamsAvailable = false; |
| for (const auto& it : availableSessionKeys) { |
| if (modifiedSessionParams.exists(it)) { |
| modifiedSessionParams.erase(it); |
| newSessionParamsAvailable = true; |
| break; |
| } |
| } |
| if (newSessionParamsAvailable) { |
| auto modifiedSessionParamsBuffer = modifiedSessionParams.release(); |
| verifySessionReconfigurationQuery(mSession, sessionParamsBuffer, |
| modifiedSessionParamsBuffer); |
| modifiedSessionParams.acquire(modifiedSessionParamsBuffer); |
| } |
| |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(1u, halConfigs.size()); |
| |
| sessionParams.acquire(sessionParamsBuffer); |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Verify that all supported preview + still capture stream combinations |
| // can be configured successfully. |
| TEST_P(CameraAidlTest, configureStreamsPreviewStillOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputBlobStreams; |
| std::vector<AvailableStream> outputPreviewStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| AvailableStream blobThreshold = {INT32_MAX, INT32_MAX, static_cast<int32_t>(PixelFormat::BLOB)}; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| |
| // Check if camera support depth only |
| if (isDepthOnly(staticMeta)) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| outputBlobStreams.clear(); |
| ASSERT_EQ(Status::OK, |
| getAvailableOutputStreams(staticMeta, outputBlobStreams, &blobThreshold)); |
| ASSERT_NE(0u, outputBlobStreams.size()); |
| |
| outputPreviewStreams.clear(); |
| ASSERT_EQ(Status::OK, |
| getAvailableOutputStreams(staticMeta, outputPreviewStreams, &previewThreshold)); |
| ASSERT_NE(0u, outputPreviewStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| uint32_t streamConfigCounter = 0; |
| |
| for (auto& blobIter : outputBlobStreams) { |
| for (auto& previewIter : outputPreviewStreams) { |
| Stream previewStream = { |
| streamId++, |
| StreamType::OUTPUT, |
| previewIter.width, |
| previewIter.height, |
| static_cast<PixelFormat>(previewIter.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| Stream blobStream = { |
| streamId++, |
| StreamType::OUTPUT, |
| blobIter.width, |
| blobIter.height, |
| static_cast<PixelFormat>(blobIter.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_CPU_READ), |
| Dataspace::JFIF, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| std::vector<Stream> streams = {previewStream, blobStream}; |
| StreamConfiguration config; |
| |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| config.streamConfigCounter = streamConfigCounter++; |
| verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); |
| |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(2u, halConfigs.size()); |
| } |
| } |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // In case constrained mode is supported, test whether it can be |
| // configured. Additionally check for common invalid inputs when |
| // using this mode. |
| TEST_P(CameraAidlTest, configureStreamsConstrainedOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| |
| Status rc = isConstrainedModeAvailable(staticMeta); |
| if (Status::OPERATION_NOT_SUPPORTED == rc) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| ASSERT_EQ(Status::OK, rc); |
| |
| AvailableStream hfrStream; |
| rc = pickConstrainedModeSize(staticMeta, hfrStream); |
| ASSERT_EQ(Status::OK, rc); |
| |
| int32_t streamId = 0; |
| uint32_t streamConfigCounter = 0; |
| Stream stream = {streamId, |
| StreamType::OUTPUT, |
| hfrStream.width, |
| hfrStream.height, |
| static_cast<PixelFormat>(hfrStream.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| std::vector<Stream> streams = {stream}; |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE, |
| &config); |
| |
| verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(1u, halConfigs.size()); |
| ASSERT_EQ(halConfigs[0].id, streamId); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| static_cast<uint32_t>(0), |
| static_cast<uint32_t>(0), |
| static_cast<PixelFormat>(hfrStream.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE, |
| &config); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| std::vector<HalStream> halConfig; |
| ret = mSession->configureStreams(config, &halConfig); |
| ASSERT_TRUE(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT) == |
| ret.getServiceSpecificError() || |
| static_cast<int32_t>(Status::INTERNAL_ERROR) == ret.getServiceSpecificError()); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| INT32_MAX, |
| INT32_MAX, |
| static_cast<PixelFormat>(hfrStream.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE, |
| &config); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| halConfigs.clear(); |
| ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| hfrStream.width, |
| hfrStream.height, |
| static_cast<PixelFormat>(UINT32_MAX), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| /*bufferSize*/ 0, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE, |
| &config); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| halConfigs.clear(); |
| ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Verify that all supported video + snapshot stream combinations can |
| // be configured successfully. |
| TEST_P(CameraAidlTest, configureStreamsVideoStillOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputBlobStreams; |
| std::vector<AvailableStream> outputVideoStreams; |
| AvailableStream videoThreshold = {kMaxVideoWidth, kMaxVideoHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| AvailableStream blobThreshold = {kMaxVideoWidth, kMaxVideoHeight, |
| static_cast<int32_t>(PixelFormat::BLOB)}; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| |
| // Check if camera support depth only |
| if (isDepthOnly(staticMeta)) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| outputBlobStreams.clear(); |
| ASSERT_EQ(Status::OK, |
| getAvailableOutputStreams(staticMeta, outputBlobStreams, &blobThreshold)); |
| ASSERT_NE(0u, outputBlobStreams.size()); |
| |
| outputVideoStreams.clear(); |
| ASSERT_EQ(Status::OK, |
| getAvailableOutputStreams(staticMeta, outputVideoStreams, &videoThreshold)); |
| ASSERT_NE(0u, outputVideoStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMeta, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| uint32_t streamConfigCounter = 0; |
| for (auto& blobIter : outputBlobStreams) { |
| for (auto& videoIter : outputVideoStreams) { |
| Stream videoStream = { |
| streamId++, |
| StreamType::OUTPUT, |
| videoIter.width, |
| videoIter.height, |
| static_cast<PixelFormat>(videoIter.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| Stream blobStream = { |
| streamId++, |
| StreamType::OUTPUT, |
| blobIter.width, |
| blobIter.height, |
| static_cast<PixelFormat>(blobIter.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_CPU_READ), |
| Dataspace::JFIF, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| /*groupId*/ -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| std::vector<Stream> streams = {videoStream, blobStream}; |
| StreamConfiguration config; |
| |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| std::vector<HalStream> halConfigs; |
| ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(2u, halConfigs.size()); |
| } |
| } |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Generate and verify a camera capture request |
| TEST_P(CameraAidlTest, processCaptureRequestPreview) { |
| // TODO(b/220897574): Failing with BUFFER_ERROR |
| processCaptureRequestInternal(GRALLOC1_CONSUMER_USAGE_HWCOMPOSER, RequestTemplate::PREVIEW, |
| false /*secureOnlyCameras*/); |
| } |
| |
| // Generate and verify a secure camera capture request |
| TEST_P(CameraAidlTest, processSecureCaptureRequest) { |
| processCaptureRequestInternal(GRALLOC1_PRODUCER_USAGE_PROTECTED, RequestTemplate::STILL_CAPTURE, |
| true /*secureOnlyCameras*/); |
| } |
| |
| TEST_P(CameraAidlTest, processCaptureRequestPreviewStabilization) { |
| std::unordered_map<std::string, nsecs_t> cameraDeviceToTimeLag; |
| processPreviewStabilizationCaptureRequestInternal(/*previewStabilizationOn*/ false, |
| cameraDeviceToTimeLag); |
| processPreviewStabilizationCaptureRequestInternal(/*previewStabilizationOn*/ true, |
| cameraDeviceToTimeLag); |
| } |
| |
| // Generate and verify a multi-camera capture request |
| TEST_P(CameraAidlTest, processMultiCaptureRequestPreview) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::YCBCR_420_888)}; |
| int64_t bufferId = 1; |
| uint32_t frameNumber = 1; |
| std::vector<uint8_t> settings; |
| std::vector<uint8_t> emptySettings; |
| std::string invalidPhysicalId = "-1"; |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::string version, deviceId; |
| ALOGI("processMultiCaptureRequestPreview: Test device %s", name.c_str()); |
| ASSERT_TRUE(matchDeviceName(name, mProviderType, &version, &deviceId)); |
| CameraMetadata metadata; |
| |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &metadata /*out*/, |
| &unusedDevice /*out*/); |
| |
| camera_metadata_t* staticMeta = |
| reinterpret_cast<camera_metadata_t*>(metadata.metadata.data()); |
| Status rc = isLogicalMultiCamera(staticMeta); |
| if (Status::OPERATION_NOT_SUPPORTED == rc) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| ASSERT_EQ(Status::OK, rc); |
| |
| std::unordered_set<std::string> physicalIds; |
| rc = getPhysicalCameraIds(staticMeta, &physicalIds); |
| ASSERT_TRUE(Status::OK == rc); |
| ASSERT_TRUE(physicalIds.size() > 1); |
| |
| std::unordered_set<int32_t> physicalRequestKeyIDs; |
| rc = getSupportedKeys(staticMeta, ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS, |
| &physicalRequestKeyIDs); |
| ASSERT_TRUE(Status::OK == rc); |
| if (physicalRequestKeyIDs.empty()) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| // The logical camera doesn't support any individual physical requests. |
| continue; |
| } |
| |
| android::hardware::camera::common::V1_0::helper::CameraMetadata defaultPreviewSettings; |
| android::hardware::camera::common::V1_0::helper::CameraMetadata filteredSettings; |
| constructFilteredSettings(mSession, physicalRequestKeyIDs, RequestTemplate::PREVIEW, |
| &defaultPreviewSettings, &filteredSettings); |
| if (filteredSettings.isEmpty()) { |
| // No physical device settings in default request. |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| const camera_metadata_t* settingsBuffer = defaultPreviewSettings.getAndLock(); |
| uint8_t* rawSettingsBuffer = (uint8_t*)settingsBuffer; |
| settings.assign(rawSettingsBuffer, |
| rawSettingsBuffer + get_camera_metadata_size(settingsBuffer)); |
| CameraMetadata settingsMetadata = {settings}; |
| overrideRotateAndCrop(&settingsMetadata); |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| |
| // Leave only 2 physical devices in the id set. |
| auto it = physicalIds.begin(); |
| std::string physicalDeviceId = *it; |
| it++; |
| physicalIds.erase(++it, physicalIds.end()); |
| ASSERT_EQ(physicalIds.size(), 2u); |
| |
| std::vector<HalStream> halStreams; |
| bool supportsPartialResults = false; |
| std::set<int32_t> halBufManagedStreamIds; |
| int32_t partialResultCount = 0; |
| Stream previewStream; |
| std::shared_ptr<DeviceCb> cb; |
| |
| configurePreviewStreams( |
| name, mProvider, &previewThreshold, physicalIds, &mSession, &previewStream, |
| &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/, |
| &halBufManagedStreamIds /*out*/, &cb /*out*/, 0 /*streamConfigCounter*/, true); |
| if (mSession == nullptr) { |
| // stream combination not supported by HAL, skip test for device |
| continue; |
| } |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| descriptor; |
| auto resultQueueRet = mSession->getCaptureResultMetadataQueue(&descriptor); |
| ASSERT_TRUE(resultQueueRet.isOk()); |
| std::shared_ptr<ResultMetadataQueue> resultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: HAL returns empty result metadata fmq, not use it", __func__); |
| resultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| |
| std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>( |
| static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults, |
| partialResultCount, physicalIds, resultQueue); |
| |
| std::vector<CaptureRequest> requests(1); |
| CaptureRequest& request = requests[0]; |
| request.frameNumber = frameNumber; |
| request.fmqSettingsSize = 0; |
| request.settings = settingsMetadata; |
| |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| |
| std::vector<buffer_handle_t> graphicBuffers; |
| graphicBuffers.reserve(halStreams.size()); |
| outputBuffers.resize(halStreams.size()); |
| size_t k = 0; |
| for (const auto& halStream : halStreams) { |
| buffer_handle_t buffer_handle; |
| bool useHalBufManagerForStream = |
| halBufManagedStreamIds.find(halStream.id) != halBufManagedStreamIds.end(); |
| if (useHalBufManagerForStream) { |
| outputBuffers[k] = {halStream.id, /*bufferId*/ 0, NativeHandle(), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| } else { |
| allocateGraphicBuffer(previewStream.width, previewStream.height, |
| android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStream.producerUsage), |
| static_cast<uint64_t>(halStream.consumerUsage)), |
| halStream.overrideFormat, &buffer_handle); |
| graphicBuffers.push_back(buffer_handle); |
| outputBuffers[k] = { |
| halStream.id, bufferId, ::android::makeToAidl(buffer_handle), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| bufferId++; |
| } |
| k++; |
| } |
| |
| std::vector<PhysicalCameraSetting> camSettings(1); |
| const camera_metadata_t* filteredSettingsBuffer = filteredSettings.getAndLock(); |
| uint8_t* rawFilteredSettingsBuffer = (uint8_t*)filteredSettingsBuffer; |
| camSettings[0].settings = {std::vector( |
| rawFilteredSettingsBuffer, |
| rawFilteredSettingsBuffer + get_camera_metadata_size(filteredSettingsBuffer))}; |
| overrideRotateAndCrop(&camSettings[0].settings); |
| camSettings[0].fmqSettingsSize = 0; |
| camSettings[0].physicalCameraId = physicalDeviceId; |
| |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| request.physicalCameraSettings = camSettings; |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| mInflightMap.clear(); |
| mInflightMap[frameNumber] = inflightReq; |
| } |
| |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| ndk::ScopedAStatus returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(returnStatus.isOk()); |
| ASSERT_EQ(numRequestProcessed, 1u); |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReq->errorCodeValid && |
| ((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| ASSERT_FALSE(inflightReq->errorCodeValid); |
| ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u); |
| |
| request.frameNumber++; |
| // Empty settings should be supported after the first call |
| // for repeating requests. |
| request.settings.metadata.clear(); |
| request.physicalCameraSettings[0].settings.metadata.clear(); |
| // The buffer has been registered to HAL by bufferId, so per |
| // API contract we should send a null handle for this buffer |
| request.outputBuffers[0].buffer = NativeHandle(); |
| mInflightMap.clear(); |
| inflightReq = std::make_shared<InFlightRequest>( |
| static_cast<ssize_t>(physicalIds.size()), false, supportsPartialResults, |
| partialResultCount, physicalIds, resultQueue); |
| mInflightMap[request.frameNumber] = inflightReq; |
| } |
| |
| returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(returnStatus.isOk()); |
| ASSERT_EQ(numRequestProcessed, 1u); |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReq->errorCodeValid && |
| ((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| ASSERT_FALSE(inflightReq->errorCodeValid); |
| ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u); |
| } |
| |
| // Invalid physical camera id should fail process requests |
| frameNumber++; |
| camSettings[0].physicalCameraId = invalidPhysicalId; |
| camSettings[0].settings.metadata = settings; |
| |
| request.physicalCameraSettings = camSettings; // Invalid camera settings |
| returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), |
| returnStatus.getServiceSpecificError()); |
| |
| defaultPreviewSettings.unlock(settingsBuffer); |
| filteredSettings.unlock(filteredSettingsBuffer); |
| |
| if (halBufManagedStreamIds.size() != 0) { |
| std::vector<int32_t> streamIds; |
| for (size_t i = 0; i < halStreams.size(); i++) { |
| int32_t streamId = halStreams[i].id; |
| if (halBufManagedStreamIds.find(streamId) != halBufManagedStreamIds.end()) { |
| streamIds.emplace_back(streamId); |
| } |
| } |
| verifyBuffersReturned(mSession, streamIds, cb); |
| } |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Generate and verify an ultra high resolution capture request |
| TEST_P(CameraAidlTest, processUltraHighResolutionRequest) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| int64_t bufferId = 1; |
| int32_t frameNumber = 1; |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::string version, deviceId; |
| ASSERT_TRUE(matchDeviceName(name, mProviderType, &version, &deviceId)); |
| CameraMetadata meta; |
| |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession, &meta, &unusedDevice); |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| if (!isUltraHighResolution(staticMeta)) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| CameraMetadata req; |
| android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings; |
| ndk::ScopedAStatus ret = |
| mSession->constructDefaultRequestSettings(RequestTemplate::STILL_CAPTURE, &req); |
| ASSERT_TRUE(ret.isOk()); |
| |
| const camera_metadata_t* metadata = |
| reinterpret_cast<const camera_metadata_t*>(req.metadata.data()); |
| size_t expectedSize = req.metadata.size(); |
| int result = validate_camera_metadata_structure(metadata, &expectedSize); |
| ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED)); |
| |
| size_t entryCount = get_camera_metadata_entry_count(metadata); |
| ASSERT_GT(entryCount, 0u); |
| defaultSettings = metadata; |
| uint8_t sensorPixelMode = |
| static_cast<uint8_t>(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION); |
| ASSERT_EQ(::android::OK, |
| defaultSettings.update(ANDROID_SENSOR_PIXEL_MODE, &sensorPixelMode, 1)); |
| |
| const camera_metadata_t* settingsBuffer = defaultSettings.getAndLock(); |
| uint8_t* rawSettingsBuffer = (uint8_t*)settingsBuffer; |
| settings.metadata = std::vector( |
| rawSettingsBuffer, rawSettingsBuffer + get_camera_metadata_size(settingsBuffer)); |
| overrideRotateAndCrop(&settings); |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| |
| std::vector<HalStream> halStreams; |
| bool supportsPartialResults = false; |
| std::set<int32_t> halBufManagedStreamIds; |
| int32_t partialResultCount = 0; |
| Stream previewStream; |
| std::shared_ptr<DeviceCb> cb; |
| |
| std::list<PixelFormat> pixelFormats = {PixelFormat::YCBCR_420_888, PixelFormat::RAW16}; |
| for (PixelFormat format : pixelFormats) { |
| previewStream.usage = |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_CPU_READ); |
| previewStream.dataSpace = Dataspace::UNKNOWN; |
| configureStreams(name, mProvider, format, &mSession, &previewStream, &halStreams, |
| &supportsPartialResults, &partialResultCount, &halBufManagedStreamIds, |
| &cb, 0, /*maxResolution*/ true); |
| ASSERT_NE(mSession, nullptr); |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| descriptor; |
| auto resultQueueRet = mSession->getCaptureResultMetadataQueue(&descriptor); |
| ASSERT_TRUE(resultQueueRet.isOk()); |
| |
| std::shared_ptr<ResultMetadataQueue> resultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: HAL returns empty result metadata fmq, not use it", __func__); |
| resultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| |
| std::vector<buffer_handle_t> graphicBuffers; |
| graphicBuffers.reserve(halStreams.size()); |
| std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>( |
| static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults, |
| partialResultCount, std::unordered_set<std::string>(), resultQueue); |
| |
| std::vector<CaptureRequest> requests(1); |
| CaptureRequest& request = requests[0]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(halStreams.size()); |
| |
| size_t k = 0; |
| for (const auto& halStream : halStreams) { |
| buffer_handle_t buffer_handle; |
| bool halBufManagerUsed = |
| halBufManagedStreamIds.find(halStream.id) != halBufManagedStreamIds.end(); |
| if (halBufManagerUsed) { |
| outputBuffers[k] = {halStream.id, 0, |
| NativeHandle(), BufferStatus::OK, |
| NativeHandle(), NativeHandle()}; |
| } else { |
| allocateGraphicBuffer(previewStream.width, previewStream.height, |
| android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStream.producerUsage), |
| static_cast<uint64_t>(halStream.consumerUsage)), |
| halStream.overrideFormat, &buffer_handle); |
| graphicBuffers.push_back(buffer_handle); |
| outputBuffers[k] = { |
| halStream.id, bufferId, ::android::makeToAidl(buffer_handle), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| bufferId++; |
| } |
| k++; |
| } |
| |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| request.frameNumber = frameNumber; |
| request.fmqSettingsSize = 0; |
| request.settings = settings; |
| request.inputWidth = 0; |
| request.inputHeight = 0; |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| mInflightMap.clear(); |
| mInflightMap[frameNumber] = inflightReq; |
| } |
| |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| ndk::ScopedAStatus returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(returnStatus.isOk()); |
| ASSERT_EQ(numRequestProcessed, 1u); |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReq->errorCodeValid && |
| ((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| ASSERT_FALSE(inflightReq->errorCodeValid); |
| ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u); |
| } |
| if (halBufManagedStreamIds.size()) { |
| std::vector<int32_t> streamIds; |
| for (size_t i = 0; i < halStreams.size(); i++) { |
| if (contains(halBufManagedStreamIds, halStreams[i].id)) { |
| streamIds.emplace_back(halStreams[i].id); |
| } |
| } |
| verifyBuffersReturned(mSession, streamIds, cb); |
| } |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| } |
| |
| // Generate and verify 10-bit dynamic range request |
| TEST_P(CameraAidlTest, process10BitDynamicRangeRequest) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| std::string version, deviceId; |
| ASSERT_TRUE(matchDeviceName(name, mProviderType, &version, &deviceId)); |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> device; |
| openEmptyDeviceSession(name, mProvider, &mSession, &meta, &device); |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| if (!is10BitDynamicRangeCapable(staticMeta)) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| std::vector<RequestAvailableDynamicRangeProfilesMap> profileList; |
| get10BitDynamicRangeProfiles(staticMeta, &profileList); |
| ASSERT_FALSE(profileList.empty()); |
| |
| CameraMetadata req; |
| android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings; |
| ndk::ScopedAStatus ret = |
| mSession->constructDefaultRequestSettings(RequestTemplate::PREVIEW, &req); |
| ASSERT_TRUE(ret.isOk()); |
| |
| const camera_metadata_t* metadata = |
| reinterpret_cast<const camera_metadata_t*>(req.metadata.data()); |
| size_t expectedSize = req.metadata.size(); |
| int result = validate_camera_metadata_structure(metadata, &expectedSize); |
| ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED)); |
| |
| size_t entryCount = get_camera_metadata_entry_count(metadata); |
| ASSERT_GT(entryCount, 0u); |
| defaultSettings = metadata; |
| |
| const camera_metadata_t* settingsBuffer = defaultSettings.getAndLock(); |
| uint8_t* rawSettingsBuffer = (uint8_t*)settingsBuffer; |
| settings.metadata = std::vector( |
| rawSettingsBuffer, rawSettingsBuffer + get_camera_metadata_size(settingsBuffer)); |
| overrideRotateAndCrop(&settings); |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| |
| std::vector<HalStream> halStreams; |
| bool supportsPartialResults = false; |
| std::set<int32_t> halBufManagedStreamIds; |
| int32_t partialResultCount = 0; |
| Stream previewStream; |
| std::shared_ptr<DeviceCb> cb; |
| for (const auto& profile : profileList) { |
| previewStream.usage = |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER); |
| previewStream.dataSpace = getDataspace(PixelFormat::IMPLEMENTATION_DEFINED); |
| configureStreams(name, mProvider, PixelFormat::IMPLEMENTATION_DEFINED, &mSession, |
| &previewStream, &halStreams, &supportsPartialResults, |
| &partialResultCount, &halBufManagedStreamIds, &cb, 0, |
| /*maxResolution*/ false, profile); |
| ASSERT_NE(mSession, nullptr); |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| descriptor; |
| auto resultQueueRet = mSession->getCaptureResultMetadataQueue(&descriptor); |
| ASSERT_TRUE(resultQueueRet.isOk()); |
| |
| std::shared_ptr<ResultMetadataQueue> resultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: HAL returns empty result metadata fmq, not use it", __func__); |
| resultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| |
| mInflightMap.clear(); |
| // Stream as long as needed to fill the Hal inflight queue |
| std::vector<CaptureRequest> requests(halStreams[0].maxBuffers); |
| |
| for (int32_t requestId = 0; requestId < requests.size(); requestId++) { |
| std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>( |
| static_cast<ssize_t>(halStreams.size()), false, supportsPartialResults, |
| partialResultCount, std::unordered_set<std::string>(), resultQueue); |
| |
| CaptureRequest& request = requests[requestId]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(halStreams.size()); |
| |
| size_t k = 0; |
| inflightReq->mOutstandingBufferIds.resize(halStreams.size()); |
| std::vector<buffer_handle_t> graphicBuffers; |
| graphicBuffers.reserve(halStreams.size()); |
| |
| auto bufferId = requestId + 1; // Buffer id value 0 is not valid |
| for (const auto& halStream : halStreams) { |
| buffer_handle_t buffer_handle; |
| if (contains(halBufManagedStreamIds, halStream.id)) { |
| outputBuffers[k] = {halStream.id, 0, |
| NativeHandle(), BufferStatus::OK, |
| NativeHandle(), NativeHandle()}; |
| } else { |
| auto usage = android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStream.producerUsage), |
| static_cast<uint64_t>(halStream.consumerUsage)); |
| allocateGraphicBuffer(previewStream.width, previewStream.height, usage, |
| halStream.overrideFormat, &buffer_handle); |
| |
| inflightReq->mOutstandingBufferIds[halStream.id][bufferId] = buffer_handle; |
| graphicBuffers.push_back(buffer_handle); |
| outputBuffers[k] = {halStream.id, bufferId, |
| android::makeToAidl(buffer_handle), BufferStatus::OK, NativeHandle(), |
| NativeHandle()}; |
| } |
| k++; |
| } |
| |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| request.frameNumber = bufferId; |
| request.fmqSettingsSize = 0; |
| request.settings = settings; |
| request.inputWidth = 0; |
| request.inputHeight = 0; |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| mInflightMap[bufferId] = inflightReq; |
| } |
| |
| } |
| |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| ndk::ScopedAStatus returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(returnStatus.isOk()); |
| ASSERT_EQ(numRequestProcessed, requests.size()); |
| |
| returnStatus = mSession->repeatingRequestEnd(requests.size() - 1, |
| std::vector<int32_t> {halStreams[0].id}); |
| ASSERT_TRUE(returnStatus.isOk()); |
| |
| // We are keeping frame numbers and buffer ids consistent. Buffer id value of 0 |
| // is used to indicate a buffer that is not present/available so buffer ids as well |
| // as frame numbers begin with 1. |
| for (int32_t frameNumber = 1; frameNumber <= requests.size(); frameNumber++) { |
| const auto& inflightReq = mInflightMap[frameNumber]; |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReq->errorCodeValid && |
| ((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| waitForReleaseFence(inflightReq->resultOutputBuffers); |
| |
| ASSERT_FALSE(inflightReq->errorCodeValid); |
| ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u); |
| verify10BitMetadata(mHandleImporter, *inflightReq, profile); |
| } |
| |
| if (halBufManagedStreamIds.size() != 0) { |
| std::vector<int32_t> streamIds; |
| for (size_t i = 0; i < halStreams.size(); i++) { |
| if (contains(halBufManagedStreamIds, halStreams[i].id)) { |
| streamIds.emplace_back(halStreams[i].id); |
| } |
| } |
| mSession->signalStreamFlush(streamIds, /*streamConfigCounter*/ 0); |
| cb->waitForBuffersReturned(); |
| } |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| } |
| |
| TEST_P(CameraAidlTest, process8BitColorSpaceRequests) { |
| static int profiles[] = {ColorSpaceNamed::DISPLAY_P3, ColorSpaceNamed::SRGB}; |
| |
| for (int32_t i = 0; i < sizeof(profiles) / sizeof(profiles[0]); i++) { |
| processColorSpaceRequest(static_cast<RequestAvailableColorSpaceProfilesMap>(profiles[i]), |
| static_cast<RequestAvailableDynamicRangeProfilesMap>( |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD)); |
| } |
| } |
| |
| TEST_P(CameraAidlTest, process10BitColorSpaceRequests) { |
| static const camera_metadata_enum_android_request_available_dynamic_range_profiles_map |
| dynamicRangeProfiles[] = { |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM, |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO |
| }; |
| |
| // Process all dynamic range profiles with BT2020_HLG |
| for (int32_t i = 0; i < sizeof(dynamicRangeProfiles) / sizeof(dynamicRangeProfiles[0]); i++) { |
| processColorSpaceRequest( |
| static_cast<RequestAvailableColorSpaceProfilesMap>(ColorSpaceNamed::BT2020_HLG), |
| static_cast<RequestAvailableDynamicRangeProfilesMap>(dynamicRangeProfiles[i])); |
| } |
| } |
| |
| TEST_P(CameraAidlTest, processZoomSettingsOverrideRequests) { |
| const int32_t kFrameCount = 5; |
| const int32_t kTestCases = 2; |
| const bool kOverrideSequence[kTestCases][kFrameCount] = {// ZOOM, ZOOM, ZOOM, ZOOM, ZOOM; |
| {true, true, true, true, true}, |
| // OFF, ZOOM, ZOOM, ZOOM, OFF; |
| {false, true, true, true, false}}; |
| const bool kExpectedOverrideResults[kTestCases][kFrameCount] = { |
| // All resuls should be overridden except the last one. The last result's |
| // zoom doesn't have speed-up. |
| {true, true, true, true, false}, |
| // Because we require at least 1 frame speed-up, request #1, #2 and #3 |
| // will be overridden. |
| {true, true, true, false, false}}; |
| |
| for (int i = 0; i < kTestCases; i++) { |
| processZoomSettingsOverrideRequests(kFrameCount, kOverrideSequence[i], |
| kExpectedOverrideResults[i]); |
| } |
| } |
| |
| // Generate and verify a burst containing alternating sensor sensitivity values |
| TEST_P(CameraAidlTest, processCaptureRequestBurstISO) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| int64_t bufferId = 1; |
| int32_t frameNumber = 1; |
| float isoTol = .03f; |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| settings.metadata.clear(); |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &unusedDevice /*out*/); |
| camera_metadata_t* staticMetaBuffer = |
| clone_camera_metadata(reinterpret_cast<camera_metadata_t*>(meta.metadata.data())); |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata staticMeta( |
| staticMetaBuffer); |
| |
| camera_metadata_entry_t hwLevel = staticMeta.find(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL); |
| ASSERT_TRUE(0 < hwLevel.count); |
| if (ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED == hwLevel.data.u8[0] || |
| ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL == hwLevel.data.u8[0]) { |
| // Limited/External devices can skip this test |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| camera_metadata_entry_t isoRange = staticMeta.find(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE); |
| ASSERT_EQ(isoRange.count, 2u); |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| |
| bool supportsPartialResults = false; |
| bool useHalBufManager = false; |
| int32_t partialResultCount = 0; |
| Stream previewStream; |
| std::vector<HalStream> halStreams; |
| std::shared_ptr<DeviceCb> cb; |
| configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/, |
| &previewStream /*out*/, &halStreams /*out*/, |
| &supportsPartialResults /*out*/, &partialResultCount /*out*/, |
| &useHalBufManager /*out*/, &cb /*out*/); |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| descriptor; |
| auto resultQueueRet = mSession->getCaptureResultMetadataQueue(&descriptor); |
| std::shared_ptr<ResultMetadataQueue> resultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| ASSERT_TRUE(resultQueueRet.isOk()); |
| if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: HAL returns empty result metadata fmq, not use it", __func__); |
| resultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| |
| ret = mSession->constructDefaultRequestSettings(RequestTemplate::PREVIEW, &settings); |
| ASSERT_TRUE(ret.isOk()); |
| |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata requestMeta; |
| std::vector<CaptureRequest> requests(kBurstFrameCount); |
| std::vector<buffer_handle_t> buffers(kBurstFrameCount); |
| std::vector<std::shared_ptr<InFlightRequest>> inflightReqs(kBurstFrameCount); |
| std::vector<int32_t> isoValues(kBurstFrameCount); |
| std::vector<CameraMetadata> requestSettings(kBurstFrameCount); |
| |
| for (int32_t i = 0; i < kBurstFrameCount; i++) { |
| std::unique_lock<std::mutex> l(mLock); |
| CaptureRequest& request = requests[i]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(1); |
| StreamBuffer& outputBuffer = outputBuffers[0]; |
| |
| isoValues[i] = ((i % 2) == 0) ? isoRange.data.i32[0] : isoRange.data.i32[1]; |
| if (useHalBufManager) { |
| outputBuffer = {halStreams[0].id, 0, |
| NativeHandle(), BufferStatus::OK, |
| NativeHandle(), NativeHandle()}; |
| } else { |
| allocateGraphicBuffer(previewStream.width, previewStream.height, |
| android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStreams[0].producerUsage), |
| static_cast<uint64_t>(halStreams[0].consumerUsage)), |
| halStreams[0].overrideFormat, &buffers[i]); |
| outputBuffer = {halStreams[0].id, bufferId + i, ::android::makeToAidl(buffers[i]), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| } |
| |
| requestMeta.append(reinterpret_cast<camera_metadata_t*>(settings.metadata.data())); |
| |
| // Disable all 3A routines |
| uint8_t mode = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF); |
| ASSERT_EQ(::android::OK, requestMeta.update(ANDROID_CONTROL_MODE, &mode, 1)); |
| ASSERT_EQ(::android::OK, |
| requestMeta.update(ANDROID_SENSOR_SENSITIVITY, &isoValues[i], 1)); |
| camera_metadata_t* metaBuffer = requestMeta.release(); |
| uint8_t* rawMetaBuffer = reinterpret_cast<uint8_t*>(metaBuffer); |
| requestSettings[i].metadata = std::vector( |
| rawMetaBuffer, rawMetaBuffer + get_camera_metadata_size(metaBuffer)); |
| overrideRotateAndCrop(&(requestSettings[i])); |
| |
| request.frameNumber = frameNumber + i; |
| request.fmqSettingsSize = 0; |
| request.settings = requestSettings[i]; |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| |
| inflightReqs[i] = std::make_shared<InFlightRequest>(1, false, supportsPartialResults, |
| partialResultCount, resultQueue); |
| mInflightMap[frameNumber + i] = inflightReqs[i]; |
| } |
| |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| |
| ndk::ScopedAStatus returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(returnStatus.isOk()); |
| ASSERT_EQ(numRequestProcessed, kBurstFrameCount); |
| |
| for (size_t i = 0; i < kBurstFrameCount; i++) { |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReqs[i]->errorCodeValid && ((0 < inflightReqs[i]->numBuffersLeft) || |
| (!inflightReqs[i]->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| ASSERT_FALSE(inflightReqs[i]->errorCodeValid); |
| ASSERT_NE(inflightReqs[i]->resultOutputBuffers.size(), 0u); |
| ASSERT_EQ(previewStream.id, inflightReqs[i]->resultOutputBuffers[0].buffer.streamId); |
| ASSERT_FALSE(inflightReqs[i]->collectedResult.isEmpty()); |
| ASSERT_TRUE(inflightReqs[i]->collectedResult.exists(ANDROID_SENSOR_SENSITIVITY)); |
| camera_metadata_entry_t isoResult = |
| inflightReqs[i]->collectedResult.find(ANDROID_SENSOR_SENSITIVITY); |
| ASSERT_TRUE(std::abs(isoResult.data.i32[0] - isoValues[i]) <= |
| std::round(isoValues[i] * isoTol)); |
| } |
| |
| if (useHalBufManager) { |
| verifyBuffersReturned(mSession, previewStream.id, cb); |
| } |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Test whether an incorrect capture request with missing settings will |
| // be reported correctly. |
| TEST_P(CameraAidlTest, processCaptureRequestInvalidSinglePreview) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputPreviewStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| int64_t bufferId = 1; |
| int32_t frameNumber = 1; |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| Stream previewStream; |
| std::vector<HalStream> halStreams; |
| std::shared_ptr<DeviceCb> cb; |
| bool supportsPartialResults = false; |
| bool useHalBufManager = false; |
| int32_t partialResultCount = 0; |
| configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/, |
| &previewStream /*out*/, &halStreams /*out*/, |
| &supportsPartialResults /*out*/, &partialResultCount /*out*/, |
| &useHalBufManager /*out*/, &cb /*out*/); |
| ASSERT_NE(mSession, nullptr); |
| ASSERT_FALSE(halStreams.empty()); |
| |
| buffer_handle_t buffer_handle = nullptr; |
| |
| if (useHalBufManager) { |
| bufferId = 0; |
| } else { |
| allocateGraphicBuffer(previewStream.width, previewStream.height, |
| android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStreams[0].producerUsage), |
| static_cast<uint64_t>(halStreams[0].consumerUsage)), |
| halStreams[0].overrideFormat, &buffer_handle); |
| } |
| |
| std::vector<CaptureRequest> requests(1); |
| CaptureRequest& request = requests[0]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(1); |
| StreamBuffer& outputBuffer = outputBuffers[0]; |
| |
| outputBuffer = { |
| halStreams[0].id, |
| bufferId, |
| buffer_handle == nullptr ? NativeHandle() : ::android::makeToAidl(buffer_handle), |
| BufferStatus::OK, |
| NativeHandle(), |
| NativeHandle()}; |
| |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| request.frameNumber = frameNumber; |
| request.fmqSettingsSize = 0; |
| request.settings = settings; |
| |
| // Settings were not correctly initialized, we should fail here |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| ndk::ScopedAStatus ret = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| ASSERT_EQ(numRequestProcessed, 0u); |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Verify camera offline session behavior |
| TEST_P(CameraAidlTest, switchToOffline) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| AvailableStream threshold = {kMaxStillWidth, kMaxStillHeight, |
| static_cast<int32_t>(PixelFormat::BLOB)}; |
| int64_t bufferId = 1; |
| int32_t frameNumber = 1; |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| { |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &unusedDevice); |
| camera_metadata_t* staticMetaBuffer = clone_camera_metadata( |
| reinterpret_cast<camera_metadata_t*>(meta.metadata.data())); |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata staticMeta( |
| staticMetaBuffer); |
| |
| if (isOfflineSessionSupported(staticMetaBuffer) != Status::OK) { |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| |
| bool supportsPartialResults = false; |
| int32_t partialResultCount = 0; |
| Stream stream; |
| std::vector<HalStream> halStreams; |
| std::shared_ptr<DeviceCb> cb; |
| int32_t jpegBufferSize; |
| std::set<int32_t> halBufManagedStreamIds; |
| configureOfflineStillStream(name, mProvider, &threshold, &mSession /*out*/, &stream /*out*/, |
| &halStreams /*out*/, &supportsPartialResults /*out*/, |
| &partialResultCount /*out*/, &cb /*out*/, |
| &jpegBufferSize /*out*/, &halBufManagedStreamIds /*out*/); |
| |
| auto ret = mSession->constructDefaultRequestSettings(RequestTemplate::STILL_CAPTURE, |
| &settings); |
| ASSERT_TRUE(ret.isOk()); |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| descriptor; |
| |
| ndk::ScopedAStatus resultQueueRet = mSession->getCaptureResultMetadataQueue(&descriptor); |
| ASSERT_TRUE(resultQueueRet.isOk()); |
| std::shared_ptr<ResultMetadataQueue> resultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: HAL returns empty result metadata fmq, not use it", __func__); |
| resultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| |
| ::android::hardware::camera::common::V1_0::helper::CameraMetadata requestMeta; |
| |
| std::vector<buffer_handle_t> buffers(kBurstFrameCount); |
| std::vector<std::shared_ptr<InFlightRequest>> inflightReqs(kBurstFrameCount); |
| std::vector<CameraMetadata> requestSettings(kBurstFrameCount); |
| |
| std::vector<CaptureRequest> requests(kBurstFrameCount); |
| |
| HalStream halStream = halStreams[0]; |
| for (uint32_t i = 0; i < kBurstFrameCount; i++) { |
| CaptureRequest& request = requests[i]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(1); |
| StreamBuffer& outputBuffer = outputBuffers[0]; |
| |
| std::unique_lock<std::mutex> l(mLock); |
| if (contains(halBufManagedStreamIds, halStream.id)) { |
| outputBuffer = {halStream.id, 0, NativeHandle(), BufferStatus::OK, NativeHandle(), |
| NativeHandle()}; |
| } else { |
| // jpeg buffer (w,h) = (blobLen, 1) |
| allocateGraphicBuffer(jpegBufferSize, /*height*/ 1, |
| android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStream.producerUsage), |
| static_cast<uint64_t>(halStream.consumerUsage)), |
| halStream.overrideFormat, &buffers[i]); |
| outputBuffer = {halStream.id, bufferId + i, ::android::makeToAidl(buffers[i]), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| } |
| |
| requestMeta.clear(); |
| requestMeta.append(reinterpret_cast<camera_metadata_t*>(settings.metadata.data())); |
| |
| camera_metadata_t* metaBuffer = requestMeta.release(); |
| uint8_t* rawMetaBuffer = reinterpret_cast<uint8_t*>(metaBuffer); |
| requestSettings[i].metadata = std::vector( |
| rawMetaBuffer, rawMetaBuffer + get_camera_metadata_size(metaBuffer)); |
| overrideRotateAndCrop(&requestSettings[i]); |
| |
| request.frameNumber = frameNumber + i; |
| request.fmqSettingsSize = 0; |
| request.settings = requestSettings[i]; |
| request.inputBuffer = {/*streamId*/ -1, |
| /*bufferId*/ 0, NativeHandle(), |
| BufferStatus::ERROR, NativeHandle(), |
| NativeHandle()}; |
| |
| inflightReqs[i] = std::make_shared<InFlightRequest>(1, false, supportsPartialResults, |
| partialResultCount, resultQueue); |
| mInflightMap[frameNumber + i] = inflightReqs[i]; |
| } |
| |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| |
| ndk::ScopedAStatus returnStatus = |
| mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(returnStatus.isOk()); |
| ASSERT_EQ(numRequestProcessed, kBurstFrameCount); |
| |
| std::vector<int32_t> offlineStreamIds = {halStream.id}; |
| CameraOfflineSessionInfo offlineSessionInfo; |
| std::shared_ptr<ICameraOfflineSession> offlineSession; |
| returnStatus = |
| mSession->switchToOffline(offlineStreamIds, &offlineSessionInfo, &offlineSession); |
| |
| if (!halStreams[0].supportOffline) { |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), |
| returnStatus.getServiceSpecificError()); |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| ASSERT_TRUE(returnStatus.isOk()); |
| // Hal might be unable to find any requests qualified for offline mode. |
| if (offlineSession == nullptr) { |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| ASSERT_EQ(offlineSessionInfo.offlineStreams.size(), 1u); |
| ASSERT_EQ(offlineSessionInfo.offlineStreams[0].id, halStream.id); |
| ASSERT_NE(offlineSessionInfo.offlineRequests.size(), 0u); |
| |
| // close device session to make sure offline session does not rely on it |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| offlineResultDescriptor; |
| |
| auto offlineResultQueueRet = |
| offlineSession->getCaptureResultMetadataQueue(&offlineResultDescriptor); |
| std::shared_ptr<ResultMetadataQueue> offlineResultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| if (!offlineResultQueue->isValid() || offlineResultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: offline session returns empty result metadata fmq, not use it", __func__); |
| offlineResultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| ASSERT_TRUE(offlineResultQueueRet.isOk()); |
| |
| updateInflightResultQueue(offlineResultQueue); |
| |
| ret = offlineSession->setCallback(cb); |
| ASSERT_TRUE(ret.isOk()); |
| |
| for (size_t i = 0; i < kBurstFrameCount; i++) { |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReqs[i]->errorCodeValid && ((0 < inflightReqs[i]->numBuffersLeft) || |
| (!inflightReqs[i]->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| ASSERT_FALSE(inflightReqs[i]->errorCodeValid); |
| ASSERT_NE(inflightReqs[i]->resultOutputBuffers.size(), 0u); |
| ASSERT_EQ(stream.id, inflightReqs[i]->resultOutputBuffers[0].buffer.streamId); |
| ASSERT_FALSE(inflightReqs[i]->collectedResult.isEmpty()); |
| } |
| |
| ret = offlineSession->close(); |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Check whether an invalid capture request with missing output buffers |
| // will be reported correctly. |
| TEST_P(CameraAidlTest, processCaptureRequestInvalidBuffer) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputBlobStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| int32_t frameNumber = 1; |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| Stream previewStream; |
| std::vector<HalStream> halStreams; |
| std::shared_ptr<DeviceCb> cb; |
| bool supportsPartialResults = false; |
| bool useHalBufManager = false; |
| int32_t partialResultCount = 0; |
| configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/, |
| &previewStream /*out*/, &halStreams /*out*/, |
| &supportsPartialResults /*out*/, &partialResultCount /*out*/, |
| &useHalBufManager /*out*/, &cb /*out*/); |
| |
| RequestTemplate reqTemplate = RequestTemplate::PREVIEW; |
| ndk::ScopedAStatus ret = mSession->constructDefaultRequestSettings(reqTemplate, &settings); |
| ASSERT_TRUE(ret.isOk()); |
| overrideRotateAndCrop(&settings); |
| |
| std::vector<CaptureRequest> requests(1); |
| CaptureRequest& request = requests[0]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(1); |
| // Empty output buffer |
| outputBuffers[0] = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| request.frameNumber = frameNumber; |
| request.fmqSettingsSize = 0; |
| request.settings = settings; |
| |
| // Output buffers are missing, we should fail here |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| ret = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); |
| ASSERT_EQ(numRequestProcessed, 0u); |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Generate, trigger and flush a preview request |
| TEST_P(CameraAidlTest, flushPreviewRequest) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputPreviewStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| int64_t bufferId = 1; |
| int32_t frameNumber = 1; |
| CameraMetadata settings; |
| |
| for (const auto& name : cameraDeviceNames) { |
| Stream previewStream; |
| std::vector<HalStream> halStreams; |
| std::shared_ptr<DeviceCb> cb; |
| bool supportsPartialResults = false; |
| bool useHalBufManager = false; |
| int32_t partialResultCount = 0; |
| |
| configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/, |
| &previewStream /*out*/, &halStreams /*out*/, |
| &supportsPartialResults /*out*/, &partialResultCount /*out*/, |
| &useHalBufManager /*out*/, &cb /*out*/); |
| |
| ASSERT_NE(mSession, nullptr); |
| ASSERT_NE(cb, nullptr); |
| ASSERT_FALSE(halStreams.empty()); |
| |
| ::aidl::android::hardware::common::fmq::MQDescriptor< |
| int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite> |
| descriptor; |
| |
| auto resultQueueRet = mSession->getCaptureResultMetadataQueue(&descriptor); |
| std::shared_ptr<ResultMetadataQueue> resultQueue = |
| std::make_shared<ResultMetadataQueue>(descriptor); |
| ASSERT_TRUE(resultQueueRet.isOk()); |
| if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) { |
| ALOGE("%s: HAL returns empty result metadata fmq, not use it", __func__); |
| resultQueue = nullptr; |
| // Don't use the queue onwards. |
| } |
| |
| std::shared_ptr<InFlightRequest> inflightReq = std::make_shared<InFlightRequest>( |
| 1, false, supportsPartialResults, partialResultCount, resultQueue); |
| RequestTemplate reqTemplate = RequestTemplate::PREVIEW; |
| |
| ndk::ScopedAStatus ret = mSession->constructDefaultRequestSettings(reqTemplate, &settings); |
| ASSERT_TRUE(ret.isOk()); |
| overrideRotateAndCrop(&settings); |
| |
| buffer_handle_t buffer_handle; |
| std::vector<CaptureRequest> requests(1); |
| CaptureRequest& request = requests[0]; |
| std::vector<StreamBuffer>& outputBuffers = request.outputBuffers; |
| outputBuffers.resize(1); |
| StreamBuffer& outputBuffer = outputBuffers[0]; |
| if (useHalBufManager) { |
| bufferId = 0; |
| outputBuffer = {halStreams[0].id, bufferId, NativeHandle(), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| } else { |
| allocateGraphicBuffer(previewStream.width, previewStream.height, |
| android_convertGralloc1To0Usage( |
| static_cast<uint64_t>(halStreams[0].producerUsage), |
| static_cast<uint64_t>(halStreams[0].consumerUsage)), |
| halStreams[0].overrideFormat, &buffer_handle); |
| outputBuffer = {halStreams[0].id, bufferId, ::android::makeToAidl(buffer_handle), |
| BufferStatus::OK, NativeHandle(), NativeHandle()}; |
| } |
| |
| request.frameNumber = frameNumber; |
| request.fmqSettingsSize = 0; |
| request.settings = settings; |
| request.inputBuffer = { |
| -1, 0, NativeHandle(), BufferStatus::ERROR, NativeHandle(), NativeHandle()}; |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| mInflightMap.clear(); |
| mInflightMap[frameNumber] = inflightReq; |
| } |
| |
| int32_t numRequestProcessed = 0; |
| std::vector<BufferCache> cachesToRemove; |
| ret = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_EQ(numRequestProcessed, 1u); |
| |
| // Flush before waiting for request to complete. |
| ndk::ScopedAStatus returnStatus = mSession->flush(); |
| ASSERT_TRUE(returnStatus.isOk()); |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| while (!inflightReq->errorCodeValid && |
| ((0 < inflightReq->numBuffersLeft) || (!inflightReq->haveResultMetadata))) { |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::seconds(kStreamBufferTimeoutSec); |
| ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| if (!inflightReq->errorCodeValid) { |
| ASSERT_NE(inflightReq->resultOutputBuffers.size(), 0u); |
| ASSERT_EQ(previewStream.id, inflightReq->resultOutputBuffers[0].buffer.streamId); |
| } else { |
| switch (inflightReq->errorCode) { |
| case ErrorCode::ERROR_REQUEST: |
| case ErrorCode::ERROR_RESULT: |
| case ErrorCode::ERROR_BUFFER: |
| // Expected |
| break; |
| case ErrorCode::ERROR_DEVICE: |
| default: |
| FAIL() << "Unexpected error:" |
| << static_cast<uint32_t>(inflightReq->errorCode); |
| } |
| } |
| } |
| |
| if (useHalBufManager) { |
| verifyBuffersReturned(mSession, previewStream.id, cb); |
| } |
| |
| ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Verify that camera flushes correctly without any pending requests. |
| TEST_P(CameraAidlTest, flushEmpty) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputPreviewStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| |
| for (const auto& name : cameraDeviceNames) { |
| Stream previewStream; |
| std::vector<HalStream> halStreams; |
| std::shared_ptr<DeviceCb> cb; |
| bool supportsPartialResults = false; |
| bool useHalBufManager = false; |
| |
| int32_t partialResultCount = 0; |
| configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/, |
| &previewStream /*out*/, &halStreams /*out*/, |
| &supportsPartialResults /*out*/, &partialResultCount /*out*/, |
| &useHalBufManager /*out*/, &cb /*out*/); |
| |
| ndk::ScopedAStatus returnStatus = mSession->flush(); |
| ASSERT_TRUE(returnStatus.isOk()); |
| |
| { |
| std::unique_lock<std::mutex> l(mLock); |
| auto timeout = std::chrono::system_clock::now() + |
| std::chrono::milliseconds(kEmptyFlushTimeoutMSec); |
| ASSERT_EQ(std::cv_status::timeout, mResultCondition.wait_until(l, timeout)); |
| } |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Test camera provider notify method |
| TEST_P(CameraAidlTest, providerDeviceStateNotification) { |
| notifyDeviceState(ICameraProvider::DEVICE_STATE_BACK_COVERED); |
| notifyDeviceState(ICameraProvider::DEVICE_STATE_NORMAL); |
| } |
| |
| // Verify that all supported stream formats and sizes can be configured |
| // successfully for injection camera. |
| TEST_P(CameraAidlTest, configureInjectionStreamsAvailableOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputStreams; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata metadata; |
| |
| std::shared_ptr<ICameraInjectionSession> injectionSession; |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyInjectionSession(name, mProvider, &injectionSession /*out*/, &metadata /*out*/, |
| &unusedDevice /*out*/); |
| if (injectionSession == nullptr) { |
| continue; |
| } |
| |
| camera_metadata_t* staticMetaBuffer = |
| reinterpret_cast<camera_metadata_t*>(metadata.metadata.data()); |
| CameraMetadata chars; |
| chars.metadata = metadata.metadata; |
| |
| outputStreams.clear(); |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMetaBuffer, outputStreams)); |
| ASSERT_NE(0u, outputStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMetaBuffer, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| int32_t streamConfigCounter = 0; |
| for (auto& it : outputStreams) { |
| Dataspace dataspace = getDataspace(static_cast<PixelFormat>(it.format)); |
| Stream stream = {streamId, |
| StreamType::OUTPUT, |
| it.width, |
| it.height, |
| static_cast<PixelFormat>(it.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| dataspace, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| 0, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| std::vector<Stream> streams = {stream}; |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| ndk::ScopedAStatus s = injectionSession->configureInjectionStreams(config, chars); |
| ASSERT_TRUE(s.isOk()); |
| streamId++; |
| } |
| |
| std::shared_ptr<ICameraDeviceSession> session; |
| ndk::ScopedAStatus ret = injectionSession->getCameraDeviceSession(&session); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(session, nullptr); |
| ret = session->close(); |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Check for correct handling of invalid/incorrect configuration parameters for injection camera. |
| TEST_P(CameraAidlTest, configureInjectionStreamsInvalidOutputs) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputStreams; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata metadata; |
| std::shared_ptr<ICameraInjectionSession> injectionSession; |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyInjectionSession(name, mProvider, &injectionSession /*out*/, &metadata /*out*/, |
| &unusedDevice); |
| if (injectionSession == nullptr) { |
| continue; |
| } |
| |
| camera_metadata_t* staticMetaBuffer = |
| reinterpret_cast<camera_metadata_t*>(metadata.metadata.data()); |
| std::shared_ptr<ICameraDeviceSession> session; |
| ndk::ScopedAStatus ret = injectionSession->getCameraDeviceSession(&session); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(session, nullptr); |
| |
| CameraMetadata chars; |
| chars.metadata = metadata.metadata; |
| |
| outputStreams.clear(); |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMetaBuffer, outputStreams)); |
| ASSERT_NE(0u, outputStreams.size()); |
| |
| int32_t jpegBufferSize = 0; |
| ASSERT_EQ(Status::OK, getJpegBufferSize(staticMetaBuffer, &jpegBufferSize)); |
| ASSERT_NE(0u, jpegBufferSize); |
| |
| int32_t streamId = 0; |
| Stream stream = {streamId++, |
| StreamType::OUTPUT, |
| 0, |
| 0, |
| static_cast<PixelFormat>(outputStreams[0].format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| 0, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| int32_t streamConfigCounter = 0; |
| std::vector<Stream> streams = {stream}; |
| StreamConfiguration config; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| |
| config.streamConfigCounter = streamConfigCounter++; |
| ndk::ScopedAStatus s = injectionSession->configureInjectionStreams(config, chars); |
| ASSERT_TRUE( |
| (static_cast<int32_t>(Status::ILLEGAL_ARGUMENT) == s.getServiceSpecificError()) || |
| (static_cast<int32_t>(Status::INTERNAL_ERROR) == s.getServiceSpecificError())); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| INT32_MAX, |
| INT32_MAX, |
| static_cast<PixelFormat>(outputStreams[0].format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| 0, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| config.streamConfigCounter = streamConfigCounter++; |
| s = injectionSession->configureInjectionStreams(config, chars); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), s.getServiceSpecificError()); |
| |
| for (auto& it : outputStreams) { |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| it.width, |
| it.height, |
| static_cast<PixelFormat>(INT32_MAX), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| jpegBufferSize, |
| 0, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| config.streamConfigCounter = streamConfigCounter++; |
| s = injectionSession->configureInjectionStreams(config, chars); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), s.getServiceSpecificError()); |
| |
| stream = {streamId++, |
| StreamType::OUTPUT, |
| it.width, |
| it.height, |
| static_cast<PixelFormat>(it.format), |
| static_cast<aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| static_cast<StreamRotation>(INT32_MAX), |
| std::string(), |
| jpegBufferSize, |
| 0, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| streams[0] = stream; |
| createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, |
| jpegBufferSize); |
| config.streamConfigCounter = streamConfigCounter++; |
| s = injectionSession->configureInjectionStreams(config, chars); |
| ASSERT_EQ(static_cast<int32_t>(Status::ILLEGAL_ARGUMENT), s.getServiceSpecificError()); |
| } |
| |
| ret = session->close(); |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| // Check whether session parameters are supported for injection camera. If Hal support for them |
| // exist, then try to configure a preview stream using them. |
| TEST_P(CameraAidlTest, configureInjectionStreamsWithSessionParameters) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputPreviewStreams; |
| AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, |
| static_cast<int32_t>(PixelFormat::IMPLEMENTATION_DEFINED)}; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata metadata; |
| std::shared_ptr<ICameraInjectionSession> injectionSession; |
| std::shared_ptr<ICameraDevice> unusedDevice; |
| openEmptyInjectionSession(name, mProvider, &injectionSession /*out*/, &metadata /*out*/, |
| &unusedDevice /*out*/); |
| if (injectionSession == nullptr) { |
| continue; |
| } |
| |
| std::shared_ptr<ICameraDeviceSession> session; |
| ndk::ScopedAStatus ret = injectionSession->getCameraDeviceSession(&session); |
| ASSERT_TRUE(ret.isOk()); |
| ASSERT_NE(session, nullptr); |
| |
| camera_metadata_t* staticMetaBuffer = |
| reinterpret_cast<camera_metadata_t*>(metadata.metadata.data()); |
| CameraMetadata chars; |
| chars.metadata = metadata.metadata; |
| |
| std::unordered_set<int32_t> availableSessionKeys; |
| Status rc = getSupportedKeys(staticMetaBuffer, ANDROID_REQUEST_AVAILABLE_SESSION_KEYS, |
| &availableSessionKeys); |
| ASSERT_EQ(Status::OK, rc); |
| if (availableSessionKeys.empty()) { |
| ret = session->close(); |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| android::hardware::camera::common::V1_0::helper::CameraMetadata previewRequestSettings; |
| android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams, |
| modifiedSessionParams; |
| constructFilteredSettings(session, availableSessionKeys, RequestTemplate::PREVIEW, |
| &previewRequestSettings, &sessionParams); |
| if (sessionParams.isEmpty()) { |
| ret = session->close(); |
| ASSERT_TRUE(ret.isOk()); |
| continue; |
| } |
| |
| outputPreviewStreams.clear(); |
| |
| ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMetaBuffer, outputPreviewStreams, |
| &previewThreshold)); |
| ASSERT_NE(0u, outputPreviewStreams.size()); |
| |
| Stream previewStream = { |
| 0, |
| StreamType::OUTPUT, |
| outputPreviewStreams[0].width, |
| outputPreviewStreams[0].height, |
| static_cast<PixelFormat>(outputPreviewStreams[0].format), |
| static_cast<::aidl::android::hardware::graphics::common::BufferUsage>( |
| GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), |
| Dataspace::UNKNOWN, |
| StreamRotation::ROTATION_0, |
| std::string(), |
| 0, |
| -1, |
| {SensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_DEFAULT}, |
| RequestAvailableDynamicRangeProfilesMap:: |
| ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD}; |
| std::vector<Stream> streams = {previewStream}; |
| StreamConfiguration config; |
| config.streams = streams; |
| config.operationMode = StreamConfigurationMode::NORMAL_MODE; |
| |
| modifiedSessionParams = sessionParams; |
| camera_metadata_t* sessionParamsBuffer = sessionParams.release(); |
| uint8_t* rawSessionParamsBuffer = reinterpret_cast<uint8_t*>(sessionParamsBuffer); |
| config.sessionParams.metadata = |
| std::vector(rawSessionParamsBuffer, |
| rawSessionParamsBuffer + get_camera_metadata_size(sessionParamsBuffer)); |
| |
| config.streamConfigCounter = 0; |
| config.streamConfigCounter = 0; |
| config.multiResolutionInputImage = false; |
| |
| ndk::ScopedAStatus s = injectionSession->configureInjectionStreams(config, chars); |
| ASSERT_TRUE(s.isOk()); |
| |
| sessionParams.acquire(sessionParamsBuffer); |
| free_camera_metadata(staticMetaBuffer); |
| ret = session->close(); |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| TEST_P(CameraAidlTest, configureStreamsUseCasesCroppedRaw) { |
| AvailableStream rawStreamThreshold = |
| {INT_MAX, INT_MAX, static_cast<int32_t>(PixelFormat::RAW16)}; |
| configureStreamUseCaseInternal(rawStreamThreshold); |
| } |
| |
| // Verify that valid stream use cases can be configured successfully, and invalid use cases |
| // fail stream configuration. |
| TEST_P(CameraAidlTest, configureStreamsUseCases) { |
| AvailableStream previewStreamThreshold = |
| {kMaxPreviewWidth, kMaxPreviewHeight, static_cast<int32_t>(PixelFormat::YCBCR_420_888)}; |
| configureStreamUseCaseInternal(previewStreamThreshold); |
| } |
| |
| // Validate the integrity of stream configuration metadata |
| TEST_P(CameraAidlTest, validateStreamConfigurations) { |
| std::vector<std::string> cameraDeviceNames = getCameraDeviceNames(mProvider); |
| std::vector<AvailableStream> outputStreams; |
| |
| const int32_t scalerSizesTag = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS; |
| const int32_t scalerMinFrameDurationsTag = ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS; |
| const int32_t scalerStallDurationsTag = ANDROID_SCALER_AVAILABLE_STALL_DURATIONS; |
| |
| for (const auto& name : cameraDeviceNames) { |
| CameraMetadata meta; |
| std::shared_ptr<ICameraDevice> cameraDevice; |
| |
| openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, |
| &cameraDevice /*out*/); |
| camera_metadata_t* staticMeta = reinterpret_cast<camera_metadata_t*>(meta.metadata.data()); |
| |
| if (is10BitDynamicRangeCapable(staticMeta)) { |
| std::vector<std::tuple<size_t, size_t>> supportedP010Sizes, supportedBlobSizes; |
| |
| getSupportedSizes(staticMeta, scalerSizesTag, HAL_PIXEL_FORMAT_BLOB, |
| &supportedBlobSizes); |
| getSupportedSizes(staticMeta, scalerSizesTag, HAL_PIXEL_FORMAT_YCBCR_P010, |
| &supportedP010Sizes); |
| ASSERT_FALSE(supportedP010Sizes.empty()); |
| |
| std::vector<int64_t> blobMinDurations, blobStallDurations; |
| getSupportedDurations(staticMeta, scalerMinFrameDurationsTag, HAL_PIXEL_FORMAT_BLOB, |
| supportedP010Sizes, &blobMinDurations); |
| getSupportedDurations(staticMeta, scalerStallDurationsTag, HAL_PIXEL_FORMAT_BLOB, |
| supportedP010Sizes, &blobStallDurations); |
| ASSERT_FALSE(blobStallDurations.empty()); |
| ASSERT_FALSE(blobMinDurations.empty()); |
| ASSERT_EQ(supportedP010Sizes.size(), blobMinDurations.size()); |
| ASSERT_EQ(blobMinDurations.size(), blobStallDurations.size()); |
| } |
| |
| // TODO (b/280887191): Validate other aspects of stream configuration metadata... |
| |
| ndk::ScopedAStatus ret = mSession->close(); |
| mSession = nullptr; |
| ASSERT_TRUE(ret.isOk()); |
| } |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CameraAidlTest); |
| INSTANTIATE_TEST_SUITE_P( |
| PerInstance, CameraAidlTest, |
| testing::ValuesIn(android::getAidlHalInstanceNames(ICameraProvider::descriptor)), |
| android::hardware::PrintInstanceNameToString); |