/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 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 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 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 cb = ndk::SharedRefBase::make(); ScopedAStatus ret = mProvider->setCallback(cb); ASSERT_TRUE(ret.isOk()); ret = mProvider->setCallback(nullptr); ASSERT_EQ(static_cast(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); } // Test if ICameraProvider::getCameraDeviceInterface returns Status::OK and non-null device TEST_P(CameraAidlTest, getCameraDeviceInterface) { std::vector cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { std::shared_ptr 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& deviceName : cameraDeviceNames) { std::shared_ptr 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { ALOGI("validateManualFlashStrengthControlKeys: Testing camera device %s", name.c_str()); CameraMetadata meta; std::shared_ptr 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(meta.metadata.data()); verifyManualFlashStrengthControlCharacteristics(staticMeta); ret = mSession->close(); mSession = nullptr; ASSERT_TRUE(ret.isOk()); } } else { ALOGI("validateManualFlashStrengthControlKeys: Test skipped.\n"); GTEST_SKIP(); } } TEST_P(CameraAidlTest, systemCameraTest) { std::vector cameraDeviceNames = getCameraDeviceNames(mProvider); std::map> hiddenPhysicalIdToLogicalMap; for (const auto& name : cameraDeviceNames) { std::shared_ptr 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(cameraCharacteristics.metadata.data()); Status rc = isLogicalMultiCamera(staticMeta); if (rc == Status::OPERATION_NOT_SUPPORTED) { return; } ASSERT_EQ(rc, Status::OK); std::unordered_set 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}))); } 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { std::shared_ptr 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(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); ASSERT_EQ(0, devChars.metadata.size()); } } TEST_P(CameraAidlTest, getSessionCharacteristics) { if (flags::feature_combination_query()) { std::vector cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { std::shared_ptr 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); int32_t interfaceVersion = -1; ret = device->getInterfaceVersion(&interfaceVersion); ASSERT_TRUE(ret.isOk()); bool supportSessionCharacteristics = (interfaceVersion >= CAMERA_DEVICE_API_MINOR_VERSION_3); if (!supportSessionCharacteristics) { continue; } CameraMetadata meta; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &device /*out*/); std::vector outputStreams; camera_metadata_t* staticMeta = reinterpret_cast(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(sampleStream.format), static_cast( 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 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); ret = mSession->close(); mSession = nullptr; ASSERT_TRUE(ret.isOk()); } } 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::shared_ptr cb = ndk::SharedRefBase::make(this); ndk::ScopedAStatus ret = mProvider->setCallback(cb); ASSERT_TRUE(ret.isOk()); for (const auto& name : cameraDeviceNames) { int32_t defaultLevel; std::shared_ptr 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(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(Status::OPERATION_NOT_SUPPORTED), ret.getServiceSpecificError()); } else { { ASSERT_TRUE(ret.isOk()); std::unique_lock 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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::shared_ptr cb = ndk::SharedRefBase::make(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 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(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(Status::OPERATION_NOT_SUPPORTED), ret.getServiceSpecificError()); } else { ASSERT_TRUE(ret.isOk()); { std::unique_lock 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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { std::shared_ptr 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { std::shared_ptr 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 cb = ndk::SharedRefBase::make(); 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { std::shared_ptr 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 cb = ndk::SharedRefBase::make(); 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(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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputStreams; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr device; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &device /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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(it.format)); stream.id = streamId; stream.streamType = StreamType::OUTPUT; stream.width = it.width; stream.height = it.height; stream.format = static_cast(it.format); stream.dataSpace = dataspace; stream.usage = static_cast( 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( RequestAvailableColorSpaceProfilesMap:: ANDROID_REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED); std::vector streams = {stream}; StreamConfiguration config; createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, jpegBufferSize); verifyStreamCombination(device, config, /*expectedStatus*/ true); config.streamConfigCounter = streamConfigCounter++; std::vector 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 session; std::shared_ptr cameraDevice; StreamConfiguration config; }; std::map idToNameMap = getCameraDeviceIdToNameMap(mProvider); std::vector concurrentDeviceCombinations = getConcurrentDeviceCombinations(mProvider); std::vector outputStreams; for (const auto& cameraDeviceIds : concurrentDeviceCombinations) { std::vector cameraIdsAndStreamCombinations; std::vector 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(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 streams(outputStreams.size()); size_t j = 0; for (const auto& s : outputStreams) { Stream stream; Dataspace dataspace = getDataspace(static_cast(s.format)); stream.id = streamId++; stream.streamType = StreamType::OUTPUT; stream.width = s.width; stream.height = s.height; stream.format = static_cast(s.format); stream.usage = static_cast( 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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputStreams; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr cameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &cameraDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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(0), static_cast(0), static_cast(outputStreams[0].format), static_cast( 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 streams = {stream}; StreamConfiguration config; createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, jpegBufferSize); verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ false); config.streamConfigCounter = streamConfigCounter++; std::vector halConfigs; ndk::ScopedAStatus ret = mSession->configureStreams(config, &halConfigs); ASSERT_TRUE(static_cast(Status::ILLEGAL_ARGUMENT) == ret.getServiceSpecificError() || static_cast(Status::INTERNAL_ERROR) == ret.getServiceSpecificError()); stream = {streamId++, StreamType::OUTPUT, /*width*/ INT32_MAX, /*height*/ INT32_MAX, static_cast(outputStreams[0].format), static_cast( 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(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); for (auto& it : outputStreams) { stream = {streamId++, StreamType::OUTPUT, it.width, it.height, static_cast(UINT32_MAX), static_cast( 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(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); stream = {streamId++, StreamType::OUTPUT, it.width, it.height, static_cast(it.format), static_cast( GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), Dataspace::UNKNOWN, static_cast(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(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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector inputStreams; std::vector inputOutputMap; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr cameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &cameraDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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(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(PixelFormat::IMPLEMENTATION_DEFINED) && inputIter.outputFormat == static_cast(PixelFormat::Y8)) { hasPrivToY8 = true; } else if (inputIter.inputFormat == static_cast(PixelFormat::Y8)) { if (inputIter.outputFormat == static_cast(PixelFormat::BLOB)) { hasY8ToBlob = true; } else if (inputIter.outputFormat == static_cast(PixelFormat::Y8)) { hasY8ToY8 = true; } } AvailableStream outputThreshold = {INT32_MAX, INT32_MAX, inputIter.outputFormat}; std::vector outputStreams; ASSERT_EQ(Status::OK, getAvailableOutputStreams(staticMeta, outputStreams, &outputThreshold)); for (auto& outputIter : outputStreams) { Dataspace outputDataSpace = getDataspace(static_cast(outputIter.format)); Stream zslStream = { streamId++, StreamType::OUTPUT, input.width, input.height, static_cast(input.format), static_cast( 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(input.format), static_cast(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(outputIter.format), static_cast( 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 streams = {inputStream, zslStream, outputStream}; StreamConfiguration config; createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, jpegBufferSize); verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); config.streamConfigCounter = streamConfigCounter++; std::vector 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputPreviewStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr unusedCameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &unusedCameraDevice /*out*/); camera_metadata_t* staticMetaBuffer = reinterpret_cast(meta.metadata.data()); std::unordered_set 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(outputPreviewStreams[0].format), static_cast( 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 streams = {previewStream}; StreamConfiguration config; config.streams = streams; config.operationMode = StreamConfigurationMode::NORMAL_MODE; modifiedSessionParams = sessionParams; auto sessionParamsBuffer = sessionParams.release(); std::vector rawSessionParam = std::vector(reinterpret_cast(sessionParamsBuffer), reinterpret_cast(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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputBlobStreams; std::vector outputPreviewStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; AvailableStream blobThreshold = {INT32_MAX, INT32_MAX, static_cast(PixelFormat::BLOB)}; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr cameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &cameraDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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(previewIter.format), static_cast( 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(blobIter.format), static_cast( 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 streams = {previewStream, blobStream}; StreamConfiguration config; createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, jpegBufferSize); config.streamConfigCounter = streamConfigCounter++; verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); std::vector 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 cameraDeviceNames = getCameraDeviceNames(mProvider); for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr cameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &cameraDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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(hfrStream.format), static_cast( 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 streams = {stream}; StreamConfiguration config; createStreamConfiguration(streams, StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE, &config); verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); config.streamConfigCounter = streamConfigCounter++; std::vector 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(0), static_cast(0), static_cast(hfrStream.format), static_cast( 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 halConfig; ret = mSession->configureStreams(config, &halConfig); ASSERT_TRUE(static_cast(Status::ILLEGAL_ARGUMENT) == ret.getServiceSpecificError() || static_cast(Status::INTERNAL_ERROR) == ret.getServiceSpecificError()); stream = {streamId++, StreamType::OUTPUT, INT32_MAX, INT32_MAX, static_cast(hfrStream.format), static_cast( 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(Status::ILLEGAL_ARGUMENT), ret.getServiceSpecificError()); stream = {streamId++, StreamType::OUTPUT, hfrStream.width, hfrStream.height, static_cast(UINT32_MAX), static_cast( 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(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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputBlobStreams; std::vector outputVideoStreams; AvailableStream videoThreshold = {kMaxVideoWidth, kMaxVideoHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; AvailableStream blobThreshold = {kMaxVideoWidth, kMaxVideoHeight, static_cast(PixelFormat::BLOB)}; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; std::shared_ptr cameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &cameraDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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(videoIter.format), static_cast( 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(blobIter.format), static_cast( 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 streams = {videoStream, blobStream}; StreamConfiguration config; createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config, jpegBufferSize); verifyStreamCombination(cameraDevice, config, /*expectedStatus*/ true); config.streamConfigCounter = streamConfigCounter++; std::vector 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 cameraDeviceToTimeLag; processPreviewStabilizationCaptureRequestInternal(/*previewStabilizationOn*/ false, cameraDeviceToTimeLag); processPreviewStabilizationCaptureRequestInternal(/*previewStabilizationOn*/ true, cameraDeviceToTimeLag); } // Generate and verify a multi-camera capture request TEST_P(CameraAidlTest, processMultiCaptureRequestPreview) { std::vector cameraDeviceNames = getCameraDeviceNames(mProvider); AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::YCBCR_420_888)}; int64_t bufferId = 1; uint32_t frameNumber = 1; std::vector settings; std::vector 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 unusedDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &metadata /*out*/, &unusedDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(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 physicalIds; rc = getPhysicalCameraIds(staticMeta, &physicalIds); ASSERT_TRUE(Status::OK == rc); ASSERT_TRUE(physicalIds.size() > 1); std::unordered_set 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 halStreams; bool supportsPartialResults = false; std::set halBufManagedStreamIds; int32_t partialResultCount = 0; Stream previewStream; std::shared_ptr 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 resultQueue = std::make_shared(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 inflightReq = std::make_shared( static_cast(halStreams.size()), false, supportsPartialResults, partialResultCount, physicalIds, resultQueue); std::vector requests(1); CaptureRequest& request = requests[0]; request.frameNumber = frameNumber; request.fmqSettingsSize = 0; request.settings = settingsMetadata; std::vector& outputBuffers = request.outputBuffers; std::vector 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStream.producerUsage), static_cast(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 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 l(mLock); mInflightMap.clear(); mInflightMap[frameNumber] = inflightReq; } int32_t numRequestProcessed = 0; std::vector cachesToRemove; ndk::ScopedAStatus returnStatus = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); ASSERT_TRUE(returnStatus.isOk()); ASSERT_EQ(numRequestProcessed, 1u); { std::unique_lock 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( static_cast(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 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(Status::ILLEGAL_ARGUMENT), returnStatus.getServiceSpecificError()); defaultPreviewSettings.unlock(settingsBuffer); filteredSettings.unlock(filteredSettingsBuffer); if (halBufManagedStreamIds.size() != 0) { std::vector 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 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 unusedDevice; openEmptyDeviceSession(name, mProvider, &mSession, &meta, &unusedDevice); camera_metadata_t* staticMeta = reinterpret_cast(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(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(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 halStreams; bool supportsPartialResults = false; std::set halBufManagedStreamIds; int32_t partialResultCount = 0; Stream previewStream; std::shared_ptr cb; std::list pixelFormats = {PixelFormat::YCBCR_420_888, PixelFormat::RAW16}; for (PixelFormat format : pixelFormats) { previewStream.usage = static_cast( 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 resultQueue = std::make_shared(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 graphicBuffers; graphicBuffers.reserve(halStreams.size()); std::shared_ptr inflightReq = std::make_shared( static_cast(halStreams.size()), false, supportsPartialResults, partialResultCount, std::unordered_set(), resultQueue); std::vector requests(1); CaptureRequest& request = requests[0]; std::vector& 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStream.producerUsage), static_cast(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 l(mLock); mInflightMap.clear(); mInflightMap[frameNumber] = inflightReq; } int32_t numRequestProcessed = 0; std::vector cachesToRemove; ndk::ScopedAStatus returnStatus = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); ASSERT_TRUE(returnStatus.isOk()); ASSERT_EQ(numRequestProcessed, 1u); { std::unique_lock 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 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 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 device; openEmptyDeviceSession(name, mProvider, &mSession, &meta, &device); camera_metadata_t* staticMeta = reinterpret_cast(meta.metadata.data()); if (!is10BitDynamicRangeCapable(staticMeta)) { ndk::ScopedAStatus ret = mSession->close(); mSession = nullptr; ASSERT_TRUE(ret.isOk()); continue; } std::vector 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(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 halStreams; bool supportsPartialResults = false; std::set halBufManagedStreamIds; int32_t partialResultCount = 0; Stream previewStream; std::shared_ptr cb; for (const auto& profile : profileList) { previewStream.usage = static_cast( 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 resultQueue = std::make_shared(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 requests(halStreams[0].maxBuffers); for (int32_t requestId = 0; requestId < requests.size(); requestId++) { std::shared_ptr inflightReq = std::make_shared( static_cast(halStreams.size()), false, supportsPartialResults, partialResultCount, std::unordered_set(), resultQueue); CaptureRequest& request = requests[requestId]; std::vector& outputBuffers = request.outputBuffers; outputBuffers.resize(halStreams.size()); size_t k = 0; inflightReq->mOutstandingBufferIds.resize(halStreams.size()); std::vector 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStream.producerUsage), static_cast(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 l(mLock); mInflightMap[bufferId] = inflightReq; } } int32_t numRequestProcessed = 0; std::vector 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 {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 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 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(profiles[i]), static_cast( 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(ColorSpaceNamed::BT2020_HLG), static_cast(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 cameraDeviceNames = getCameraDeviceNames(mProvider); AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(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 unusedDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &unusedDevice /*out*/); camera_metadata_t* staticMetaBuffer = clone_camera_metadata(reinterpret_cast(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 halStreams; std::shared_ptr 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 resultQueue = std::make_shared(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 requests(kBurstFrameCount); std::vector buffers(kBurstFrameCount); std::vector> inflightReqs(kBurstFrameCount); std::vector isoValues(kBurstFrameCount); std::vector requestSettings(kBurstFrameCount); for (int32_t i = 0; i < kBurstFrameCount; i++) { std::unique_lock l(mLock); CaptureRequest& request = requests[i]; std::vector& 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStreams[0].producerUsage), static_cast(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(settings.metadata.data())); // Disable all 3A routines uint8_t mode = static_cast(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(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(1, false, supportsPartialResults, partialResultCount, resultQueue); mInflightMap[frameNumber + i] = inflightReqs[i]; } int32_t numRequestProcessed = 0; std::vector 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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputPreviewStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; int64_t bufferId = 1; int32_t frameNumber = 1; CameraMetadata settings; for (const auto& name : cameraDeviceNames) { Stream previewStream; std::vector halStreams; std::shared_ptr 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStreams[0].producerUsage), static_cast(halStreams[0].consumerUsage))), halStreams[0].overrideFormat, &buffer_handle); } std::vector requests(1); CaptureRequest& request = requests[0]; std::vector& 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 cachesToRemove; ndk::ScopedAStatus ret = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); ASSERT_EQ(static_cast(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 cameraDeviceNames = getCameraDeviceNames(mProvider); AvailableStream threshold = {kMaxStillWidth, kMaxStillHeight, static_cast(PixelFormat::BLOB)}; int64_t bufferId = 1; int32_t frameNumber = 1; CameraMetadata settings; for (const auto& name : cameraDeviceNames) { CameraMetadata meta; { std::shared_ptr unusedDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &unusedDevice); camera_metadata_t* staticMetaBuffer = clone_camera_metadata( reinterpret_cast(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 halStreams; std::shared_ptr cb; int32_t jpegBufferSize; std::set 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 resultQueue = std::make_shared(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 buffers(kBurstFrameCount); std::vector> inflightReqs(kBurstFrameCount); std::vector requestSettings(kBurstFrameCount); std::vector requests(kBurstFrameCount); HalStream halStream = halStreams[0]; for (uint32_t i = 0; i < kBurstFrameCount; i++) { CaptureRequest& request = requests[i]; std::vector& outputBuffers = request.outputBuffers; outputBuffers.resize(1); StreamBuffer& outputBuffer = outputBuffers[0]; std::unique_lock 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStream.producerUsage), static_cast(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(settings.metadata.data())); camera_metadata_t* metaBuffer = requestMeta.release(); uint8_t* rawMetaBuffer = reinterpret_cast(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(1, false, supportsPartialResults, partialResultCount, resultQueue); mInflightMap[frameNumber + i] = inflightReqs[i]; } int32_t numRequestProcessed = 0; std::vector cachesToRemove; ndk::ScopedAStatus returnStatus = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); ASSERT_TRUE(returnStatus.isOk()); ASSERT_EQ(numRequestProcessed, kBurstFrameCount); std::vector offlineStreamIds = {halStream.id}; CameraOfflineSessionInfo offlineSessionInfo; std::shared_ptr offlineSession; returnStatus = mSession->switchToOffline(offlineStreamIds, &offlineSessionInfo, &offlineSession); if (!halStreams[0].supportOffline) { ASSERT_EQ(static_cast(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 offlineResultQueue = std::make_shared(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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputBlobStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; int32_t frameNumber = 1; CameraMetadata settings; for (const auto& name : cameraDeviceNames) { Stream previewStream; std::vector halStreams; std::shared_ptr 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 requests(1); CaptureRequest& request = requests[0]; std::vector& 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 cachesToRemove; ret = mSession->processCaptureRequest(requests, cachesToRemove, &numRequestProcessed); ASSERT_EQ(static_cast(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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputPreviewStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; int64_t bufferId = 1; int32_t frameNumber = 1; CameraMetadata settings; for (const auto& name : cameraDeviceNames) { Stream previewStream; std::vector halStreams; std::shared_ptr 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 resultQueue = std::make_shared(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 inflightReq = std::make_shared( 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 requests(1); CaptureRequest& request = requests[0]; std::vector& 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_NATIVE_UNSIGNED_CAST(android_convertGralloc1To0Usage( static_cast(halStreams[0].producerUsage), static_cast(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 l(mLock); mInflightMap.clear(); mInflightMap[frameNumber] = inflightReq; } int32_t numRequestProcessed = 0; std::vector 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 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(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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputPreviewStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; for (const auto& name : cameraDeviceNames) { Stream previewStream; std::vector halStreams; std::shared_ptr 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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputStreams; for (const auto& name : cameraDeviceNames) { CameraMetadata metadata; std::shared_ptr injectionSession; std::shared_ptr unusedDevice; openEmptyInjectionSession(name, mProvider, &injectionSession /*out*/, &metadata /*out*/, &unusedDevice /*out*/); if (injectionSession == nullptr) { continue; } camera_metadata_t* staticMetaBuffer = reinterpret_cast(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(it.format)); Stream stream = {streamId, StreamType::OUTPUT, it.width, it.height, static_cast(it.format), static_cast( 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 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 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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputStreams; for (const auto& name : cameraDeviceNames) { CameraMetadata metadata; std::shared_ptr injectionSession; std::shared_ptr unusedDevice; openEmptyInjectionSession(name, mProvider, &injectionSession /*out*/, &metadata /*out*/, &unusedDevice); if (injectionSession == nullptr) { continue; } camera_metadata_t* staticMetaBuffer = reinterpret_cast(metadata.metadata.data()); std::shared_ptr 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(outputStreams[0].format), static_cast( 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 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(Status::ILLEGAL_ARGUMENT) == s.getServiceSpecificError()) || (static_cast(Status::INTERNAL_ERROR) == s.getServiceSpecificError())); stream = {streamId++, StreamType::OUTPUT, INT32_MAX, INT32_MAX, static_cast(outputStreams[0].format), static_cast( 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(Status::ILLEGAL_ARGUMENT), s.getServiceSpecificError()); for (auto& it : outputStreams) { stream = {streamId++, StreamType::OUTPUT, it.width, it.height, static_cast(INT32_MAX), static_cast( 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(Status::ILLEGAL_ARGUMENT), s.getServiceSpecificError()); stream = {streamId++, StreamType::OUTPUT, it.width, it.height, static_cast(it.format), static_cast( GRALLOC1_CONSUMER_USAGE_HWCOMPOSER), Dataspace::UNKNOWN, static_cast(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(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 cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector outputPreviewStreams; AvailableStream previewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight, static_cast(PixelFormat::IMPLEMENTATION_DEFINED)}; for (const auto& name : cameraDeviceNames) { CameraMetadata metadata; std::shared_ptr injectionSession; std::shared_ptr unusedDevice; openEmptyInjectionSession(name, mProvider, &injectionSession /*out*/, &metadata /*out*/, &unusedDevice /*out*/); if (injectionSession == nullptr) { continue; } std::shared_ptr session; ndk::ScopedAStatus ret = injectionSession->getCameraDeviceSession(&session); ASSERT_TRUE(ret.isOk()); ASSERT_NE(session, nullptr); camera_metadata_t* staticMetaBuffer = reinterpret_cast(metadata.metadata.data()); CameraMetadata chars; chars.metadata = metadata.metadata; std::unordered_set 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(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 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(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(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(PixelFormat::YCBCR_420_888)}; configureStreamUseCaseInternal(previewStreamThreshold); } // Validate the integrity of stream configuration metadata TEST_P(CameraAidlTest, validateStreamConfigurations) { std::vector cameraDeviceNames = getCameraDeviceNames(mProvider); std::vector 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 cameraDevice; openEmptyDeviceSession(name, mProvider, &mSession /*out*/, &meta /*out*/, &cameraDevice /*out*/); camera_metadata_t* staticMeta = reinterpret_cast(meta.metadata.data()); if (is10BitDynamicRangeCapable(staticMeta)) { std::vector> 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 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);