From 9901d9eed4f33ec01f3194ab6b02f4dee80c9927 Mon Sep 17 00:00:00 2001 From: Tim Zimmermann Date: Sun, 26 May 2024 09:53:57 +0200 Subject: [PATCH] aidl: Implement camera device/provider HALs * Based on HIDL implementation from hardware/interfaces @ android-14.0.0_r37 Change-Id: I369434f8e1f877cac88c9f06d048e4c695ac5599 --- aidl/camera/device/Android.bp | 34 + aidl/camera/device/CameraDevice.cpp | 382 ++++ aidl/camera/device/CameraDevice.h | 98 + aidl/camera/device/CameraDeviceSession.cpp | 1721 +++++++++++++++++ aidl/camera/device/CameraDeviceSession.h | 388 ++++ aidl/camera/device/convert.cpp | 150 ++ aidl/camera/device/convert.h | 84 + aidl/camera/provider/Android.bp | 35 + aidl/camera/provider/CameraProvider.cpp | 447 +++++ aidl/camera/provider/CameraProvider.h | 92 + ...ardware.camera.provider-service.samsung.rc | 8 + ...rdware.camera.provider-service.samsung.xml | 6 + aidl/camera/provider/service.cpp | 49 + 13 files changed, 3494 insertions(+) create mode 100644 aidl/camera/device/Android.bp create mode 100644 aidl/camera/device/CameraDevice.cpp create mode 100644 aidl/camera/device/CameraDevice.h create mode 100644 aidl/camera/device/CameraDeviceSession.cpp create mode 100644 aidl/camera/device/CameraDeviceSession.h create mode 100644 aidl/camera/device/convert.cpp create mode 100644 aidl/camera/device/convert.h create mode 100644 aidl/camera/provider/Android.bp create mode 100644 aidl/camera/provider/CameraProvider.cpp create mode 100644 aidl/camera/provider/CameraProvider.h create mode 100644 aidl/camera/provider/android.hardware.camera.provider-service.samsung.rc create mode 100644 aidl/camera/provider/android.hardware.camera.provider-service.samsung.xml create mode 100644 aidl/camera/provider/service.cpp diff --git a/aidl/camera/device/Android.bp b/aidl/camera/device/Android.bp new file mode 100644 index 0000000..9ff0edf --- /dev/null +++ b/aidl/camera/device/Android.bp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2024 The LineageOS Project +// +// SPDX-License-Identifier: Apache-2.0 +// + +cc_library_shared { + name: "camera.device-impl.samsung", + defaults: ["android.hardware.graphics.common-ndk_shared"], + vendor: true, + srcs: [ + "CameraDevice.cpp", + "CameraDeviceSession.cpp", + "convert.cpp", + ], + shared_libs: [ + "android.hardware.camera.common-V1-ndk", + "android.hardware.camera.device-V1-ndk", + "libbinder_ndk", + "libcamera_metadata", + "libcutils", + "libfmq", + "libgralloctypes", + "libhidlbase", + "liblog", + "libui", + "libutils", + ], + static_libs: [ + "android.hardware.camera.common-helper", + "libaidlcommonsupport", + ], + export_include_dirs: ["."], +} diff --git a/aidl/camera/device/CameraDevice.cpp b/aidl/camera/device/CameraDevice.cpp new file mode 100644 index 0000000..96d7eed --- /dev/null +++ b/aidl/camera/device/CameraDevice.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_TAG "CamDev-impl" + +#include "CameraDevice.h" +#include "convert.h" + +#include + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace implementation { + +std::string CameraDevice::kDeviceVersion = "1.1"; + +CameraDevice::CameraDevice( + sp module, const std::string& cameraId, + const SortedVector>& cameraDeviceNames) + : mModule(module), + mCameraId(cameraId), + mDisconnected(false), + mCameraDeviceNames(cameraDeviceNames) { + mCameraIdInt = atoi(mCameraId.c_str()); + // Should not reach here as provider also validate ID + if (mCameraIdInt < 0) { + ALOGE("%s: Invalid camera id: %s", __FUNCTION__, mCameraId.c_str()); + mInitFail = true; + } else if (mCameraIdInt >= mModule->getNumberOfCameras()) { + ALOGI("%s: Adding a new camera id: %s", __FUNCTION__, mCameraId.c_str()); + } + + mDeviceVersion = mModule->getDeviceVersion(mCameraIdInt); + if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) { + ALOGE("%s: Camera id %s does not support HAL3.2+", __FUNCTION__, mCameraId.c_str()); + mInitFail = true; + } +} + +CameraDevice::~CameraDevice() {} + +Status CameraDevice::initStatus() const { + Mutex::Autolock _l(mLock); + Status status = Status::OK; + if (mInitFail) { + status = Status::INTERNAL_ERROR; + } else if (mDisconnected) { + status = Status::CAMERA_DISCONNECTED; + } + return status; +} + +ndk::ScopedAStatus CameraDevice::getCameraCharacteristics(CameraMetadata* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + Status status = initStatus(); + if (status == Status::OK) { + // Module 2.1+ codepath. + struct camera_info info; + int ret = mModule->getCameraInfo(mCameraIdInt, &info); + if (ret == OK) { + convertToAidl(info.static_camera_characteristics, _aidl_return); + } else { + ALOGE("%s: get camera info failed!", __FUNCTION__); + status = Status::INTERNAL_ERROR; + } + } + + return fromStatus(status); +} + +ndk::ScopedAStatus CameraDevice::getPhysicalCameraCharacteristics( + const std::string& in_physicalCameraId, CameraMetadata* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + Status status = initStatus(); + if (status == Status::OK) { + // Require module 2.5+ version. + if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_5) { + ALOGE("%s: get_physical_camera_info must be called on camera module 2.5 or newer", + __FUNCTION__); + status = Status::INTERNAL_ERROR; + } else { + char* end; + errno = 0; + long id = strtol(in_physicalCameraId.c_str(), &end, 0); + if (id > INT_MAX || (errno == ERANGE && id == LONG_MAX) || id < INT_MIN || + (errno == ERANGE && id == LONG_MIN) || *end != '\0') { + ALOGE("%s: Invalid physicalCameraId %s", __FUNCTION__, in_physicalCameraId.c_str()); + status = Status::ILLEGAL_ARGUMENT; + } else { + camera_metadata_t* physicalInfo = nullptr; + int ret = mModule->getPhysicalCameraInfo((int)id, &physicalInfo); + if (ret == OK) { + convertToAidl(physicalInfo, _aidl_return); + } else if (ret == -EINVAL) { + ALOGE("%s: %s is not a valid physical camera Id outside of getCameraIdList()", + __FUNCTION__, in_physicalCameraId.c_str()); + status = Status::ILLEGAL_ARGUMENT; + } else { + ALOGE("%s: Failed to get physical camera %s info: %s (%d)!", __FUNCTION__, + in_physicalCameraId.c_str(), strerror(-ret), ret); + status = Status::INTERNAL_ERROR; + } + } + } + } + + return fromStatus(status); +} + +void CameraDevice::setConnectionStatus(bool connected) { + Mutex::Autolock _l(mLock); + mDisconnected = !connected; + std::shared_ptr session = mSession.lock(); + if (session == nullptr) { + return; + } + // Only notify active session disconnect events. + // Users will need to re-open camera after disconnect event + if (!connected) { + session->disconnect(); + } + return; +} + +Status CameraDevice::getAidlStatus(int status) { + switch (status) { + case 0: + return Status::OK; + case -ENOSYS: + return Status::OPERATION_NOT_SUPPORTED; + case -EBUSY: + return Status::CAMERA_IN_USE; + case -EUSERS: + return Status::MAX_CAMERAS_IN_USE; + case -ENODEV: + return Status::INTERNAL_ERROR; + case -EINVAL: + return Status::ILLEGAL_ARGUMENT; + default: + ALOGE("%s: unknown HAL status code %d", __FUNCTION__, status); + return Status::INTERNAL_ERROR; + } +} + +ndk::ScopedAStatus CameraDevice::getResourceCost(CameraResourceCost* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + Status status = initStatus(); + if (status == Status::OK) { + int cost = 100; + std::vector conflicting_devices; + struct camera_info info; + + // If using post-2.4 module version, query the cost + conflicting devices from the HAL + if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) { + int ret = mModule->getCameraInfo(mCameraIdInt, &info); + if (ret == OK) { + cost = info.resource_cost; + for (size_t i = 0; i < info.conflicting_devices_length; i++) { + std::string cameraId(info.conflicting_devices[i]); + for (const auto& pair : mCameraDeviceNames) { + if (cameraId == pair.first) { + conflicting_devices.push_back(pair.second); + } + } + } + } else { + status = Status::INTERNAL_ERROR; + } + } + + if (status == Status::OK) { + _aidl_return->resourceCost = cost; + _aidl_return->conflictingDevices.resize(conflicting_devices.size()); + for (size_t i = 0; i < conflicting_devices.size(); i++) { + _aidl_return->conflictingDevices[i] = conflicting_devices[i]; + ALOGV("CamDevice %s is conflicting with camDevice %s", mCameraId.c_str(), + _aidl_return->conflictingDevices[i].c_str()); + } + } + } + + return fromStatus(status); +} + +ndk::ScopedAStatus CameraDevice::isStreamCombinationSupported(const StreamConfiguration& in_streams, + bool* _aidl_return) { + Status status; + *_aidl_return = false; + + // Require module 2.5+ version. + if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_5) { + ALOGE("%s: is_stream_combination_supported must be called on camera module 2.5 or " + "newer", + __FUNCTION__); + status = Status::INTERNAL_ERROR; + } else { + camera_stream_combination_t streamComb{}; + streamComb.operation_mode = static_cast(in_streams.operationMode); + streamComb.num_streams = in_streams.streams.size(); + camera_stream_t* streamBuffer = new camera_stream_t[streamComb.num_streams]; + + size_t i = 0; + for (const auto& it : in_streams.streams) { + streamBuffer[i].stream_type = static_cast(it.streamType); + streamBuffer[i].width = it.width; + streamBuffer[i].height = it.height; + streamBuffer[i].format = static_cast(it.format); + streamBuffer[i].data_space = static_cast(it.dataSpace); + streamBuffer[i].usage = static_cast(it.usage); + streamBuffer[i].physical_camera_id = it.physicalCameraId.c_str(); + streamBuffer[i++].rotation = static_cast(it.rotation); + } + streamComb.streams = streamBuffer; + auto res = mModule->isStreamCombinationSupported(mCameraIdInt, &streamComb); + switch (res) { + case NO_ERROR: + *_aidl_return = true; + status = Status::OK; + break; + case BAD_VALUE: + status = Status::OK; + break; + case INVALID_OPERATION: + status = Status::OPERATION_NOT_SUPPORTED; + break; + default: + ALOGE("%s: Unexpected error: %d", __FUNCTION__, res); + status = Status::INTERNAL_ERROR; + }; + delete[] streamBuffer; + } + + return fromStatus(status); +} + +ndk::ScopedAStatus CameraDevice::open(const std::shared_ptr& in_callback, + std::shared_ptr* _aidl_return) { + if (_aidl_return == nullptr) { + ALOGE("%s: cannot open camera %s. return session ptr is null!", __FUNCTION__, + mCameraId.c_str()); + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + Status status = initStatus(); + std::shared_ptr session = nullptr; + + if (in_callback == nullptr) { + ALOGE("%s: cannot open camera %s. callback is null!", __FUNCTION__, mCameraId.c_str()); + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + if (status != Status::OK) { + // Provider will never pass initFailed device to client, so + // this must be a disconnected camera + ALOGE("%s: cannot open camera %s. camera is disconnected!", __FUNCTION__, + mCameraId.c_str()); + return fromStatus(Status::CAMERA_DISCONNECTED); + } else { + mLock.lock(); + + ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mCameraIdInt); + session = mSession.lock(); + if (session != nullptr && !session->isClosed()) { + ALOGE("%s: cannot open an already opened camera!", __FUNCTION__); + mLock.unlock(); + return fromStatus(Status::CAMERA_IN_USE); + } + + /** Open HAL device */ + status_t res; + camera3_device_t* device; + + ATRACE_BEGIN("camera3->open"); + res = mModule->open(mCameraId.c_str(), reinterpret_cast(&device)); + ATRACE_END(); + + if (res != OK) { + ALOGE("%s: cannot open camera %s!", __FUNCTION__, mCameraId.c_str()); + mLock.unlock(); + return fromStatus(getAidlStatus(res)); + } + + /** Cross-check device version */ + if (device->common.version < CAMERA_DEVICE_API_VERSION_3_2) { + ALOGE("%s: Could not open camera: " + "Camera device should be at least %x, reports %x instead", + __FUNCTION__, CAMERA_DEVICE_API_VERSION_3_2, device->common.version); + device->common.close(&device->common); + mLock.unlock(); + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + struct camera_info info; + res = mModule->getCameraInfo(mCameraIdInt, &info); + if (res != OK) { + ALOGE("%s: Could not open camera: getCameraInfo failed", __FUNCTION__); + device->common.close(&device->common); + mLock.unlock(); + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + session = createSession(device, info.static_camera_characteristics, in_callback); + if (session == nullptr) { + ALOGE("%s: camera device session allocation failed", __FUNCTION__); + mLock.unlock(); + return fromStatus(Status::INTERNAL_ERROR); + } + if (session->isInitFailed()) { + ALOGE("%s: camera device session init failed", __FUNCTION__); + session = nullptr; + mLock.unlock(); + return fromStatus(Status::INTERNAL_ERROR); + } + mSession = session; + + mLock.unlock(); + } + + *_aidl_return = session; + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraDevice::openInjectionSession(const std::shared_ptr&, + std::shared_ptr*) { + return fromStatus(Status::OPERATION_NOT_SUPPORTED); +} + +ndk::ScopedAStatus CameraDevice::setTorchMode(bool in_on) { + if (!mModule->isSetTorchModeSupported()) { + return fromStatus(Status::OPERATION_NOT_SUPPORTED); + } + + Status status = initStatus(); + if (status == Status::OK) { + status = getAidlStatus(mModule->setTorchMode(mCameraId.c_str(), in_on)); + } + return fromStatus(status); +} + +ndk::ScopedAStatus CameraDevice::turnOnTorchWithStrengthLevel(int32_t) { + return fromStatus(Status::OPERATION_NOT_SUPPORTED); +} + +ndk::ScopedAStatus CameraDevice::getTorchStrengthLevel(int32_t*) { + return fromStatus(Status::OPERATION_NOT_SUPPORTED); +} + +std::shared_ptr CameraDevice::createSession( + camera3_device_t* device, const camera_metadata_t* deviceInfo, + const std::shared_ptr& callback) { + return ndk::SharedRefBase::make(device, deviceInfo, callback); +} + +binder_status_t CameraDevice::dump(int fd, const char** args, uint32_t numArgs) { + std::shared_ptr session = mSession.lock(); + if (session == nullptr) { + dprintf(fd, "No active camera device session instance\n"); + return STATUS_OK; + } + + return session->dump(fd, args, numArgs); +} + +} // namespace implementation +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/device/CameraDevice.h b/aidl/camera/device/CameraDevice.h new file mode 100644 index 0000000..a41c496 --- /dev/null +++ b/aidl/camera/device/CameraDevice.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "CameraDeviceSession.h" + +#include +#include +#include + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace implementation { + +using ::aidl::android::hardware::camera::common::CameraResourceCost; +using ::aidl::android::hardware::camera::common::Status; +using ::aidl::android::hardware::camera::device::BnCameraDevice; +using ::aidl::android::hardware::camera::device::CameraMetadata; +using ::aidl::android::hardware::camera::device::ICameraDeviceCallback; +using ::aidl::android::hardware::camera::device::ICameraDeviceSession; +using ::aidl::android::hardware::camera::device::ICameraInjectionSession; +using ::aidl::android::hardware::camera::device::StreamConfiguration; +using ::android::hardware::camera::common::helper::CameraModule; + +class CameraDevice : public BnCameraDevice { + public: + // Called by provider HAL. + // Provider HAL must ensure the uniqueness of CameraDevice object per cameraId, or there could + // be multiple CameraDevice trying to access the same physical camera. Also, provider will have + // to keep track of all CameraDevice objects in order to notify CameraDevice when the underlying + // camera is detached. + CameraDevice(sp module, const std::string& cameraId, + const SortedVector>& cameraDeviceNames); + virtual ~CameraDevice(); + + ndk::ScopedAStatus getCameraCharacteristics(CameraMetadata* _aidl_return) override; + ndk::ScopedAStatus getPhysicalCameraCharacteristics(const std::string& in_physicalCameraId, + CameraMetadata* _aidl_return) override; + ndk::ScopedAStatus getResourceCost(CameraResourceCost* _aidl_return) override; + ndk::ScopedAStatus isStreamCombinationSupported(const StreamConfiguration& in_streams, + bool* _aidl_return) override; + ndk::ScopedAStatus open(const std::shared_ptr& in_callback, + std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus openInjectionSession( + const std::shared_ptr& in_callback, + std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus setTorchMode(bool in_on) override; + ndk::ScopedAStatus turnOnTorchWithStrengthLevel(int32_t in_torchStrength) override; + ndk::ScopedAStatus getTorchStrengthLevel(int32_t* _aidl_return) override; + + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; + + // Caller must use this method to check if CameraDevice ctor failed + bool isInitFailed() { return mInitFail; } + // Used by provider HAL to signal external camera disconnected + void setConnectionStatus(bool connected); + + // Device version to be used by the external camera provider. + // Should be of the form . + static std::string kDeviceVersion; + + protected: + virtual std::shared_ptr createSession( + camera3_device_t*, const camera_metadata_t* deviceInfo, + const std::shared_ptr&); + + const sp mModule; + const std::string mCameraId; + // const after ctor + int mCameraIdInt; + int mDeviceVersion; + bool mInitFail = false; + // Set by provider (when external camera is connected/disconnected) + bool mDisconnected; + std::weak_ptr mSession{}; + + const SortedVector>& mCameraDeviceNames; + + // gating access to mSession and mDisconnected + mutable Mutex mLock; + + // convert conventional HAL status to AIDL Status + static Status getAidlStatus(int); + + Status initStatus() const; +}; + +} // namespace implementation +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/device/CameraDeviceSession.cpp b/aidl/camera/device/CameraDeviceSession.cpp new file mode 100644 index 0000000..df79f28 --- /dev/null +++ b/aidl/camera/device/CameraDeviceSession.cpp @@ -0,0 +1,1721 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_TAG "CamDevSession-impl" +#define ATRACE_TAG ATRACE_TAG_CAMERA + +#include "CameraDeviceSession.h" + +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace implementation { + +using ::aidl::android::hardware::camera::device::ErrorCode; +using ::aidl::android::hardware::camera::device::ErrorMsg; +using ::aidl::android::hardware::camera::device::PhysicalCameraMetadata; +using ::aidl::android::hardware::camera::device::ShutterMsg; +using ::aidl::android::hardware::camera::device::StreamType; +using ::aidl::android::hardware::graphics::common::BufferUsage; +using ::android::hardware::camera::common::helper::CameraModule; + +// Size of request metadata fast message queue. Change to 0 to always use hwbinder buffer. +static constexpr int32_t CAMERA_REQUEST_METADATA_QUEUE_SIZE = 1 << 20 /* 1MB */; +// Size of result metadata fast message queue. Change to 0 to always use hwbinder buffer. +static constexpr int32_t CAMERA_RESULT_METADATA_QUEUE_SIZE = 1 << 20 /* 1MB */; + +// Metadata sent by HAL will be replaced by a compact copy +// if their (total size >= compact size + METADATA_SHRINK_ABS_THRESHOLD && +// total_size >= compact size * METADATA_SHRINK_REL_THRESHOLD) +// Heuristically picked by size of one page +static constexpr int METADATA_SHRINK_ABS_THRESHOLD = 4096; +static constexpr int METADATA_SHRINK_REL_THRESHOLD = 2; + +HandleImporter CameraDeviceSession::sHandleImporter; +buffer_handle_t CameraDeviceSession::sEmptyBuffer = nullptr; + +CameraDeviceSession::CameraDeviceSession(camera3_device_t* device, + const camera_metadata_t* deviceInfo, + const std::shared_ptr& callback) + : camera3_callback_ops({&sProcessCaptureResult, &sNotify, nullptr, nullptr}), + mDevice(device), + mDeviceVersion(device->common.version), + mFreeBufEarly(shouldFreeBufEarly()), + mIsAELockAvailable(false), + mDerivePostRawSensKey(false), + mNumPartialResults(1), + mResultBatcher(callback) { + mDeviceInfo = deviceInfo; + camera_metadata_entry partialResultsCount = + mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT); + if (partialResultsCount.count > 0) { + mNumPartialResults = partialResultsCount.data.i32[0]; + } + mResultBatcher.setNumPartialResults(mNumPartialResults); + + camera_metadata_entry aeLockAvailableEntry = + mDeviceInfo.find(ANDROID_CONTROL_AE_LOCK_AVAILABLE); + if (aeLockAvailableEntry.count > 0) { + mIsAELockAvailable = + (aeLockAvailableEntry.data.u8[0] == ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE); + } + + // Determine whether we need to derive sensitivity boost values for older devices. + // If post-RAW sensitivity boost range is listed, so should post-raw sensitivity control + // be listed (as the default value 100) + if (mDeviceInfo.exists(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE)) { + mDerivePostRawSensKey = true; + } + + (void)CameraModule::isLogicalMultiCamera(mDeviceInfo, &mPhysicalCameraIds); + + mInitFail = initialize(); +} + +bool CameraDeviceSession::initialize() { + /** Initialize device with callback functions */ + ATRACE_BEGIN("camera3->initialize"); + status_t res = mDevice->ops->initialize(mDevice, this); + ATRACE_END(); + + if (res != OK) { + ALOGE("%s: Unable to initialize HAL device: %s (%d)", __FUNCTION__, strerror(-res), res); + mDevice->common.close(&mDevice->common); + mClosed = true; + return true; + } + + // "ro.camera" properties are no longer supported on vendor side. + // Support a fall back for the fmq size override that uses "ro.vendor.camera" + // properties. + int32_t reqFMQSize = property_get_int32("ro.vendor.camera.req.fmq.size", /*default*/ -1); + if (reqFMQSize < 0) { + reqFMQSize = property_get_int32("ro.camera.req.fmq.size", /*default*/ -1); + if (reqFMQSize < 0) { + reqFMQSize = CAMERA_REQUEST_METADATA_QUEUE_SIZE; + } else { + ALOGV("%s: request FMQ size overridden to %d", __FUNCTION__, reqFMQSize); + } + } else { + ALOGV("%s: request FMQ size overridden to %d via fallback property", __FUNCTION__, + reqFMQSize); + } + + mRequestMetadataQueue = std::make_unique(static_cast(reqFMQSize), + false /* non blocking */); + if (!mRequestMetadataQueue->isValid()) { + ALOGE("%s: invalid request fmq", __FUNCTION__); + return true; + } + + // "ro.camera" properties are no longer supported on vendor side. + // Support a fall back for the fmq size override that uses "ro.vendor.camera" + // properties. + int32_t resFMQSize = property_get_int32("ro.vendor.camera.res.fmq.size", /*default*/ -1); + if (resFMQSize < 0) { + resFMQSize = property_get_int32("ro.camera.res.fmq.size", /*default*/ -1); + if (resFMQSize < 0) { + resFMQSize = CAMERA_RESULT_METADATA_QUEUE_SIZE; + } else { + ALOGV("%s: result FMQ size overridden to %d", __FUNCTION__, resFMQSize); + } + } else { + ALOGV("%s: result FMQ size overridden to %d via fallback property", __FUNCTION__, + resFMQSize); + } + + mResultMetadataQueue = std::make_shared(static_cast(resFMQSize), + false /* non blocking */); + if (!mResultMetadataQueue->isValid()) { + ALOGE("%s: invalid result fmq", __FUNCTION__); + return true; + } + mResultBatcher.setResultMetadataQueue(mResultMetadataQueue); + + return false; +} + +bool CameraDeviceSession::shouldFreeBufEarly() { + return property_get_bool("ro.vendor.camera.free_buf_early", 0) == 1; +} + +CameraDeviceSession::~CameraDeviceSession() { + if (!isClosed()) { + ALOGE("CameraDeviceSession deleted before close!"); + close(); + } +} + +bool CameraDeviceSession::isClosed() { + Mutex::Autolock _l(mStateLock); + return mClosed; +} + +ndk::ScopedAStatus CameraDeviceSession::repeatingRequestEnd( + int32_t /*in_frameNumber*/, const std::vector& /*in_streamIds*/) { + // TODO: Figure this one out. + return fromStatus(Status::OK); +} + +Status CameraDeviceSession::initStatus() const { + Mutex::Autolock _l(mStateLock); + Status status = Status::OK; + if (mInitFail) { + status = Status::INTERNAL_ERROR; + } else if (mDisconnected) { + status = Status::CAMERA_DISCONNECTED; + } else if (mClosed) { + status = Status::INTERNAL_ERROR; + } + return status; +} + +void CameraDeviceSession::disconnect() { + Mutex::Autolock _l(mStateLock); + mDisconnected = true; + ALOGW("%s: Camera device is disconnected. Closing.", __FUNCTION__); + if (!mClosed) { + mDevice->common.close(&mDevice->common); + mClosed = true; + } +} + +/** + * For devices <= CAMERA_DEVICE_API_VERSION_3_2, AE_PRECAPTURE_TRIGGER_CANCEL is not supported so + * we need to override AE_PRECAPTURE_TRIGGER_CANCEL to AE_PRECAPTURE_TRIGGER_IDLE and AE_LOCK_OFF + * to AE_LOCK_ON to start cancelling AE precapture. If AE lock is not available, it still overrides + * AE_PRECAPTURE_TRIGGER_CANCEL to AE_PRECAPTURE_TRIGGER_IDLE but doesn't add AE_LOCK_ON to the + * request. + */ +bool CameraDeviceSession::handleAePrecaptureCancelRequestLocked( + const camera3_capture_request_t& halRequest, + ::android::hardware::camera::common::helper::CameraMetadata* settings /*out*/, + AETriggerCancelOverride* override /*out*/) { + if ((mDeviceVersion > CAMERA_DEVICE_API_VERSION_3_2) || (nullptr == halRequest.settings) || + (nullptr == settings) || (0 == get_camera_metadata_entry_count(halRequest.settings))) { + return false; + } + + settings->clear(); + settings->append(halRequest.settings); + camera_metadata_entry_t aePrecaptureTrigger = + settings->find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER); + if (aePrecaptureTrigger.count > 0 && + aePrecaptureTrigger.data.u8[0] == ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL) { + // Always override CANCEL to IDLE + uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE; + settings->update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &aePrecaptureTrigger, 1); + *override = {false, ANDROID_CONTROL_AE_LOCK_OFF, true, + ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL}; + + if (mIsAELockAvailable == true) { + camera_metadata_entry_t aeLock = settings->find(ANDROID_CONTROL_AE_LOCK); + if (aeLock.count == 0 || aeLock.data.u8[0] == ANDROID_CONTROL_AE_LOCK_OFF) { + uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_ON; + settings->update(ANDROID_CONTROL_AE_LOCK, &aeLock, 1); + override->applyAeLock = true; + override->aeLock = ANDROID_CONTROL_AE_LOCK_OFF; + } + } + + return true; + } + + return false; +} + +ndk::ScopedAStatus CameraDeviceSession::signalStreamFlush(const std::vector& streamIds, + int32_t streamConfigCounter) { + if (mDevice->ops->signal_stream_flush == nullptr) { + return fromStatus(Status::OK); + } + + uint32_t currentCounter = 0; + { + Mutex::Autolock _l(mStreamConfigCounterLock); + currentCounter = mStreamConfigCounter; + } + + if (streamConfigCounter < currentCounter) { + ALOGV("%s: streamConfigCounter %d is stale (current %d), skipping signal_stream_flush call", + __FUNCTION__, streamConfigCounter, mStreamConfigCounter); + return fromStatus(Status::OK); + } + + std::vector streams(streamIds.size()); + { + Mutex::Autolock _l(mInflightLock); + for (size_t i = 0; i < streamIds.size(); i++) { + int32_t id = streamIds[i]; + if (mStreamMap.count(id) == 0) { + ALOGE("%s: unknown streamId %d", __FUNCTION__, id); + return fromStatus(Status::OK); + } + streams[i] = &mStreamMap[id]; + } + } + + mDevice->ops->signal_stream_flush(mDevice, streams.size(), streams.data()); + return fromStatus(Status::OK); +} + +/** + * Override result metadata for cancelling AE precapture trigger applied in + * handleAePrecaptureCancelRequestLocked(). + */ +void CameraDeviceSession::overrideResultForPrecaptureCancelLocked( + const AETriggerCancelOverride& aeTriggerCancelOverride, + ::android::hardware::camera::common::helper::CameraMetadata* settings /*out*/) { + if (aeTriggerCancelOverride.applyAeLock) { + // Only devices <= v3.2 should have this override + assert(mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2); + settings->update(ANDROID_CONTROL_AE_LOCK, &aeTriggerCancelOverride.aeLock, 1); + } + + if (aeTriggerCancelOverride.applyAePrecaptureTrigger) { + // Only devices <= v3.2 should have this override + assert(mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2); + settings->update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, + &aeTriggerCancelOverride.aePrecaptureTrigger, 1); + } +} + +Status CameraDeviceSession::importBuffer(int32_t streamId, uint64_t bufId, buffer_handle_t buf, + /*out*/ buffer_handle_t** outBufPtr, bool allowEmptyBuf) { + if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) { + if (allowEmptyBuf) { + *outBufPtr = &sEmptyBuffer; + return Status::OK; + } else { + ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId); + return Status::ILLEGAL_ARGUMENT; + } + } + + Mutex::Autolock _l(mInflightLock); + CirculatingBuffers& cbs = mCirculatingBuffers[streamId]; + if (cbs.count(bufId) == 0) { + // Register a newly seen buffer + buffer_handle_t importedBuf = buf; + sHandleImporter.importBuffer(importedBuf); + if (importedBuf == nullptr) { + ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId); + return Status::INTERNAL_ERROR; + } else { + cbs[bufId] = importedBuf; + } + } + *outBufPtr = &cbs[bufId]; + return Status::OK; +} + +Status CameraDeviceSession::importRequest(const CaptureRequest& request, + std::vector& allBufPtrs, + std::vector& allFences) { + return importRequestImpl(request, allBufPtrs, allFences, /*allowEmptyBuf*/ true); +} + +Status CameraDeviceSession::importRequestImpl(const CaptureRequest& request, + std::vector& allBufPtrs, + std::vector& allFences, bool allowEmptyBuf) { + bool hasInputBuf = (request.inputBuffer.streamId != -1 && request.inputBuffer.bufferId != 0); + size_t numOutputBufs = request.outputBuffers.size(); + size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0); + // Validate all I/O buffers + std::vector allBufs; + std::vector allBufIds; + allBufs.resize(numBufs); + allBufIds.resize(numBufs); + allBufPtrs.resize(numBufs); + allFences.resize(numBufs); + std::vector streamIds(numBufs); + + for (size_t i = 0; i < numOutputBufs; i++) { + allBufs[i] = ::android::makeFromAidl(request.outputBuffers[i].buffer); + allBufIds[i] = request.outputBuffers[i].bufferId; + allBufPtrs[i] = &allBufs[i]; + streamIds[i] = request.outputBuffers[i].streamId; + } + if (hasInputBuf) { + allBufs[numOutputBufs] = ::android::makeFromAidl(request.inputBuffer.buffer); + allBufIds[numOutputBufs] = request.inputBuffer.bufferId; + allBufPtrs[numOutputBufs] = &allBufs[numOutputBufs]; + streamIds[numOutputBufs] = request.inputBuffer.streamId; + } + + for (size_t i = 0; i < numBufs; i++) { + Status st = importBuffer(streamIds[i], allBufIds[i], allBufs[i], &allBufPtrs[i], + // Disallow empty buf for input stream, otherwise follow + // the allowEmptyBuf argument. + (hasInputBuf && i == numOutputBufs) ? false : allowEmptyBuf); + if (st != Status::OK) { + // Detailed error logs printed in importBuffer + return st; + } + } + + // All buffers are imported. Now validate output buffer acquire fences + for (size_t i = 0; i < numOutputBufs; i++) { + if (!sHandleImporter.importFence( + ::android::makeFromAidl(request.outputBuffers[i].acquireFence), allFences[i])) { + ALOGE("%s: output buffer %zu acquire fence is invalid", __FUNCTION__, i); + cleanupInflightFences(allFences, i); + return Status::INTERNAL_ERROR; + } + } + + // Validate input buffer acquire fences + if (hasInputBuf) { + if (!sHandleImporter.importFence(::android::makeFromAidl(request.inputBuffer.acquireFence), + allFences[numOutputBufs])) { + ALOGE("%s: input buffer acquire fence is invalid", __FUNCTION__); + cleanupInflightFences(allFences, numOutputBufs); + return Status::INTERNAL_ERROR; + } + } + return Status::OK; +} + +void CameraDeviceSession::cleanupInflightFences(std::vector& allFences, size_t numFences) { + for (size_t j = 0; j < numFences; j++) { + sHandleImporter.closeFence(allFences[j]); + } +} + +CameraDeviceSession::ResultBatcher::ResultBatcher( + const std::shared_ptr& callback) + : mCallback(callback){}; + +bool CameraDeviceSession::ResultBatcher::InflightBatch::allDelivered() const { + if (!mShutterDelivered) return false; + + if (mPartialResultProgress < mNumPartialResults) { + return false; + } + + for (const auto& pair : mBatchBufs) { + if (!pair.second.mDelivered) { + return false; + } + } + return true; +} + +void CameraDeviceSession::ResultBatcher::setNumPartialResults(uint32_t n) { + Mutex::Autolock _l(mLock); + mNumPartialResults = n; +} + +void CameraDeviceSession::ResultBatcher::setBatchedStreams(const std::vector& streamsToBatch) { + Mutex::Autolock _l(mLock); + mStreamsToBatch = streamsToBatch; +} + +void CameraDeviceSession::ResultBatcher::setResultMetadataQueue( + std::shared_ptr q) { + Mutex::Autolock _l(mLock); + mResultMetadataQueue = q; +} + +void CameraDeviceSession::ResultBatcher::registerBatch(uint32_t frameNumber, uint32_t batchSize) { + auto batch = std::make_shared(); + batch->mFirstFrame = frameNumber; + batch->mBatchSize = batchSize; + batch->mLastFrame = batch->mFirstFrame + batch->mBatchSize - 1; + batch->mNumPartialResults = mNumPartialResults; + for (int id : mStreamsToBatch) { + batch->mBatchBufs.emplace(id, batch->mBatchSize); + } + Mutex::Autolock _l(mLock); + mInflightBatches.push_back(batch); +} + +std::pair> +CameraDeviceSession::ResultBatcher::getBatch(uint32_t frameNumber) { + Mutex::Autolock _l(mLock); + int numBatches = mInflightBatches.size(); + if (numBatches == 0) { + return std::make_pair(NOT_BATCHED, nullptr); + } + uint32_t frameMin = mInflightBatches[0]->mFirstFrame; + uint32_t frameMax = mInflightBatches[numBatches - 1]->mLastFrame; + if (frameNumber < frameMin || frameNumber > frameMax) { + return std::make_pair(NOT_BATCHED, nullptr); + } + for (int i = 0; i < numBatches; i++) { + if (frameNumber >= mInflightBatches[i]->mFirstFrame && + frameNumber <= mInflightBatches[i]->mLastFrame) { + return std::make_pair(i, mInflightBatches[i]); + } + } + return std::make_pair(NOT_BATCHED, nullptr); +} + +void CameraDeviceSession::ResultBatcher::checkAndRemoveFirstBatch() { + Mutex::Autolock _l(mLock); + if (mInflightBatches.size() > 0) { + std::shared_ptr batch = mInflightBatches[0]; + bool shouldRemove = false; + { + Mutex::Autolock _l(batch->mLock); + if (batch->allDelivered()) { + batch->mRemoved = true; + shouldRemove = true; + } + } + if (shouldRemove) { + mInflightBatches.pop_front(); + } + } +} + +void CameraDeviceSession::ResultBatcher::sendBatchShutterCbsLocked( + std::shared_ptr batch) { + if (batch->mShutterDelivered) { + ALOGW("%s: batch shutter callback already sent!", __FUNCTION__); + return; + } + + auto ret = mCallback->notify(batch->mShutterMsgs); + if (!ret.isOk()) { + ALOGE("%s: notify shutter transaction failed: %s", __FUNCTION__, + ret.getDescription().c_str()); + } + batch->mShutterDelivered = true; + batch->mShutterMsgs.clear(); +} + +void CameraDeviceSession::ResultBatcher::freeReleaseFences(std::vector& results) { + for (auto& result : results) { + if (::android::makeFromAidl(result.inputBuffer.releaseFence) != nullptr) { + native_handle_t* handle = const_cast( + ::android::makeFromAidl(result.inputBuffer.releaseFence)); + native_handle_close(handle); + native_handle_delete(handle); + } + for (auto& buf : result.outputBuffers) { + if (::android::makeFromAidl(buf.releaseFence) != nullptr) { + native_handle_t* handle = + const_cast(::android::makeFromAidl(buf.releaseFence)); + native_handle_close(handle); + native_handle_delete(handle); + } + } + } + return; +} + +void CameraDeviceSession::ResultBatcher::moveStreamBuffer(StreamBuffer&& src, StreamBuffer& dst) { + // Only dealing with releaseFence here. Assume buffer/acquireFence are null + const native_handle_t* handle = ::android::makeFromAidl(src.releaseFence); + src.releaseFence = makeToAidlIfNotNull(nullptr); + dst = std::move(src); + dst.releaseFence = makeToAidlIfNotNull(handle); + if (handle != ::android::makeFromAidl(dst.releaseFence)) { + ALOGE("%s: native handle cloned!", __FUNCTION__); + } +} + +void CameraDeviceSession::ResultBatcher::pushStreamBuffer(StreamBuffer&& src, + std::vector& dst) { + // Only dealing with releaseFence here. Assume buffer/acquireFence are null + const native_handle_t* handle = ::android::makeFromAidl(src.releaseFence); + src.releaseFence = makeToAidlIfNotNull(nullptr); + dst.push_back(std::move(src)); + dst.back().releaseFence = makeToAidlIfNotNull(handle); + if (handle != ::android::makeFromAidl(dst.back().releaseFence)) { + ALOGE("%s: native handle cloned!", __FUNCTION__); + } +} + +void CameraDeviceSession::ResultBatcher::sendBatchBuffersLocked( + std::shared_ptr batch) { + sendBatchBuffersLocked(batch, mStreamsToBatch); +} + +void CameraDeviceSession::ResultBatcher::sendBatchBuffersLocked( + std::shared_ptr batch, const std::vector& streams) { + size_t batchSize = 0; + for (int streamId : streams) { + auto it = batch->mBatchBufs.find(streamId); + if (it != batch->mBatchBufs.end()) { + InflightBatch::BufferBatch& bb = it->second; + if (bb.mDelivered) { + continue; + } + if (bb.mBuffers.size() > batchSize) { + batchSize = bb.mBuffers.size(); + } + } else { + ALOGE("%s: stream ID %d is not batched!", __FUNCTION__, streamId); + return; + } + } + + if (batchSize == 0) { + ALOGW("%s: there is no buffer to be delivered for this batch.", __FUNCTION__); + for (int streamId : streams) { + auto it = batch->mBatchBufs.find(streamId); + if (it == batch->mBatchBufs.end()) { + ALOGE("%s: cannot find stream %d in batched buffers!", __FUNCTION__, streamId); + return; + } + InflightBatch::BufferBatch& bb = it->second; + bb.mDelivered = true; + } + return; + } + + std::vector results; + results.resize(batchSize); + for (size_t i = 0; i < batchSize; i++) { + results[i].frameNumber = batch->mFirstFrame + i; + results[i].fmqResultSize = 0; + results[i].partialResult = 0; // 0 for buffer only results + results[i].inputBuffer.streamId = -1; + results[i].inputBuffer.bufferId = 0; + results[i].inputBuffer.buffer = makeToAidlIfNotNull(nullptr); + std::vector outBufs; + outBufs.reserve(streams.size()); + for (int streamId : streams) { + auto it = batch->mBatchBufs.find(streamId); + if (it == batch->mBatchBufs.end()) { + ALOGE("%s: cannot find stream %d in batched buffers!", __FUNCTION__, streamId); + return; + } + InflightBatch::BufferBatch& bb = it->second; + if (bb.mDelivered) { + continue; + } + if (i < bb.mBuffers.size()) { + pushStreamBuffer(std::move(bb.mBuffers[i]), outBufs); + } + } + results[i].outputBuffers.resize(outBufs.size()); + for (size_t j = 0; j < outBufs.size(); j++) { + moveStreamBuffer(std::move(outBufs[j]), results[i].outputBuffers[j]); + } + } + invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ false); + freeReleaseFences(results); + for (int streamId : streams) { + auto it = batch->mBatchBufs.find(streamId); + if (it == batch->mBatchBufs.end()) { + ALOGE("%s: cannot find stream %d in batched buffers!", __FUNCTION__, streamId); + return; + } + InflightBatch::BufferBatch& bb = it->second; + bb.mDelivered = true; + bb.mBuffers.clear(); + } +} + +void CameraDeviceSession::ResultBatcher::sendBatchMetadataLocked( + std::shared_ptr batch, uint32_t lastPartialResultIdx) { + if (lastPartialResultIdx <= batch->mPartialResultProgress) { + // Result has been delivered. Return + ALOGW("%s: partial result %u has been delivered", __FUNCTION__, lastPartialResultIdx); + return; + } + + std::vector results; + std::vector toBeRemovedIdxes; + for (auto& pair : batch->mResultMds) { + uint32_t partialIdx = pair.first; + if (partialIdx > lastPartialResultIdx) { + continue; + } + toBeRemovedIdxes.push_back(partialIdx); + InflightBatch::MetadataBatch& mb = pair.second; + for (const auto& p : mb.mMds) { + CaptureResult result; + result.frameNumber = p.first; + result.result = std::move(p.second); + result.fmqResultSize = 0; + result.inputBuffer.streamId = -1; + result.inputBuffer.bufferId = 0; + result.inputBuffer.buffer = makeToAidlIfNotNull(nullptr); + result.partialResult = partialIdx; + results.push_back(std::move(result)); + } + mb.mMds.clear(); + } + invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ true); + batch->mPartialResultProgress = lastPartialResultIdx; + for (uint32_t partialIdx : toBeRemovedIdxes) { + batch->mResultMds.erase(partialIdx); + } +} + +void CameraDeviceSession::ResultBatcher::notifySingleMsg(NotifyMsg& msg) { + auto ret = mCallback->notify({msg}); + if (!ret.isOk()) { + ALOGE("%s: notify transaction failed: %s", __FUNCTION__, ret.getDescription().c_str()); + } + return; +} + +void CameraDeviceSession::ResultBatcher::notify(NotifyMsg& msg) { + uint32_t frameNumber; + if (CC_LIKELY(msg.getTag() == NotifyMsg::Tag::shutter)) { + ShutterMsg shutter = msg.get(); + frameNumber = shutter.frameNumber; + } else { + ErrorMsg error = msg.get(); + frameNumber = error.frameNumber; + } + + auto pair = getBatch(frameNumber); + int batchIdx = pair.first; + if (batchIdx == NOT_BATCHED) { + notifySingleMsg(msg); + return; + } + + // When error happened, stop batching for all batches earlier + if (CC_UNLIKELY(msg.getTag() == NotifyMsg::Tag::error)) { + Mutex::Autolock _l(mLock); + for (int i = 0; i <= batchIdx; i++) { + // Send batched data up + std::shared_ptr batch = mInflightBatches[0]; + { + Mutex::Autolock _l(batch->mLock); + sendBatchShutterCbsLocked(batch); + sendBatchBuffersLocked(batch); + sendBatchMetadataLocked(batch, mNumPartialResults); + if (!batch->allDelivered()) { + ALOGE("%s: error: some batch data not sent back to framework!", __FUNCTION__); + } + batch->mRemoved = true; + } + mInflightBatches.pop_front(); + } + // Send the error up + notifySingleMsg(msg); + return; + } + // Queue shutter callbacks for future delivery + std::shared_ptr batch = pair.second; + { + Mutex::Autolock _l(batch->mLock); + // Check if the batch is removed (mostly by notify error) before lock was acquired + if (batch->mRemoved) { + // Fall back to non-batch path + notifySingleMsg(msg); + return; + } + + batch->mShutterMsgs.push_back(msg); + if (frameNumber == batch->mLastFrame) { + sendBatchShutterCbsLocked(batch); + } + } // end of batch lock scope + + // see if the batch is complete + if (frameNumber == batch->mLastFrame) { + checkAndRemoveFirstBatch(); + } +} + +void CameraDeviceSession::ResultBatcher::invokeProcessCaptureResultCallback( + std::vector& results, bool tryWriteFmq) { + if (mProcessCaptureResultLock.tryLock() != OK) { + ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__); + if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) { + ALOGE("%s: cannot acquire lock in 1s, cannot proceed", __FUNCTION__); + return; + } + } + if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) { + for (CaptureResult& result : results) { + if (result.result.metadata.size() > 0) { + if (mResultMetadataQueue->write( + reinterpret_cast(result.result.metadata.data()), + result.result.metadata.size())) { + result.fmqResultSize = result.result.metadata.size(); + result.result.metadata.resize(0); + } else { + ALOGW("%s: couldn't utilize fmq, fall back to hwbinder, result size: %zu," + "shared message queue available size: %zu", + __FUNCTION__, result.result.metadata.size(), + mResultMetadataQueue->availableToWrite()); + result.fmqResultSize = 0; + } + } + + for (auto& onePhysMetadata : result.physicalCameraMetadata) { + if (mResultMetadataQueue->write( + reinterpret_cast(onePhysMetadata.metadata.metadata.data()), + onePhysMetadata.metadata.metadata.size())) { + onePhysMetadata.fmqMetadataSize = onePhysMetadata.metadata.metadata.size(); + onePhysMetadata.metadata.metadata.resize(0); + } else { + ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__); + onePhysMetadata.fmqMetadataSize = 0; + } + } + } + } + auto ret = mCallback->processCaptureResult(results); + if (!ret.isOk()) { + ALOGE("%s: processCaptureResult transaction failed: %s", __FUNCTION__, + ret.getDescription().c_str()); + } + mProcessCaptureResultLock.unlock(); +} + +void CameraDeviceSession::ResultBatcher::processOneCaptureResult(CaptureResult& result) { + std::vector results; + results.resize(1); + results[0] = std::move(result); + invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ true); + freeReleaseFences(results); + return; +} + +void CameraDeviceSession::ResultBatcher::processCaptureResult(CaptureResult& result) { + auto pair = getBatch(result.frameNumber); + int batchIdx = pair.first; + if (batchIdx == NOT_BATCHED) { + processOneCaptureResult(result); + return; + } + std::shared_ptr batch = pair.second; + { + Mutex::Autolock _l(batch->mLock); + // Check if the batch is removed (mostly by notify error) before lock was acquired + if (batch->mRemoved) { + // Fall back to non-batch path + processOneCaptureResult(result); + return; + } + + // queue metadata + if (result.result.metadata.size() != 0) { + // Save a copy of metadata + batch->mResultMds[result.partialResult].mMds.push_back( + std::make_pair(result.frameNumber, result.result)); + } + + // queue buffer + std::vector filledStreams; + std::vector nonBatchedBuffers; + for (auto& buffer : result.outputBuffers) { + auto it = batch->mBatchBufs.find(buffer.streamId); + if (it != batch->mBatchBufs.end()) { + InflightBatch::BufferBatch& bb = it->second; + auto id = buffer.streamId; + pushStreamBuffer(std::move(buffer), bb.mBuffers); + filledStreams.push_back(id); + } else { + pushStreamBuffer(std::move(buffer), nonBatchedBuffers); + } + } + + // send non-batched buffers up + if (nonBatchedBuffers.size() > 0 || result.inputBuffer.streamId != -1) { + CaptureResult nonBatchedResult; + nonBatchedResult.frameNumber = result.frameNumber; + nonBatchedResult.fmqResultSize = 0; + nonBatchedResult.outputBuffers.resize(nonBatchedBuffers.size()); + for (size_t i = 0; i < nonBatchedBuffers.size(); i++) { + moveStreamBuffer(std::move(nonBatchedBuffers[i]), + nonBatchedResult.outputBuffers[i]); + } + moveStreamBuffer(std::move(result.inputBuffer), nonBatchedResult.inputBuffer); + nonBatchedResult.partialResult = 0; // 0 for buffer only results + processOneCaptureResult(nonBatchedResult); + } + + if (result.frameNumber == batch->mLastFrame) { + // Send data up + if (result.partialResult > 0) { + sendBatchMetadataLocked(batch, result.partialResult); + } + // send buffer up + if (filledStreams.size() > 0) { + sendBatchBuffersLocked(batch, filledStreams); + } + } + } // end of batch lock scope + + // see if the batch is complete + if (result.frameNumber == batch->mLastFrame) { + checkAndRemoveFirstBatch(); + } +} + +ndk::ScopedAStatus CameraDeviceSession::configureStreams( + const StreamConfiguration& in_requestedConfiguration, + std::vector* _aidl_return) { + Status status = initStatus(); + + // hold the inflight lock for entire configureStreams scope since there must not be any + // inflight request/results during stream configuration. + Mutex::Autolock _l(mInflightLock); + if (!mInflightBuffers.empty()) { + ALOGE("%s: trying to configureStreams while there are still %zu inflight buffers!", + __FUNCTION__, mInflightBuffers.size()); + return fromStatus(Status::INTERNAL_ERROR); + } + + if (!mInflightAETriggerOverrides.empty()) { + ALOGE("%s: trying to configureStreams while there are still %zu inflight" + " trigger overrides!", + __FUNCTION__, mInflightAETriggerOverrides.size()); + return fromStatus(Status::INTERNAL_ERROR); + } + + if (!mInflightRawBoostPresent.empty()) { + ALOGE("%s: trying to configureStreams while there are still %zu inflight" + " boost overrides!", + __FUNCTION__, mInflightRawBoostPresent.size()); + return fromStatus(Status::INTERNAL_ERROR); + } + + if (status != Status::OK) { + return fromStatus(status); + } + + const camera_metadata_t* paramBuffer = nullptr; + if (0 < in_requestedConfiguration.sessionParams.metadata.size()) { + convertFromAidl(in_requestedConfiguration.sessionParams, ¶mBuffer); + } + + camera3_stream_configuration_t stream_list{}; + // Block reading mStreamConfigCounter until configureStream returns + Mutex::Autolock _sccl(mStreamConfigCounterLock); + mStreamConfigCounter = in_requestedConfiguration.streamConfigCounter; + std::vector streams; + stream_list.session_parameters = paramBuffer; + if (!preProcessConfigurationLocked(in_requestedConfiguration, &stream_list, &streams)) { + return fromStatus(Status::INTERNAL_ERROR); + } + + ATRACE_BEGIN("camera3->configure_streams"); + status_t ret = mDevice->ops->configure_streams(mDevice, &stream_list); + ATRACE_END(); + + // In case Hal returns error most likely it was not able to release + // the corresponding resources of the deleted streams. + if (ret == OK) { + postProcessConfigurationLocked(in_requestedConfiguration); + } else { + postProcessConfigurationFailureLocked(in_requestedConfiguration); + } + + if (ret == -EINVAL) { + status = Status::ILLEGAL_ARGUMENT; + } else if (ret != OK) { + status = Status::INTERNAL_ERROR; + } else { + convertToAidl(stream_list, _aidl_return); + mFirstRequest = true; + } + + return fromStatus(status); +} + +// Needs to get called after acquiring 'mInflightLock' +void CameraDeviceSession::cleanupBuffersLocked(int id) { + for (auto& pair : mCirculatingBuffers.at(id)) { + sHandleImporter.freeBuffer(pair.second); + } + mCirculatingBuffers[id].clear(); + mCirculatingBuffers.erase(id); +} + +void CameraDeviceSession::updateBufferCaches(const std::vector& cachesToRemove) { + Mutex::Autolock _l(mInflightLock); + for (auto& cache : cachesToRemove) { + auto cbsIt = mCirculatingBuffers.find(cache.streamId); + if (cbsIt == mCirculatingBuffers.end()) { + // The stream could have been removed + continue; + } + CirculatingBuffers& cbs = cbsIt->second; + auto it = cbs.find(cache.bufferId); + if (it != cbs.end()) { + sHandleImporter.freeBuffer(it->second); + cbs.erase(it); + } else { + ALOGE("%s: stream %d buffer %" PRIu64 " is not cached", __FUNCTION__, cache.streamId, + cache.bufferId); + } + } +} + +bool CameraDeviceSession::preProcessConfigurationLocked( + const StreamConfiguration& requestedConfiguration, + camera3_stream_configuration_t* stream_list /*out*/, + std::vector* streams /*out*/) { + if ((stream_list == nullptr) || (streams == nullptr)) { + return false; + } + + stream_list->operation_mode = (uint32_t)requestedConfiguration.operationMode; + stream_list->num_streams = requestedConfiguration.streams.size(); + streams->resize(stream_list->num_streams); + stream_list->streams = streams->data(); + + for (uint32_t i = 0; i < stream_list->num_streams; i++) { + int id = requestedConfiguration.streams[i].id; + + if (mStreamMap.count(id) == 0) { + Camera3Stream stream; + convertFromAidl(requestedConfiguration.streams[i], &stream); + mStreamMap[id] = stream; + mPhysicalCameraIdMap[id] = requestedConfiguration.streams[i].physicalCameraId; + mStreamMap[id].data_space = mStreamMap[id].data_space; + mCirculatingBuffers.emplace(stream.mId, CirculatingBuffers{}); + } else { + // width/height/format must not change, but usage/rotation might need to change. + // format and data_space may change. + if (mStreamMap[id].stream_type != (int)requestedConfiguration.streams[i].streamType || + mStreamMap[id].width != requestedConfiguration.streams[i].width || + mStreamMap[id].height != requestedConfiguration.streams[i].height || + mPhysicalCameraIdMap[id] != requestedConfiguration.streams[i].physicalCameraId) { + ALOGE("%s: stream %d configuration changed!", __FUNCTION__, id); + return false; + } + mStreamMap[id].format = (int)requestedConfiguration.streams[i].format; + mStreamMap[id].data_space = + (android_dataspace_t)requestedConfiguration.streams[i].dataSpace; + mStreamMap[id].rotation = (int)requestedConfiguration.streams[i].rotation; + mStreamMap[id].usage = (uint32_t)requestedConfiguration.streams[i].usage; + } + // It is possible for the entry in 'mStreamMap' to get initialized by an older + // HIDL API. Make sure that the physical id is always initialized when using + // a more recent API call. + mStreamMap[id].physical_camera_id = mPhysicalCameraIdMap[id].c_str(); + + (*streams)[i] = &mStreamMap[id]; + } + + if (mFreeBufEarly) { + // Remove buffers of deleted streams + for (auto it = mStreamMap.begin(); it != mStreamMap.end(); it++) { + int id = it->first; + bool found = false; + for (const auto& stream : requestedConfiguration.streams) { + if (id == stream.id) { + found = true; + break; + } + } + if (!found) { + // Unmap all buffers of deleted stream + cleanupBuffersLocked(id); + } + } + } + return true; +} + +void CameraDeviceSession::postProcessConfigurationLocked( + const StreamConfiguration& requestedConfiguration) { + // delete unused streams, note we do this after adding new streams to ensure new stream + // will not have the same address as deleted stream, and HAL has a chance to reference + // the to be deleted stream in configure_streams call + for (auto it = mStreamMap.begin(); it != mStreamMap.end();) { + int id = it->first; + bool found = false; + for (const auto& stream : requestedConfiguration.streams) { + if (id == stream.id) { + found = true; + break; + } + } + if (!found) { + // Unmap all buffers of deleted stream + // in case the configuration call succeeds and HAL + // is able to release the corresponding resources too. + if (!mFreeBufEarly) { + cleanupBuffersLocked(id); + } + it = mStreamMap.erase(it); + } else { + ++it; + } + } + + // Track video streams + mVideoStreamIds.clear(); + for (const auto& stream : requestedConfiguration.streams) { + if (stream.streamType == StreamType::OUTPUT && + (int64_t)stream.usage & (int64_t)BufferUsage::VIDEO_ENCODER) { + mVideoStreamIds.push_back(stream.id); + } + } + mResultBatcher.setBatchedStreams(mVideoStreamIds); +} + +void CameraDeviceSession::postProcessConfigurationFailureLocked( + const StreamConfiguration& requestedConfiguration) { + if (mFreeBufEarly) { + // Re-build the buf cache entry for deleted streams + for (auto it = mStreamMap.begin(); it != mStreamMap.end(); it++) { + int id = it->first; + bool found = false; + for (const auto& stream : requestedConfiguration.streams) { + if (id == stream.id) { + found = true; + break; + } + } + if (!found) { + mCirculatingBuffers.emplace(id, CirculatingBuffers{}); + } + } + } +} + +ndk::ScopedAStatus CameraDeviceSession::constructDefaultRequestSettings( + RequestTemplate type, CameraMetadata* _aidl_return) { + Status status = constructDefaultRequestSettingsRaw((int)type, _aidl_return); + return fromStatus(status); +} + +Status CameraDeviceSession::constructDefaultRequestSettingsRaw(int type, + CameraMetadata* outMetadata) { + Status status = initStatus(); + const camera_metadata_t* rawRequest; + if (status == Status::OK) { + ATRACE_BEGIN("camera3->construct_default_request_settings"); + rawRequest = mDevice->ops->construct_default_request_settings(mDevice, (int)type); + ATRACE_END(); + if (rawRequest == nullptr) { + ALOGI("%s: template %d is not supported on this camera device", __FUNCTION__, type); + status = Status::ILLEGAL_ARGUMENT; + } else { + mOverridenRequest.clear(); + mOverridenRequest.append(rawRequest); + // Derive some new keys for backward compatibility + if (mDerivePostRawSensKey && + !mOverridenRequest.exists(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST)) { + int32_t defaultBoost[1] = {100}; + mOverridenRequest.update(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST, defaultBoost, + 1); + } + const camera_metadata_t* metaBuffer = mOverridenRequest.getAndLock(); + convertToAidl(metaBuffer, outMetadata); + mOverridenRequest.unlock(metaBuffer); + } + } + return status; +} + +ndk::ScopedAStatus CameraDeviceSession::flush() { + Status status = initStatus(); + if (status == Status::OK) { + // Flush is always supported on device 3.1 or later + status_t ret = mDevice->ops->flush(mDevice); + if (ret != OK) { + status = Status::INTERNAL_ERROR; + } + } + return fromStatus(status); +} + +ndk::ScopedAStatus CameraDeviceSession::getCaptureRequestMetadataQueue( + MQDescriptor* _aidl_return) { + *_aidl_return = mRequestMetadataQueue->dupeDesc(); + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraDeviceSession::getCaptureResultMetadataQueue( + MQDescriptor* _aidl_return) { + *_aidl_return = mResultMetadataQueue->dupeDesc(); + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraDeviceSession::isReconfigurationRequired( + const CameraMetadata& in_oldSessionParams, const CameraMetadata& in_newSessionParams, + bool* _aidl_return) { + // reconfiguration required if there is any change in the session params + *_aidl_return = in_oldSessionParams != in_newSessionParams; + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraDeviceSession::processCaptureRequest( + const std::vector& in_requests, + const std::vector& in_cachesToRemove, int32_t* _aidl_return) { + updateBufferCaches(in_cachesToRemove); + + *_aidl_return = 0; + Status s = Status::OK; + for (size_t i = 0; i < in_requests.size(); i++) { + s = processOneCaptureRequest(in_requests[i]); + if (s != Status::OK) { + break; + } + *_aidl_return = static_cast(i) + 1; + } + + if (s == Status::OK && in_requests.size() > 1) { + mResultBatcher.registerBatch(in_requests[0].frameNumber, in_requests.size()); + } + + return fromStatus(s); +} + +Status CameraDeviceSession::processOneCaptureRequest(const CaptureRequest& request) { + Status status = initStatus(); + if (status != Status::OK) { + ALOGE("%s: camera init failed or disconnected", __FUNCTION__); + return status; + } + + camera3_capture_request_t halRequest; + halRequest.frame_number = request.frameNumber; + + bool converted = true; + CameraMetadata settingsFmq; // settings from FMQ + if (request.fmqSettingsSize > 0) { + // non-blocking read; client must write metadata before calling + // processOneCaptureRequest + settingsFmq.metadata.resize(request.fmqSettingsSize); + bool read = mRequestMetadataQueue->read( + reinterpret_cast(settingsFmq.metadata.data()), request.fmqSettingsSize); + if (read) { + converted = convertFromAidl(settingsFmq, &halRequest.settings); + } else { + ALOGE("%s: capture request settings metadata couldn't be read from fmq!", __FUNCTION__); + converted = false; + } + } else { + converted = convertFromAidl(request.settings, &halRequest.settings); + } + + if (!converted) { + ALOGE("%s: capture request settings metadata is corrupt!", __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + + if (mFirstRequest && halRequest.settings == nullptr) { + ALOGE("%s: capture request settings must not be null for first request!", __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + + std::vector allBufPtrs; + std::vector allFences; + bool hasInputBuf = (request.inputBuffer.streamId != -1 && request.inputBuffer.bufferId != 0); + size_t numOutputBufs = request.outputBuffers.size(); + size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0); + + if (numOutputBufs == 0) { + ALOGE("%s: capture request must have at least one output buffer!", __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + + status = importRequest(request, allBufPtrs, allFences); + if (status != Status::OK) { + return status; + } + + std::vector outHalBufs; + outHalBufs.resize(numOutputBufs); + bool aeCancelTriggerNeeded = false; + ::android::hardware::camera::common::helper::CameraMetadata settingsOverride; + { + Mutex::Autolock _l(mInflightLock); + if (hasInputBuf) { + auto streamId = request.inputBuffer.streamId; + auto key = std::make_pair(request.inputBuffer.streamId, request.frameNumber); + auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{}; + convertFromAidl(allBufPtrs[numOutputBufs], request.inputBuffer.status, + &mStreamMap[request.inputBuffer.streamId], allFences[numOutputBufs], + &bufCache); + bufCache.stream->physical_camera_id = mPhysicalCameraIdMap[streamId].c_str(); + halRequest.input_buffer = &bufCache; + } else { + halRequest.input_buffer = nullptr; + } + + halRequest.num_output_buffers = numOutputBufs; + for (size_t i = 0; i < numOutputBufs; i++) { + auto streamId = request.outputBuffers[i].streamId; + auto key = std::make_pair(streamId, request.frameNumber); + auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{}; + convertFromAidl(allBufPtrs[i], request.outputBuffers[i].status, &mStreamMap[streamId], + allFences[i], &bufCache); + bufCache.stream->physical_camera_id = mPhysicalCameraIdMap[streamId].c_str(); + outHalBufs[i] = bufCache; + } + halRequest.output_buffers = outHalBufs.data(); + + AETriggerCancelOverride triggerOverride; + aeCancelTriggerNeeded = handleAePrecaptureCancelRequestLocked( + halRequest, &settingsOverride /*out*/, &triggerOverride /*out*/); + if (aeCancelTriggerNeeded) { + mInflightAETriggerOverrides[halRequest.frame_number] = triggerOverride; + halRequest.settings = settingsOverride.getAndLock(); + } + } + + std::vector physicalCameraIds; + std::vector physicalCameraSettings; + std::vector physicalFmq; + size_t settingsCount = request.physicalCameraSettings.size(); + if (settingsCount > 0) { + physicalCameraIds.reserve(settingsCount); + physicalCameraSettings.reserve(settingsCount); + physicalFmq.reserve(settingsCount); + + for (size_t i = 0; i < settingsCount; i++) { + uint64_t settingsSize = request.physicalCameraSettings[i].fmqSettingsSize; + const camera_metadata_t* settings = nullptr; + if (settingsSize > 0) { + physicalFmq.push_back(CameraMetadata()); + physicalFmq[i].metadata.resize(settingsSize); + bool read = mRequestMetadataQueue->read( + reinterpret_cast(physicalFmq[i].metadata.data()), settingsSize); + if (read) { + converted = convertFromAidl(physicalFmq[i], &settings); + physicalCameraSettings.push_back(settings); + } else { + ALOGE("%s: physical camera settings metadata couldn't be read from fmq!", + __FUNCTION__); + converted = false; + } + } else { + converted = convertFromAidl(request.physicalCameraSettings[i].settings, &settings); + physicalCameraSettings.push_back(settings); + } + + if (!converted) { + ALOGE("%s: physical camera settings metadata is corrupt!", __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + + if (mFirstRequest && settings == nullptr) { + ALOGE("%s: Individual request settings must not be null for first request!", + __FUNCTION__); + return Status::ILLEGAL_ARGUMENT; + } + + physicalCameraIds.push_back(request.physicalCameraSettings[i].physicalCameraId.c_str()); + } + } + halRequest.num_physcam_settings = settingsCount; + halRequest.physcam_id = physicalCameraIds.data(); + halRequest.physcam_settings = physicalCameraSettings.data(); + + ATRACE_ASYNC_BEGIN("frame capture", request.frameNumber); + ATRACE_BEGIN("camera3->process_capture_request"); + status_t ret = mDevice->ops->process_capture_request(mDevice, &halRequest); + ATRACE_END(); + if (aeCancelTriggerNeeded) { + settingsOverride.unlock(halRequest.settings); + } + if (ret != OK) { + Mutex::Autolock _l(mInflightLock); + ALOGE("%s: HAL process_capture_request call failed!", __FUNCTION__); + + cleanupInflightFences(allFences, numBufs); + if (hasInputBuf) { + auto key = std::make_pair(request.inputBuffer.streamId, request.frameNumber); + mInflightBuffers.erase(key); + } + for (size_t i = 0; i < numOutputBufs; i++) { + auto key = std::make_pair(request.outputBuffers[i].streamId, request.frameNumber); + mInflightBuffers.erase(key); + } + if (aeCancelTriggerNeeded) { + mInflightAETriggerOverrides.erase(request.frameNumber); + } + + if (ret == BAD_VALUE) { + return Status::ILLEGAL_ARGUMENT; + } else { + return Status::INTERNAL_ERROR; + } + } + + mFirstRequest = false; + return Status::OK; +} + +ndk::ScopedAStatus CameraDeviceSession::close() { + Mutex::Autolock _l(mStateLock); + if (!mClosed) { + { + Mutex::Autolock _l(mInflightLock); + if (!mInflightBuffers.empty()) { + ALOGE("%s: trying to close while there are still %zu inflight buffers!", + __FUNCTION__, mInflightBuffers.size()); + } + if (!mInflightAETriggerOverrides.empty()) { + ALOGE("%s: trying to close while there are still %zu inflight " + "trigger overrides!", + __FUNCTION__, mInflightAETriggerOverrides.size()); + } + if (!mInflightRawBoostPresent.empty()) { + ALOGE("%s: trying to close while there are still %zu inflight " + " RAW boost overrides!", + __FUNCTION__, mInflightRawBoostPresent.size()); + } + } + + ATRACE_BEGIN("camera3->close"); + mDevice->common.close(&mDevice->common); + ATRACE_END(); + + // free all imported buffers + Mutex::Autolock _l(mInflightLock); + for (auto& pair : mCirculatingBuffers) { + CirculatingBuffers& buffers = pair.second; + for (auto& p2 : buffers) { + sHandleImporter.freeBuffer(p2.second); + } + buffers.clear(); + } + mCirculatingBuffers.clear(); + + mClosed = true; + } + return fromStatus(Status::OK); +} + +uint64_t CameraDeviceSession::popBufferId(const buffer_handle_t& buf, int streamId) { + std::lock_guard lock(mBufferIdMapLock); + + auto streamIt = mBufferIdMaps.find(streamId); + if (streamIt == mBufferIdMaps.end()) { + return BUFFER_ID_NO_BUFFER; + } + BufferIdMap& bIdMap = streamIt->second; + auto it = bIdMap.find(buf); + if (it == bIdMap.end()) { + return BUFFER_ID_NO_BUFFER; + } + uint64_t bufId = it->second; + bIdMap.erase(it); + if (bIdMap.empty()) { + mBufferIdMaps.erase(streamIt); + } + return bufId; +} + +uint64_t CameraDeviceSession::getCapResultBufferId(const buffer_handle_t& buf, int streamId) { + return popBufferId(buf, streamId); +} + +status_t CameraDeviceSession::constructCaptureResult(CaptureResult& result, + const camera3_capture_result* hal_result) { + uint32_t frameNumber = hal_result->frame_number; + bool hasInputBuf = (hal_result->input_buffer != nullptr); + size_t numOutputBufs = hal_result->num_output_buffers; + size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0); + if (numBufs > 0) { + Mutex::Autolock _l(mInflightLock); + if (hasInputBuf) { + int streamId = static_cast(hal_result->input_buffer->stream)->mId; + // validate if buffer is inflight + auto key = std::make_pair(streamId, frameNumber); + if (mInflightBuffers.count(key) != 1) { + ALOGE("%s: input buffer for stream %d frame %d is not inflight!", __FUNCTION__, + streamId, frameNumber); + return -EINVAL; + } + } + + for (size_t i = 0; i < numOutputBufs; i++) { + int streamId = static_cast(hal_result->output_buffers[i].stream)->mId; + // validate if buffer is inflight + auto key = std::make_pair(streamId, frameNumber); + if (mInflightBuffers.count(key) != 1) { + ALOGE("%s: output buffer for stream %d frame %d is not inflight!", __FUNCTION__, + streamId, frameNumber); + return -EINVAL; + } + } + } + // We don't need to validate/import fences here since we will be passing them to camera service + // within the scope of this function + result.frameNumber = frameNumber; + result.fmqResultSize = 0; + result.partialResult = hal_result->partial_result; + convertToAidl(hal_result->result, &result.result); + if (nullptr != hal_result->result) { + bool resultOverriden = false; + Mutex::Autolock _l(mInflightLock); + + // Derive some new keys for backward compatibility + if (mDerivePostRawSensKey) { + camera_metadata_ro_entry entry; + if (find_camera_metadata_ro_entry(hal_result->result, + ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST, + &entry) == 0) { + mInflightRawBoostPresent[frameNumber] = true; + } else { + auto entry = mInflightRawBoostPresent.find(frameNumber); + if (mInflightRawBoostPresent.end() == entry) { + mInflightRawBoostPresent[frameNumber] = false; + } + } + + if ((hal_result->partial_result == mNumPartialResults)) { + if (!mInflightRawBoostPresent[frameNumber]) { + if (!resultOverriden) { + mOverridenResult.clear(); + mOverridenResult.append(hal_result->result); + resultOverriden = true; + } + int32_t defaultBoost[1] = {100}; + mOverridenResult.update(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST, + defaultBoost, 1); + } + + mInflightRawBoostPresent.erase(frameNumber); + } + } + + auto entry = mInflightAETriggerOverrides.find(frameNumber); + if (mInflightAETriggerOverrides.end() != entry) { + if (!resultOverriden) { + mOverridenResult.clear(); + mOverridenResult.append(hal_result->result); + resultOverriden = true; + } + overrideResultForPrecaptureCancelLocked(entry->second, &mOverridenResult); + if (hal_result->partial_result == mNumPartialResults) { + mInflightAETriggerOverrides.erase(frameNumber); + } + } + + if (resultOverriden) { + const camera_metadata_t* metaBuffer = mOverridenResult.getAndLock(); + convertToAidl(metaBuffer, &result.result); + mOverridenResult.unlock(metaBuffer); + } + } + if (hasInputBuf) { + result.inputBuffer.streamId = + static_cast(hal_result->input_buffer->stream)->mId; + result.inputBuffer.buffer = makeToAidlIfNotNull(nullptr); + result.inputBuffer.status = (BufferStatus)hal_result->input_buffer->status; + // skip acquire fence since it's no use to camera service + if (hal_result->input_buffer->release_fence != -1) { + native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0); + handle->data[0] = hal_result->input_buffer->release_fence; + result.inputBuffer.releaseFence = makeToAidlIfNotNull(handle); + } else { + result.inputBuffer.releaseFence = makeToAidlIfNotNull(nullptr); + } + } else { + result.inputBuffer.streamId = -1; + } + + result.outputBuffers.resize(numOutputBufs); + for (size_t i = 0; i < numOutputBufs; i++) { + result.outputBuffers[i].streamId = + static_cast(hal_result->output_buffers[i].stream)->mId; + result.outputBuffers[i].buffer = makeToAidlIfNotNull(nullptr); + if (hal_result->output_buffers[i].buffer != nullptr) { + result.outputBuffers[i].bufferId = getCapResultBufferId( + *(hal_result->output_buffers[i].buffer), result.outputBuffers[i].streamId); + } else { + result.outputBuffers[i].bufferId = 0; + } + + result.outputBuffers[i].status = (BufferStatus)hal_result->output_buffers[i].status; + // skip acquire fence since it's of no use to camera service + if (hal_result->output_buffers[i].release_fence != -1) { + native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0); + handle->data[0] = hal_result->output_buffers[i].release_fence; + result.outputBuffers[i].releaseFence = makeToAidlIfNotNull(handle); + } else { + result.outputBuffers[i].releaseFence = makeToAidlIfNotNull(nullptr); + } + } + + // Free inflight record/fences. + // Do this before call back to camera service because camera service might jump to + // configure_streams right after the processCaptureResult call so we need to finish + // updating inflight queues first + if (numBufs > 0) { + Mutex::Autolock _l(mInflightLock); + if (hasInputBuf) { + int streamId = static_cast(hal_result->input_buffer->stream)->mId; + auto key = std::make_pair(streamId, frameNumber); + mInflightBuffers.erase(key); + } + + for (size_t i = 0; i < numOutputBufs; i++) { + int streamId = static_cast(hal_result->output_buffers[i].stream)->mId; + auto key = std::make_pair(streamId, frameNumber); + mInflightBuffers.erase(key); + } + + if (mInflightBuffers.empty()) { + ALOGV("%s: inflight buffer queue is now empty!", __FUNCTION__); + } + } + return OK; +} + +// Static helper method to copy/shrink capture result metadata sent by HAL +void CameraDeviceSession::sShrinkCaptureResult( + camera3_capture_result* dst, const camera3_capture_result* src, + std::vector<::android::hardware::camera::common::helper::CameraMetadata>* mds, + std::vector* physCamMdArray, bool handlePhysCam) { + *dst = *src; + // Reserve maximum number of entries to avoid metadata re-allocation. + mds->reserve(1 + (handlePhysCam ? src->num_physcam_metadata : 0)); + if (sShouldShrink(src->result)) { + mds->emplace_back(sCreateCompactCopy(src->result)); + dst->result = mds->back().getAndLock(); + } + + if (handlePhysCam) { + // First determine if we need to create new camera_metadata_t* array + bool needShrink = false; + for (uint32_t i = 0; i < src->num_physcam_metadata; i++) { + if (sShouldShrink(src->physcam_metadata[i])) { + needShrink = true; + } + } + + if (!needShrink) return; + + physCamMdArray->reserve(src->num_physcam_metadata); + dst->physcam_metadata = physCamMdArray->data(); + for (uint32_t i = 0; i < src->num_physcam_metadata; i++) { + if (sShouldShrink(src->physcam_metadata[i])) { + mds->emplace_back(sCreateCompactCopy(src->physcam_metadata[i])); + dst->physcam_metadata[i] = mds->back().getAndLock(); + } else { + dst->physcam_metadata[i] = src->physcam_metadata[i]; + } + } + } +} + +bool CameraDeviceSession::sShouldShrink(const camera_metadata_t* md) { + size_t compactSize = get_camera_metadata_compact_size(md); + size_t totalSize = get_camera_metadata_size(md); + if (totalSize >= compactSize + METADATA_SHRINK_ABS_THRESHOLD && + totalSize >= compactSize * METADATA_SHRINK_REL_THRESHOLD) { + ALOGV("Camera metadata should be shrunk from %zu to %zu", totalSize, compactSize); + return true; + } + return false; +} + +camera_metadata_t* CameraDeviceSession::sCreateCompactCopy(const camera_metadata_t* src) { + size_t compactSize = get_camera_metadata_compact_size(src); + void* buffer = calloc(1, compactSize); + if (buffer == nullptr) { + ALOGE("%s: Allocating %zu bytes failed", __FUNCTION__, compactSize); + } + return copy_camera_metadata(buffer, compactSize, src); +} + +/** + * Static callback forwarding methods from HAL to instance + */ +void CameraDeviceSession::sProcessCaptureResult(const camera3_callback_ops* cb, + const camera3_capture_result* hal_result) { + CameraDeviceSession* d = + const_cast(static_cast(cb)); + + CaptureResult result = {}; + camera3_capture_result shadowResult; + bool handlePhysCam = (d->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_5); + std::vector<::android::hardware::camera::common::helper::CameraMetadata> compactMds; + std::vector physCamMdArray; + sShrinkCaptureResult(&shadowResult, hal_result, &compactMds, &physCamMdArray, handlePhysCam); + + status_t ret = d->constructCaptureResult(result, &shadowResult); + if (ret != OK) { + return; + } + + if (handlePhysCam) { + if (shadowResult.num_physcam_metadata > d->mPhysicalCameraIds.size()) { + ALOGE("%s: Fatal: Invalid num_physcam_metadata %u", __FUNCTION__, + shadowResult.num_physcam_metadata); + return; + } + result.physicalCameraMetadata.resize(shadowResult.num_physcam_metadata); + for (uint32_t i = 0; i < shadowResult.num_physcam_metadata; i++) { + std::string physicalId = shadowResult.physcam_ids[i]; + if (d->mPhysicalCameraIds.find(physicalId) == d->mPhysicalCameraIds.end()) { + ALOGE("%s: Fatal: Invalid physcam_ids[%u]: %s", __FUNCTION__, i, + shadowResult.physcam_ids[i]); + return; + } + CameraMetadata physicalMetadata; + convertToAidl(shadowResult.physcam_metadata[i], &physicalMetadata); + PhysicalCameraMetadata physicalCameraMetadata = {.fmqMetadataSize = 0, + .physicalCameraId = physicalId, + .metadata = physicalMetadata}; + result.physicalCameraMetadata[i] = physicalCameraMetadata; + } + } + d->mResultBatcher.processCaptureResult(result); +} + +void CameraDeviceSession::sNotify(const camera3_callback_ops* cb, const camera3_notify_msg* msg) { + CameraDeviceSession* d = + const_cast(static_cast(cb)); + NotifyMsg aidlMsg; + convertToAidl(msg, &aidlMsg); + + if (aidlMsg.getTag() == NotifyMsg::Tag::error) { + ErrorMsg error = aidlMsg.get(); + + if (error.errorStreamId != -1 && d->mStreamMap.count(error.errorStreamId) != 1) { + ALOGE("%s: unknown stream ID %d reports an error!", __FUNCTION__, error.errorStreamId); + return; + } + + switch (error.errorCode) { + case ErrorCode::ERROR_DEVICE: + case ErrorCode::ERROR_REQUEST: + case ErrorCode::ERROR_RESULT: { + Mutex::Autolock _l(d->mInflightLock); + auto entry = d->mInflightAETriggerOverrides.find(error.frameNumber); + if (d->mInflightAETriggerOverrides.end() != entry) { + d->mInflightAETriggerOverrides.erase(error.frameNumber); + } + + auto boostEntry = d->mInflightRawBoostPresent.find(error.frameNumber); + if (d->mInflightRawBoostPresent.end() != boostEntry) { + d->mInflightRawBoostPresent.erase(error.frameNumber); + } + + } break; + case ErrorCode::ERROR_BUFFER: + default: + break; + } + } + + d->mResultBatcher.notify(aidlMsg); +} + +ndk::ScopedAStatus CameraDeviceSession::switchToOffline( + const std::vector& /*in_streamsToKeep*/, + CameraOfflineSessionInfo* /*out_offlineSessionInfo*/, + std::shared_ptr* /*_aidl_return*/) { + return fromStatus(Status::OPERATION_NOT_SUPPORTED); +} + +} // namespace implementation +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/device/CameraDeviceSession.h b/aidl/camera/device/CameraDeviceSession.h new file mode 100644 index 0000000..7bb38dc --- /dev/null +++ b/aidl/camera/device/CameraDeviceSession.h @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "convert.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace implementation { + +using ::aidl::android::hardware::camera::common::Status; +using ::aidl::android::hardware::camera::device::BnCameraDeviceSession; +using ::aidl::android::hardware::camera::device::BufferCache; +using ::aidl::android::hardware::camera::device::CameraMetadata; +using ::aidl::android::hardware::camera::device::CameraOfflineSessionInfo; +using ::aidl::android::hardware::camera::device::CaptureRequest; +using ::aidl::android::hardware::camera::device::CaptureResult; +using ::aidl::android::hardware::camera::device::HalStream; +using ::aidl::android::hardware::camera::device::ICameraDeviceCallback; +using ::aidl::android::hardware::camera::device::ICameraOfflineSession; +using ::aidl::android::hardware::camera::device::NotifyMsg; +using ::aidl::android::hardware::camera::device::RequestTemplate; +using ::aidl::android::hardware::camera::device::StreamBuffer; +using ::aidl::android::hardware::camera::device::StreamConfiguration; +using ::android::AidlMessageQueue; +using ::android::hardware::camera::common::helper::HandleImporter; + +/** + * Function pointer types with C calling convention to + * use for HAL callback functions. + */ +extern "C" { +typedef void(callbacks_process_capture_result_t)(const struct camera3_callback_ops*, + const camera3_capture_result_t*); + +typedef void(callbacks_notify_t)(const struct camera3_callback_ops*, const camera3_notify_msg_t*); +} + +class CameraDeviceSession : public BnCameraDeviceSession, protected camera3_callback_ops { + public: + CameraDeviceSession(camera3_device_t*, const camera_metadata_t* deviceInfo, + const std::shared_ptr&); + virtual ~CameraDeviceSession(); + // Caller must use this method to check if CameraDeviceSession ctor failed + bool isInitFailed() { return mInitFail; } + // Used by CameraDevice to signal external camera disconnected + void disconnect(); + bool isClosed(); + + ndk::ScopedAStatus close() override; + ndk::ScopedAStatus configureStreams(const StreamConfiguration& in_requestedConfiguration, + std::vector* _aidl_return) override; + ndk::ScopedAStatus constructDefaultRequestSettings(RequestTemplate in_type, + CameraMetadata* _aidl_return) override; + ndk::ScopedAStatus flush() override; + ndk::ScopedAStatus getCaptureRequestMetadataQueue( + MQDescriptor* _aidl_return) override; + ndk::ScopedAStatus getCaptureResultMetadataQueue( + MQDescriptor* _aidl_return) override; + ndk::ScopedAStatus isReconfigurationRequired(const CameraMetadata& in_oldSessionParams, + const CameraMetadata& in_newSessionParams, + bool* _aidl_return) override; + ndk::ScopedAStatus processCaptureRequest(const std::vector& in_requests, + const std::vector& in_cachesToRemove, + int32_t* _aidl_return) override; + ndk::ScopedAStatus signalStreamFlush(const std::vector& in_streamIds, + int32_t in_streamConfigCounter) override; + ndk::ScopedAStatus switchToOffline( + const std::vector& in_streamsToKeep, + CameraOfflineSessionInfo* out_offlineSessionInfo, + std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus repeatingRequestEnd(int32_t in_frameNumber, + const std::vector& in_streamIds) override; + + protected: + // Helper methods + Status constructDefaultRequestSettingsRaw(int type, CameraMetadata* outMetadata); + + bool preProcessConfigurationLocked(const StreamConfiguration& requestedConfiguration, + camera3_stream_configuration_t* stream_list /*out*/, + std::vector* streams /*out*/); + void postProcessConfigurationLocked(const StreamConfiguration& requestedConfiguration); + void postProcessConfigurationFailureLocked(const StreamConfiguration& requestedConfiguration); + Status processOneCaptureRequest(const CaptureRequest& request); + + // Method to pop buffer's bufferId from mBufferIdMaps + // BUFFER_ID_NO_BUFFER is returned if no matching buffer is found + uint64_t popBufferId(const buffer_handle_t& buf, int streamId); + + // By default camera service uses frameNumber/streamId pair to retrieve the buffer that + // was sent to HAL. Override this implementation if HAL is using buffers from buffer management + // APIs to send output buffer. + virtual uint64_t getCapResultBufferId(const buffer_handle_t& buf, int streamId); + + status_t constructCaptureResult(CaptureResult& result, + const camera3_capture_result* hal_result); + + // Static helper method to copy/shrink capture result metadata sent by HAL + // Temporarily allocated metadata copy will be hold in mds + static void sShrinkCaptureResult( + camera3_capture_result* dst, const camera3_capture_result* src, + std::vector<::android::hardware::camera::common::helper::CameraMetadata>* mds, + std::vector* physCamMdArray, bool handlePhysCam); + static bool sShouldShrink(const camera_metadata_t* md); + static camera_metadata_t* sCreateCompactCopy(const camera_metadata_t* src); + + // protecting mClosed/mDisconnected/mInitFail + mutable Mutex mStateLock; + // device is closed either + // - closed by user + // - init failed + // - camera disconnected + bool mClosed = false; + + // Set by CameraDevice (when external camera is disconnected) + bool mDisconnected = false; + + struct AETriggerCancelOverride { + bool applyAeLock; + uint8_t aeLock; + bool applyAePrecaptureTrigger; + uint8_t aePrecaptureTrigger; + }; + + camera3_device_t* mDevice; + const uint32_t mDeviceVersion; + const bool mFreeBufEarly; + bool mIsAELockAvailable; + bool mDerivePostRawSensKey; + uint32_t mNumPartialResults; + // Stream ID -> Camera3Stream cache + std::map mStreamMap; + + std::map mPhysicalCameraIdMap; + // Physical camera ids for the logical multi-camera. Empty if this + // is not a logical multi-camera. + std::unordered_set mPhysicalCameraIds; + + Mutex mStreamConfigCounterLock; + uint32_t mStreamConfigCounter = 1; + + mutable Mutex mInflightLock; // protecting mInflightBuffers and mCirculatingBuffers + // (streamID, frameNumber) -> inflight buffer cache + std::map, camera3_stream_buffer_t> mInflightBuffers; + + // (frameNumber, AETriggerOverride) -> inflight request AETriggerOverrides + std::map mInflightAETriggerOverrides; + ::android::hardware::camera::common::helper::CameraMetadata mOverridenResult; + std::map mInflightRawBoostPresent; + ::android::hardware::camera::common::helper::CameraMetadata mOverridenRequest; + + static const uint64_t BUFFER_ID_NO_BUFFER = 0; + // buffers currently ciculating between HAL and camera service + // key: bufferId sent via HIDL interface + // value: imported buffer_handle_t + // Buffer will be imported during process_capture_request and will be freed + // when the its stream is deleted or camera device session is closed + typedef std::unordered_map CirculatingBuffers; + // Stream ID -> circulating buffers map + std::map mCirculatingBuffers; + + static HandleImporter sHandleImporter; + static buffer_handle_t sEmptyBuffer; + + bool mInitFail; + bool mFirstRequest = false; + + struct BufferHasher { + size_t operator()(const buffer_handle_t& buf) const { + if (buf == nullptr) return 0; + + size_t result = 1; + result = 31 * result + buf->numFds; + for (int i = 0; i < buf->numFds; i++) { + result = 31 * result + buf->data[i]; + } + return result; + } + }; + + struct BufferComparator { + bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const { + if (buf1->numFds == buf2->numFds) { + for (int i = 0; i < buf1->numFds; i++) { + if (buf1->data[i] != buf2->data[i]) { + return false; + } + } + return true; + } + return false; + } + }; + + std::mutex mBufferIdMapLock; // protecting mBufferIdMaps and mNextBufferId + typedef std::unordered_map + BufferIdMap; + // stream ID -> per stream buffer ID map for buffers coming from requestStreamBuffers API + // Entries are created during requestStreamBuffers when a stream first request a buffer, and + // deleted in returnStreamBuffers/processCaptureResult* when all buffers are returned + std::unordered_map mBufferIdMaps; + + ::android::hardware::camera::common::helper::CameraMetadata mDeviceInfo; + + using RequestMetadataQueue = AidlMessageQueue; + std::unique_ptr mRequestMetadataQueue; + using ResultMetadataQueue = AidlMessageQueue; + std::shared_ptr mResultMetadataQueue; + + class ResultBatcher { + public: + ResultBatcher(const std::shared_ptr& callback); + void setNumPartialResults(uint32_t n); + void setBatchedStreams(const std::vector& streamsToBatch); + void setResultMetadataQueue(std::shared_ptr q); + + void registerBatch(uint32_t frameNumber, uint32_t batchSize); + void notify(NotifyMsg& msg); + void processCaptureResult(CaptureResult& result); + + protected: + struct InflightBatch { + // Protect access to entire struct. Acquire this lock before read/write any data or + // calling any methods. processCaptureResult and notify will compete for this lock + // HIDL IPCs might be issued while the lock is held + Mutex mLock; + + bool allDelivered() const; + + uint32_t mFirstFrame; + uint32_t mLastFrame; + uint32_t mBatchSize; + + bool mShutterDelivered = false; + std::vector mShutterMsgs; + + struct BufferBatch { + BufferBatch(uint32_t batchSize) { mBuffers.reserve(batchSize); } + bool mDelivered = false; + // This currently assumes every batched request will output to the batched stream + // and since HAL must always send buffers in order, no frameNumber tracking is + // needed + std::vector mBuffers; + }; + // Stream ID -> VideoBatch + std::unordered_map mBatchBufs; + + struct MetadataBatch { + // (frameNumber, metadata) + std::vector> mMds; + }; + // Partial result IDs that has been delivered to framework + uint32_t mNumPartialResults; + uint32_t mPartialResultProgress = 0; + // partialResult -> MetadataBatch + std::map mResultMds; + + // Set to true when batch is removed from mInflightBatches + // processCaptureResult and notify must check this flag after acquiring mLock to make + // sure this batch isn't removed while waiting for mLock + bool mRemoved = false; + }; + + // Get the batch index and pointer to InflightBatch (nullptrt if the frame is not batched) + // Caller must acquire the InflightBatch::mLock before accessing the InflightBatch + // It's possible that the InflightBatch is removed from mInflightBatches before the + // InflightBatch::mLock is acquired (most likely caused by an error notification), so + // caller must check InflightBatch::mRemoved flag after the lock is acquried. + // This method will hold ResultBatcher::mLock briefly + std::pair> getBatch(uint32_t frameNumber); + + static const int NOT_BATCHED = -1; + + // move/push function avoids "hidl_handle& operator=(hidl_handle&)", which clones native + // handle + void moveStreamBuffer(StreamBuffer&& src, StreamBuffer& dst); + void pushStreamBuffer(StreamBuffer&& src, std::vector& dst); + + void sendBatchMetadataLocked(std::shared_ptr batch, + uint32_t lastPartialResultIdx); + + // Check if the first batch in mInflightBatches is ready to be removed, and remove it if so + // This method will hold ResultBatcher::mLock briefly + void checkAndRemoveFirstBatch(); + + // The following sendXXXX methods must be called while the InflightBatch::mLock is locked + // HIDL IPC methods will be called during these methods. + void sendBatchShutterCbsLocked(std::shared_ptr batch); + // send buffers for all batched streams + void sendBatchBuffersLocked(std::shared_ptr batch); + // send buffers for specified streams + void sendBatchBuffersLocked(std::shared_ptr batch, + const std::vector& streams); + // End of sendXXXX methods + + // helper methods + void freeReleaseFences(std::vector&); + void notifySingleMsg(NotifyMsg& msg); + void processOneCaptureResult(CaptureResult& result); + void invokeProcessCaptureResultCallback(std::vector& results, + bool tryWriteFmq); + + // Protect access to mInflightBatches, mNumPartialResults and mStreamsToBatch + // processCaptureRequest, processCaptureResult, notify will compete for this lock + // Do NOT issue HIDL IPCs while holding this lock (except when HAL reports error) + mutable Mutex mLock; + std::deque> mInflightBatches; + uint32_t mNumPartialResults; + std::vector mStreamsToBatch; + const std::shared_ptr mCallback; + std::shared_ptr mResultMetadataQueue; + + // Protect against invokeProcessCaptureResultCallback() + Mutex mProcessCaptureResultLock; + + } mResultBatcher; + + std::vector mVideoStreamIds; + + bool initialize(); + + static bool shouldFreeBufEarly(); + + Status initStatus() const; + + // Validate and import request's input buffer and acquire fence + virtual Status importRequest(const CaptureRequest& request, + std::vector& allBufPtrs, + std::vector& allFences); + + Status importRequestImpl(const CaptureRequest& request, + std::vector& allBufPtrs, std::vector& allFences, + // Optional argument for ICameraDeviceSession@3.5 impl + bool allowEmptyBuf = false); + + Status importBuffer(int32_t streamId, uint64_t bufId, buffer_handle_t buf, + /*out*/ buffer_handle_t** outBufPtr, bool allowEmptyBuf); + + static void cleanupInflightFences(std::vector& allFences, size_t numFences); + + void cleanupBuffersLocked(int id); + + void updateBufferCaches(const std::vector& cachesToRemove); + + bool handleAePrecaptureCancelRequestLocked( + const camera3_capture_request_t& halRequest, + android::hardware::camera::common::helper::CameraMetadata* settings /*out*/, + AETriggerCancelOverride* override /*out*/); + + void overrideResultForPrecaptureCancelLocked( + const AETriggerCancelOverride& aeTriggerCancelOverride, + ::android::hardware::camera::common::helper::CameraMetadata* settings /*out*/); + + /** + * Static callback forwarding methods from HAL to instance + */ + static callbacks_process_capture_result_t sProcessCaptureResult; + static callbacks_notify_t sNotify; +}; + +} // namespace implementation +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/device/convert.cpp b/aidl/camera/device/convert.cpp new file mode 100644 index 0000000..ae8eb87 --- /dev/null +++ b/aidl/camera/device/convert.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "android.hardware.camera.device-convert-impl" +#include + +#include "convert.h" + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace implementation { + +using ::aidl::android::hardware::camera::device::ErrorCode; +using ::aidl::android::hardware::camera::device::ErrorMsg; +using ::aidl::android::hardware::camera::device::ShutterMsg; +using ::aidl::android::hardware::graphics::common::BufferUsage; +using ::aidl::android::hardware::graphics::common::Dataspace; +using ::aidl::android::hardware::graphics::common::PixelFormat; + +void convertToAidl(const camera_metadata_t* src, CameraMetadata* dest) { + if (src == nullptr) { + return; + } + + size_t size = get_camera_metadata_size(src); + auto* src_start = (uint8_t*)src; + uint8_t* src_end = src_start + size; + dest->metadata.assign(src_start, src_end); +} + +bool convertFromAidl(const CameraMetadata& src, const camera_metadata_t** dst) { + const std::vector& metadata = src.metadata; + if (metadata.empty()) { + // Special case for null metadata + *dst = nullptr; + return true; + } + + const uint8_t* data = metadata.data(); + // check that the size of CameraMetadata match underlying camera_metadata_t + if (get_camera_metadata_size((camera_metadata_t*)data) != metadata.size()) { + ALOGE("%s: input CameraMetadata is corrupt!", __FUNCTION__); + return false; + } + *dst = (camera_metadata_t*)data; + return true; +} + +void convertToAidl(const Camera3Stream* src, HalStream* dst) { + dst->overrideDataSpace = (Dataspace)src->data_space; + dst->id = src->mId; + dst->overrideFormat = (PixelFormat)src->format; + dst->maxBuffers = src->max_buffers; + dst->physicalCameraId = src->physical_camera_id; + if (src->stream_type == CAMERA3_STREAM_OUTPUT) { + dst->consumerUsage = (BufferUsage)0; + dst->producerUsage = (BufferUsage)src->usage; + } else if (src->stream_type == CAMERA3_STREAM_INPUT) { + dst->producerUsage = (BufferUsage)0; + dst->consumerUsage = (BufferUsage)src->usage; + } else { + // Should not reach here per current AIDL spec, but we might end up adding + // bi-directional stream to AIDL. + ALOGW("%s: Stream type %d is not currently supported!", __FUNCTION__, src->stream_type); + } +} + +void convertToAidl(const camera3_stream_configuration_t& src, std::vector* dst) { + dst->resize(src.num_streams); + for (uint32_t i = 0; i < src.num_streams; i++) { + convertToAidl(static_cast(src.streams[i]), &dst->at(i)); + } +} + +void convertFromAidl(const Stream& src, Camera3Stream* dst) { + dst->mId = src.id; + dst->stream_type = (int)src.streamType; + dst->width = src.width; + dst->height = src.height; + dst->format = (int)src.format; + dst->data_space = (android_dataspace_t)src.dataSpace; + dst->rotation = (int)src.rotation; + dst->usage = (uint32_t)src.usage; + // Fields to be filled by HAL (max_buffers, priv) are initialized to 0 + dst->max_buffers = 0; + dst->priv = 0; + // Initialize physical_camera_id + dst->physical_camera_id = nullptr; +} + +void convertFromAidl(buffer_handle_t* bufPtr, BufferStatus status, camera3_stream_t* stream, + int acquireFence, camera3_stream_buffer_t* dst) { + dst->stream = stream; + dst->buffer = bufPtr; + dst->status = (int)status; + dst->acquire_fence = acquireFence; + dst->release_fence = -1; // meant for HAL to fill in +} + +void convertToAidl(const camera3_notify_msg* src, NotifyMsg* dst) { + switch (src->type) { + case CAMERA3_MSG_ERROR: { + ErrorMsg error; + // The camera3_stream_t* must be the same as what wrapper HAL passed to conventional + // HAL, or the ID lookup will return garbage. Caller should validate the ID here is + // indeed one of active stream IDs + Camera3Stream* stream = static_cast(src->message.error.error_stream); + error.frameNumber = src->message.error.frame_number; + error.errorStreamId = (stream != nullptr) ? stream->mId : -1; + error.errorCode = (ErrorCode)src->message.error.error_code; + dst->set(error); + } break; + case CAMERA3_MSG_SHUTTER: { + ShutterMsg shutter; + shutter.frameNumber = src->message.shutter.frame_number; + shutter.timestamp = src->message.shutter.timestamp; + dst->set(shutter); + } break; + default: + ALOGE("%s: HIDL type converion failed. Unknown msg type 0x%x", __FUNCTION__, src->type); + } + return; +} + +} // namespace implementation +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/device/convert.h b/aidl/camera/device/convert.h new file mode 100644 index 0000000..8ecc2ae --- /dev/null +++ b/aidl/camera/device/convert.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The LineageOS 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. + */ + +#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_CONVERT_H_ +#define HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_CONVERT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace camera { +namespace device { +namespace implementation { + +// The camera3_stream_t sent to conventional HAL. Added mId fields to enable stream ID lookup +// fromt a downcasted camera3_stream +struct Camera3Stream : public camera3_stream { + int mId; +}; + +using ::aidl::android::hardware::camera::common::Status; +using ::aidl::android::hardware::camera::device::BufferStatus; +using ::aidl::android::hardware::camera::device::CameraMetadata; +using ::aidl::android::hardware::camera::device::HalStream; +using ::aidl::android::hardware::camera::device::NotifyMsg; +using ::aidl::android::hardware::camera::device::Stream; + +void convertToAidl(const camera_metadata_t* src, CameraMetadata* dest); + +bool convertFromAidl(const CameraMetadata& src, const camera_metadata_t** dst); + +void convertToAidl(const Camera3Stream* src, HalStream* dst); +void convertToAidl(const camera3_stream_configuration_t& src, std::vector* dst); + +void convertFromAidl(const Stream& src, Camera3Stream* dst); + +void convertFromAidl(buffer_handle_t*, BufferStatus, camera3_stream_t*, int acquireFence, // inputs + camera3_stream_buffer_t* dst); + +void convertToAidl(const camera3_notify_msg* src, NotifyMsg* dst); + +inline aidl::android::hardware::common::NativeHandle makeToAidlIfNotNull( + const native_handle_t* nh) { + if (nh == nullptr) { + return aidl::android::hardware::common::NativeHandle(); + } + return makeToAidl(nh); +} + +inline ndk::ScopedAStatus fromStatus(Status status) { + return status == Status::OK + ? ndk::ScopedAStatus::ok() + : ndk::ScopedAStatus::fromServiceSpecificError(static_cast(status)); +} + +} // namespace implementation +} // namespace device +} // namespace camera +} // namespace hardware +} // namespace android + +#endif // HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_CONVERT_H_ diff --git a/aidl/camera/provider/Android.bp b/aidl/camera/provider/Android.bp new file mode 100644 index 0000000..87af078 --- /dev/null +++ b/aidl/camera/provider/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2024 The LineageOS Project +// +// SPDX-License-Identifier: Apache-2.0 +// + +cc_binary { + name: "android.hardware.camera.provider-service.samsung", + init_rc: ["android.hardware.camera.provider-service.samsung.rc"], + vintf_fragments: ["android.hardware.camera.provider-service.samsung.xml"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "service.cpp", + "CameraProvider.cpp", + ], + shared_libs: [ + "android.hardware.camera.common-V1-ndk", + "android.hardware.camera.device-V1-ndk", + "android.hardware.camera.provider-V1-ndk", + "camera.device-impl.samsung", + "libbinder_ndk", + "libcamera_metadata", + "libcutils", + "libfmq", + "libhardware", + "libhidlbase", + "liblog", + "libutils", + ], + static_libs: [ + "android.hardware.camera.common-helper", + "libaidlcommonsupport", + ], +} diff --git a/aidl/camera/provider/CameraProvider.cpp b/aidl/camera/provider/CameraProvider.cpp new file mode 100644 index 0000000..8487201 --- /dev/null +++ b/aidl/camera/provider/CameraProvider.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_TAG "CamPrvdr" + +#include "CameraProvider.h" + +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace camera { +namespace provider { +namespace implementation { + +using ::aidl::android::hardware::camera::common::CameraMetadataType; +using ::aidl::android::hardware::camera::common::Status; +using ::aidl::android::hardware::camera::common::TorchModeStatus; +using ::aidl::android::hardware::camera::common::VendorTag; +using ::android::hardware::camera::common::helper::VendorTagDescriptor; +using ::android::hardware::camera::device::implementation::CameraDevice; +using ::android::hardware::camera::device::implementation::fromStatus; + +namespace { +// "device@/internal/" +const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/internal/(.+)"); +const int kMaxCameraDeviceNameLen = 128; +const int kMaxCameraIdLen = 16; + +bool matchDeviceName(const std::string& deviceName, std::string* deviceVersion, + std::string* cameraId) { + std::string deviceNameStd(deviceName.c_str()); + std::smatch sm; + if (std::regex_match(deviceNameStd, sm, kDeviceNameRE)) { + if (deviceVersion != nullptr) { + *deviceVersion = sm[1]; + } + if (cameraId != nullptr) { + *cameraId = sm[2]; + } + return true; + } + return false; +} + +} // anonymous namespace + +void CameraProvider::addDeviceNames(int camera_id, CameraDeviceStatus status, bool cam_new) { + char cameraId[kMaxCameraIdLen]; + snprintf(cameraId, sizeof(cameraId), "%d", camera_id); + std::string cameraIdStr(cameraId); + + mCameraIds.add(cameraIdStr); + + // initialize mCameraDeviceNames + int deviceVersion = mModule->getDeviceVersion(camera_id); + auto deviceNamePair = std::make_pair(cameraIdStr, getAidlDeviceName(cameraIdStr)); + mCameraDeviceNames.add(deviceNamePair); + if (cam_new) { + mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, status); + } +} + +void CameraProvider::removeDeviceNames(int camera_id) { + std::string cameraIdStr = std::to_string(camera_id); + + mCameraIds.remove(cameraIdStr); + + int deviceVersion = mModule->getDeviceVersion(camera_id); + auto deviceNamePair = std::make_pair(cameraIdStr, getAidlDeviceName(cameraIdStr)); + mCameraDeviceNames.remove(deviceNamePair); + mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, CameraDeviceStatus::NOT_PRESENT); + mModule->removeCamera(camera_id); +} + +/** + * static callback forwarding methods from HAL to instance + */ +void CameraProvider::sCameraDeviceStatusChange(const struct camera_module_callbacks* callbacks, + int camera_id, int new_status) { + CameraProvider* cp = const_cast(static_cast(callbacks)); + if (cp == nullptr) { + ALOGE("%s: callback ops is null", __FUNCTION__); + return; + } + + Mutex::Autolock _l(cp->mCbLock); + char cameraId[kMaxCameraIdLen]; + snprintf(cameraId, sizeof(cameraId), "%d", camera_id); + std::string cameraIdStr(cameraId); + cp->mCameraStatusMap[cameraIdStr] = (camera_device_status_t)new_status; + + if (cp->mCallbacks == nullptr) { + // For camera connected before mCallbacks is set, the corresponding + // addDeviceNames() would be called later in setCallbacks(). + return; + } + + bool found = false; + CameraDeviceStatus status = (CameraDeviceStatus)new_status; + for (auto const& deviceNamePair : cp->mCameraDeviceNames) { + if (cameraIdStr.compare(deviceNamePair.first) == 0) { + cp->mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, status); + found = true; + } + } + + switch (status) { + case CameraDeviceStatus::PRESENT: + case CameraDeviceStatus::ENUMERATING: + if (!found) { + cp->addDeviceNames(camera_id, status, true); + } + break; + case CameraDeviceStatus::NOT_PRESENT: + if (found) { + cp->removeDeviceNames(camera_id); + } + } +} + +void CameraProvider::sTorchModeStatusChange(const struct camera_module_callbacks* callbacks, + const char* camera_id, int new_status) { + CameraProvider* cp = const_cast(static_cast(callbacks)); + + if (cp == nullptr) { + ALOGE("%s: callback ops is null", __FUNCTION__); + return; + } + + Mutex::Autolock _l(cp->mCbLock); + if (cp->mCallbacks != nullptr) { + std::string cameraIdStr(camera_id); + TorchModeStatus status = (TorchModeStatus)new_status; + for (auto const& deviceNamePair : cp->mCameraDeviceNames) { + if (cameraIdStr.compare(deviceNamePair.first) == 0) { + cp->mCallbacks->torchModeStatusChange(deviceNamePair.second, status); + } + } + } +} + +std::string CameraProvider::getAidlDeviceName(std::string cameraId) { + return "device@" + CameraDevice::kDeviceVersion + "/internal/" + cameraId; +} + +CameraProvider::CameraProvider() + : camera_module_callbacks_t({sCameraDeviceStatusChange, sTorchModeStatusChange}) { + mInitFailed = initialize(); +} + +CameraProvider::~CameraProvider() {} + +bool CameraProvider::initialize() { + camera_module_t* rawModule; + int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID, (const hw_module_t**)&rawModule); + if (err < 0) { + ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err)); + return true; + } + + mModule = new CameraModule(rawModule); + err = mModule->init(); + if (err != OK) { + ALOGE("Could not initialize camera HAL module: %d (%s)", err, strerror(-err)); + mModule.clear(); + return true; + } + ALOGI("Loaded \"%s\" camera module", mModule->getModuleName()); + + // Setup vendor tags here so HAL can setup vendor keys in camera characteristics + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + if (!setUpVendorTags()) { + ALOGE("%s: Vendor tag setup failed, will not be available.", __FUNCTION__); + } + + // Setup callback now because we are going to try openLegacy next + err = mModule->setCallbacks(this); + if (err != OK) { + ALOGE("Could not set camera module callback: %d (%s)", err, strerror(-err)); + mModule.clear(); + return true; + } + + mNumberOfLegacyCameras = mModule->getNumberOfCameras(); + for (int i = 0; i < mNumberOfLegacyCameras; i++) { + struct camera_info info; + auto rc = mModule->getCameraInfo(i, &info); + if (rc != NO_ERROR) { + ALOGE("%s: Camera info query failed!", __func__); + mModule.clear(); + return true; + } + + if (checkCameraVersion(i, info) != OK) { + ALOGE("%s: Camera version check failed!", __func__); + mModule.clear(); + return true; + } + + char cameraId[kMaxCameraIdLen]; + snprintf(cameraId, sizeof(cameraId), "%d", i); + std::string cameraIdStr(cameraId); + mCameraStatusMap[cameraIdStr] = CAMERA_DEVICE_STATUS_PRESENT; + + addDeviceNames(i); + } + + return false; // mInitFailed +} + +/** + * Check that the device HAL version is still in supported. + */ +int CameraProvider::checkCameraVersion(int id, camera_info info) { + if (mModule == nullptr) { + return NO_INIT; + } + + // device_version undefined in CAMERA_MODULE_API_VERSION_1_0, + // All CAMERA_MODULE_API_VERSION_1_0 devices are backward-compatible + uint16_t moduleVersion = mModule->getModuleApiVersion(); + if (moduleVersion >= CAMERA_MODULE_API_VERSION_2_0) { + // Verify the device version is in the supported range + switch (info.device_version) { + case CAMERA_DEVICE_API_VERSION_1_0: + case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: + case CAMERA_DEVICE_API_VERSION_3_4: + case CAMERA_DEVICE_API_VERSION_3_5: + // in support + break; + case CAMERA_DEVICE_API_VERSION_3_6: + /** + * ICameraDevice@3.5 contains APIs from both + * CAMERA_DEVICE_API_VERSION_3_6 and CAMERA_MODULE_API_VERSION_2_5 + * so we require HALs to uprev both for simplified supported combinations. + * HAL can still opt in individual new APIs indepedently. + */ + if (moduleVersion < CAMERA_MODULE_API_VERSION_2_5) { + ALOGE("%s: Device %d has unsupported version combination:" + "HAL version %x and module version %x", + __FUNCTION__, id, info.device_version, moduleVersion); + return NO_INIT; + } + break; + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + // no longer supported + default: + ALOGE("%s: Device %d has HAL version %x, which is not supported", __FUNCTION__, id, + info.device_version); + return NO_INIT; + } + } + + return OK; +} + +bool CameraProvider::setUpVendorTags() { + ATRACE_CALL(); + vendor_tag_ops_t vOps = vendor_tag_ops_t(); + + // Check if vendor operations have been implemented + if (!mModule->isVendorTagDefined()) { + ALOGI("%s: No vendor tags defined for this device.", __FUNCTION__); + return true; + } + + mModule->getVendorTagOps(&vOps); + + // Ensure all vendor operations are present + if (vOps.get_tag_count == nullptr || vOps.get_all_tags == nullptr || + vOps.get_section_name == nullptr || vOps.get_tag_name == nullptr || + vOps.get_tag_type == nullptr) { + ALOGE("%s: Vendor tag operations not fully defined. Ignoring definitions.", __FUNCTION__); + return false; + } + + // Read all vendor tag definitions into a descriptor + sp desc; + status_t res; + if ((res = VendorTagDescriptor::createDescriptorFromOps(&vOps, /*out*/ desc)) != OK) { + ALOGE("%s: Could not generate descriptor from vendor tag operations," + "received error %s (%d). Camera clients will not be able to use" + "vendor tags", + __FUNCTION__, strerror(res), res); + return false; + } + + // Set the global descriptor to use with camera metadata + VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); + const SortedVector* sectionNames = desc->getAllSectionNames(); + size_t numSections = sectionNames->size(); + std::vector> tagsBySection(numSections); + int tagCount = desc->getTagCount(); + std::vector tags(tagCount); + desc->getTagArray(tags.data()); + for (int i = 0; i < tagCount; i++) { + VendorTag vt; + vt.tagId = tags[i]; + vt.tagName = desc->getTagName(tags[i]); + vt.tagType = (CameraMetadataType)desc->getTagType(tags[i]); + ssize_t sectionIdx = desc->getSectionIndex(tags[i]); + tagsBySection[sectionIdx].push_back(vt); + } + mVendorTagSections.resize(numSections); + for (size_t s = 0; s < numSections; s++) { + mVendorTagSections[s].sectionName = (*sectionNames)[s].c_str(); + mVendorTagSections[s].tags = tagsBySection[s]; + } + return true; +} + +ndk::ScopedAStatus CameraProvider::setCallback( + const std::shared_ptr& callback) { + Mutex::Autolock _l(mCbLock); + mCallbacks = callback; + if (callback == nullptr) { + return fromStatus(Status::OK); + } + // Add and report all presenting external cameras. + for (auto const& statusPair : mCameraStatusMap) { + int id = std::stoi(statusPair.first); + auto status = static_cast(statusPair.second); + if (id >= mNumberOfLegacyCameras && status != CameraDeviceStatus::NOT_PRESENT) { + addDeviceNames(id, status, true); + } + } + + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraProvider::getVendorTags(std::vector* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + _aidl_return->insert(_aidl_return->end(), mVendorTagSections.begin(), mVendorTagSections.end()); + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraProvider::getCameraIdList(std::vector* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + for (auto const& deviceNamePair : mCameraDeviceNames) { + if (std::stoi(deviceNamePair.first) >= mNumberOfLegacyCameras) { + // External camera devices must be reported through the device status change callback, + // not in this list. + continue; + } + if (mCameraStatusMap[deviceNamePair.first] == CAMERA_DEVICE_STATUS_PRESENT) { + _aidl_return->push_back(deviceNamePair.second); + } + } + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraProvider::getCameraDeviceInterface( + const std::string& in_cameraDeviceName, std::shared_ptr* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + std::string cameraId, deviceVersion; + bool match = matchDeviceName(in_cameraDeviceName, &deviceVersion, &cameraId); + + if (!match) { + *_aidl_return = nullptr; + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + + ssize_t index = mCameraDeviceNames.indexOf(std::make_pair(cameraId, in_cameraDeviceName)); + if (index == NAME_NOT_FOUND) { // Either an illegal name or a device version mismatch + Status status = Status::OK; + ssize_t idx = mCameraIds.indexOf(cameraId); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: cannot find camera %s!", __FUNCTION__, cameraId.c_str()); + status = Status::ILLEGAL_ARGUMENT; + } else { // invalid version + ALOGE("%s: camera device %s does not support version %s!", __FUNCTION__, + cameraId.c_str(), deviceVersion.c_str()); + status = Status::OPERATION_NOT_SUPPORTED; + } + return fromStatus(status); + } + + ALOGV("Constructing camera device"); + std::shared_ptr deviceImpl = + ndk::SharedRefBase::make(mModule, cameraId, mCameraDeviceNames); + if (deviceImpl == nullptr || deviceImpl->isInitFailed()) { + ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraId.c_str()); + *_aidl_return = nullptr; + return fromStatus(Status::INTERNAL_ERROR); + } + + IF_ALOGV() { + int interfaceVersion; + deviceImpl->getInterfaceVersion(&interfaceVersion); + ALOGV("%s: device interface version: %d", __FUNCTION__, interfaceVersion); + } + + *_aidl_return = deviceImpl; + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraProvider::notifyDeviceStateChange(int64_t newState) { + ALOGD("%s: New device state: 0x%" PRIx64, __FUNCTION__, newState); + uint64_t state = static_cast(newState); + mModule->notifyDeviceStateChange(state); + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraProvider::getConcurrentCameraIds( + std::vector* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + *_aidl_return = {}; + return fromStatus(Status::OK); +} + +ndk::ScopedAStatus CameraProvider::isConcurrentStreamCombinationSupported( + const std::vector&, bool* _aidl_return) { + if (_aidl_return == nullptr) { + return fromStatus(Status::ILLEGAL_ARGUMENT); + } + // No concurrent stream combinations are supported + *_aidl_return = false; + return fromStatus(Status::OK); +} + +} // namespace implementation +} // namespace provider +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/provider/CameraProvider.h b/aidl/camera/provider/CameraProvider.h new file mode 100644 index 0000000..cedc2df --- /dev/null +++ b/aidl/camera/provider/CameraProvider.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 The LineageOS Project + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace camera { +namespace provider { +namespace implementation { + +using ::aidl::android::hardware::camera::common::CameraDeviceStatus; +using ::aidl::android::hardware::camera::common::VendorTagSection; +using ::aidl::android::hardware::camera::device::ICameraDevice; +using ::aidl::android::hardware::camera::provider::BnCameraProvider; +using ::aidl::android::hardware::camera::provider::CameraIdAndStreamCombination; +using ::aidl::android::hardware::camera::provider::ConcurrentCameraIdCombination; +using ::aidl::android::hardware::camera::provider::ICameraProviderCallback; +using ::android::hardware::camera::common::helper::CameraModule; + +class CameraProvider : public BnCameraProvider, protected camera_module_callbacks_t { + public: + CameraProvider(); + ~CameraProvider() override; + + // Caller must use this method to check if CameraProvider ctor failed + bool isInitFailed() { return mInitFailed; } + + ndk::ScopedAStatus setCallback( + const std::shared_ptr& in_callback) override; + ndk::ScopedAStatus getVendorTags(std::vector* _aidl_return) override; + ndk::ScopedAStatus getCameraIdList(std::vector* _aidl_return) override; + ndk::ScopedAStatus getCameraDeviceInterface( + const std::string& in_cameraDeviceName, + std::shared_ptr* _aidl_return) override; + ndk::ScopedAStatus notifyDeviceStateChange(int64_t in_deviceState) override; + ndk::ScopedAStatus getConcurrentCameraIds( + std::vector* _aidl_return) override; + ndk::ScopedAStatus isConcurrentStreamCombinationSupported( + const std::vector& in_configs, + bool* _aidl_return) override; + + protected: + Mutex mCbLock; + std::shared_ptr mCallbacks = nullptr; + + sp mModule; + + int mNumberOfLegacyCameras; + std::map mCameraStatusMap; // camera id -> status + SortedVector mCameraIds; // the "0"/"1" legacy camera Ids + // (cameraId string, hidl device name) pairs + SortedVector> mCameraDeviceNames; + + // Must be queried before using any APIs. + // APIs will only work when this returns true + bool mInitFailed; + bool initialize(); + + std::vector mVendorTagSections; + bool setUpVendorTags(); + int checkCameraVersion(int id, camera_info info); + + // create AIDL device name from camera ID + std::string getAidlDeviceName(std::string cameraId); + + // static callback forwarding methods + static void sCameraDeviceStatusChange(const struct camera_module_callbacks* callbacks, + int camera_id, int new_status); + static void sTorchModeStatusChange(const struct camera_module_callbacks* callbacks, + const char* camera_id, int new_status); + + void addDeviceNames(int camera_id, CameraDeviceStatus status = CameraDeviceStatus::PRESENT, + bool cam_new = false); + void removeDeviceNames(int camera_id); +}; + +} // namespace implementation +} // namespace provider +} // namespace camera +} // namespace hardware +} // namespace android diff --git a/aidl/camera/provider/android.hardware.camera.provider-service.samsung.rc b/aidl/camera/provider/android.hardware.camera.provider-service.samsung.rc new file mode 100644 index 0000000..c7b2a3e --- /dev/null +++ b/aidl/camera/provider/android.hardware.camera.provider-service.samsung.rc @@ -0,0 +1,8 @@ +service vendor.camera.provider /vendor/bin/hw/android.hardware.camera.provider-service.samsung + interface aidl android.hardware.camera.provider.ICameraProvider/internal/0 + class hal + user cameraserver + group audio camera input drmrpc usb + ioprio rt 4 + capabilities SYS_NICE + task_profiles CameraServiceCapacity MaxPerformance diff --git a/aidl/camera/provider/android.hardware.camera.provider-service.samsung.xml b/aidl/camera/provider/android.hardware.camera.provider-service.samsung.xml new file mode 100644 index 0000000..acd7f19 --- /dev/null +++ b/aidl/camera/provider/android.hardware.camera.provider-service.samsung.xml @@ -0,0 +1,6 @@ + + + android.hardware.camera.provider + ICameraProvider/internal/0 + + diff --git a/aidl/camera/provider/service.cpp b/aidl/camera/provider/service.cpp new file mode 100644 index 0000000..37fbb44 --- /dev/null +++ b/aidl/camera/provider/service.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "android.hardware.camera.provider-service.samsung" + +#include "CameraProvider.h" + +#include +#include +#include +#include + +using ::android::hardware::camera::provider::implementation::CameraProvider; + +namespace { +// Default recommended RPC thread count for camera provider implementations +const int HWBINDER_THREAD_COUNT = 6; +} // namespace + +int main() { + ALOGI("CameraProvider: samsung service is starting."); + + ABinderProcess_setThreadPoolMaxThreadCount(HWBINDER_THREAD_COUNT); + + std::shared_ptr defaultProvider = ndk::SharedRefBase::make(); + const std::string serviceName = std::string(CameraProvider::descriptor) + "/internal/0"; + + binder_exception_t ret = + AServiceManager_addService(defaultProvider->asBinder().get(), serviceName.c_str()); + LOG_ALWAYS_FATAL_IF(ret != EX_NONE, "Error while registering camera provider service: %d", + ret); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} -- 2.20.1