aidl: Implement camera device/provider HALs
authorTim Zimmermann <tim@linux4.de>
Sun, 26 May 2024 07:53:57 +0000 (09:53 +0200)
committerTim Zimmermann <tim@linux4.de>
Fri, 21 Jun 2024 16:04:37 +0000 (18:04 +0200)
* Based on HIDL implementation from hardware/interfaces @ android-14.0.0_r37

Change-Id: I369434f8e1f877cac88c9f06d048e4c695ac5599

13 files changed:
aidl/camera/device/Android.bp [new file with mode: 0644]
aidl/camera/device/CameraDevice.cpp [new file with mode: 0644]
aidl/camera/device/CameraDevice.h [new file with mode: 0644]
aidl/camera/device/CameraDeviceSession.cpp [new file with mode: 0644]
aidl/camera/device/CameraDeviceSession.h [new file with mode: 0644]
aidl/camera/device/convert.cpp [new file with mode: 0644]
aidl/camera/device/convert.h [new file with mode: 0644]
aidl/camera/provider/Android.bp [new file with mode: 0644]
aidl/camera/provider/CameraProvider.cpp [new file with mode: 0644]
aidl/camera/provider/CameraProvider.h [new file with mode: 0644]
aidl/camera/provider/android.hardware.camera.provider-service.samsung.rc [new file with mode: 0644]
aidl/camera/provider/android.hardware.camera.provider-service.samsung.xml [new file with mode: 0644]
aidl/camera/provider/service.cpp [new file with mode: 0644]

diff --git a/aidl/camera/device/Android.bp b/aidl/camera/device/Android.bp
new file mode 100644 (file)
index 0000000..9ff0edf
--- /dev/null
@@ -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 (file)
index 0000000..96d7eed
--- /dev/null
@@ -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 <cutils/trace.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+std::string CameraDevice::kDeviceVersion = "1.1";
+
+CameraDevice::CameraDevice(
+        sp<CameraModule> module, const std::string& cameraId,
+        const SortedVector<std::pair<std::string, std::string>>& 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<CameraDeviceSession> 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<std::string> 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<uint32_t>(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<int>(it.streamType);
+            streamBuffer[i].width = it.width;
+            streamBuffer[i].height = it.height;
+            streamBuffer[i].format = static_cast<int>(it.format);
+            streamBuffer[i].data_space = static_cast<android_dataspace_t>(it.dataSpace);
+            streamBuffer[i].usage = static_cast<uint32_t>(it.usage);
+            streamBuffer[i].physical_camera_id = it.physicalCameraId.c_str();
+            streamBuffer[i++].rotation = static_cast<int>(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<ICameraDeviceCallback>& in_callback,
+                                      std::shared_ptr<ICameraDeviceSession>* _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<CameraDeviceSession> 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<hw_device_t**>(&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<ICameraDeviceCallback>&,
+                                                      std::shared_ptr<ICameraInjectionSession>*) {
+    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<CameraDeviceSession> CameraDevice::createSession(
+        camera3_device_t* device, const camera_metadata_t* deviceInfo,
+        const std::shared_ptr<ICameraDeviceCallback>& callback) {
+    return ndk::SharedRefBase::make<CameraDeviceSession>(device, deviceInfo, callback);
+}
+
+binder_status_t CameraDevice::dump(int fd, const char** args, uint32_t numArgs) {
+    std::shared_ptr<CameraDeviceSession> 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 (file)
index 0000000..a41c496
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "CameraDeviceSession.h"
+
+#include <CameraModule.h>
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/BnCameraDevice.h>
+
+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<CameraModule> module, const std::string& cameraId,
+                 const SortedVector<std::pair<std::string, std::string>>& 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<ICameraDeviceCallback>& in_callback,
+                            std::shared_ptr<ICameraDeviceSession>* _aidl_return) override;
+    ndk::ScopedAStatus openInjectionSession(
+            const std::shared_ptr<ICameraDeviceCallback>& in_callback,
+            std::shared_ptr<ICameraInjectionSession>* _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 <major>.<minor>
+    static std::string kDeviceVersion;
+
+  protected:
+    virtual std::shared_ptr<CameraDeviceSession> createSession(
+            camera3_device_t*, const camera_metadata_t* deviceInfo,
+            const std::shared_ptr<ICameraDeviceCallback>&);
+
+    const sp<CameraModule> 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<CameraDeviceSession> mSession{};
+
+    const SortedVector<std::pair<std::string, std::string>>& 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 (file)
index 0000000..df79f28
--- /dev/null
@@ -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 <CameraModule.h>
+#include <aidl/android/hardware/camera/device/ErrorMsg.h>
+#include <aidl/android/hardware/camera/device/ShutterMsg.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+
+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<ICameraDeviceCallback>& 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<RequestMetadataQueue>(static_cast<size_t>(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<RequestMetadataQueue>(static_cast<size_t>(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<int32_t>& /*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<int32_t>& 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<camera3_stream_t*> 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<buffer_handle_t*>& allBufPtrs,
+                                          std::vector<int>& allFences) {
+    return importRequestImpl(request, allBufPtrs, allFences, /*allowEmptyBuf*/ true);
+}
+
+Status CameraDeviceSession::importRequestImpl(const CaptureRequest& request,
+                                              std::vector<buffer_handle_t*>& allBufPtrs,
+                                              std::vector<int>& 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<buffer_handle_t> allBufs;
+    std::vector<uint64_t> allBufIds;
+    allBufs.resize(numBufs);
+    allBufIds.resize(numBufs);
+    allBufPtrs.resize(numBufs);
+    allFences.resize(numBufs);
+    std::vector<int32_t> 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<int>& allFences, size_t numFences) {
+    for (size_t j = 0; j < numFences; j++) {
+        sHandleImporter.closeFence(allFences[j]);
+    }
+}
+
+CameraDeviceSession::ResultBatcher::ResultBatcher(
+        const std::shared_ptr<ICameraDeviceCallback>& 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<int>& streamsToBatch) {
+    Mutex::Autolock _l(mLock);
+    mStreamsToBatch = streamsToBatch;
+}
+
+void CameraDeviceSession::ResultBatcher::setResultMetadataQueue(
+        std::shared_ptr<ResultMetadataQueue> q) {
+    Mutex::Autolock _l(mLock);
+    mResultMetadataQueue = q;
+}
+
+void CameraDeviceSession::ResultBatcher::registerBatch(uint32_t frameNumber, uint32_t batchSize) {
+    auto batch = std::make_shared<InflightBatch>();
+    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<int, std::shared_ptr<CameraDeviceSession::ResultBatcher::InflightBatch>>
+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<InflightBatch> 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<InflightBatch> 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<CaptureResult>& results) {
+    for (auto& result : results) {
+        if (::android::makeFromAidl(result.inputBuffer.releaseFence) != nullptr) {
+            native_handle_t* handle = const_cast<native_handle_t*>(
+                    ::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<native_handle_t*>(::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<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.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<InflightBatch> batch) {
+    sendBatchBuffersLocked(batch, mStreamsToBatch);
+}
+
+void CameraDeviceSession::ResultBatcher::sendBatchBuffersLocked(
+        std::shared_ptr<InflightBatch> batch, const std::vector<int>& 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<CaptureResult> 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<StreamBuffer> 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<InflightBatch> 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<CaptureResult> results;
+    std::vector<uint32_t> 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<NotifyMsg::Tag::shutter>();
+        frameNumber = shutter.frameNumber;
+    } else {
+        ErrorMsg error = msg.get<NotifyMsg::Tag::error>();
+        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<InflightBatch> 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<InflightBatch> 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<CaptureResult>& 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<int8_t*>(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<int8_t*>(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<CaptureResult> 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<InflightBatch> 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<int> filledStreams;
+        std::vector<StreamBuffer> 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<HalStream>* _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, &paramBuffer);
+    }
+
+    camera3_stream_configuration_t stream_list{};
+    // Block reading mStreamConfigCounter until configureStream returns
+    Mutex::Autolock _sccl(mStreamConfigCounterLock);
+    mStreamConfigCounter = in_requestedConfiguration.streamConfigCounter;
+    std::vector<camera3_stream_t*> 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<BufferCache>& 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<camera3_stream_t*>* 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<int8_t, SynchronizedReadWrite>* _aidl_return) {
+    *_aidl_return = mRequestMetadataQueue->dupeDesc();
+    return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus CameraDeviceSession::getCaptureResultMetadataQueue(
+        MQDescriptor<int8_t, SynchronizedReadWrite>* _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<CaptureRequest>& in_requests,
+        const std::vector<BufferCache>& 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<int32_t>(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<int8_t*>(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<buffer_handle_t*> allBufPtrs;
+    std::vector<int> 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<camera3_stream_buffer_t> 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<const char*> physicalCameraIds;
+    std::vector<const camera_metadata_t*> physicalCameraSettings;
+    std::vector<CameraMetadata> 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<int8_t*>(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<std::mutex> 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<Camera3Stream*>(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<Camera3Stream*>(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<Camera3Stream*>(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<Camera3Stream*>(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<Camera3Stream*>(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<Camera3Stream*>(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<const camera_metadata_t*>* 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<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(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<const camera_metadata_t*> 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<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+    NotifyMsg aidlMsg;
+    convertToAidl(msg, &aidlMsg);
+
+    if (aidlMsg.getTag() == NotifyMsg::Tag::error) {
+        ErrorMsg error = aidlMsg.get<NotifyMsg::Tag::error>();
+
+        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<int32_t>& /*in_streamsToKeep*/,
+        CameraOfflineSessionInfo* /*out_offlineSessionInfo*/,
+        std::shared_ptr<ICameraOfflineSession>* /*_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 (file)
index 0000000..7bb38dc
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "convert.h"
+
+#include <CameraMetadata.h>
+#include <HandleImporter.h>
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/BnCameraDeviceSession.h>
+#include <aidl/android/hardware/camera/device/CaptureResult.h>
+#include <aidl/android/hardware/camera/device/ICameraOfflineSession.h>
+#include <aidl/android/hardware/camera/device/NotifyMsg.h>
+#include <aidl/android/hardware/camera/device/StreamBuffer.h>
+#include <fmq/AidlMessageQueue.h>
+#include <hardware/camera3.h>
+#include <utils/Mutex.h>
+
+#include <deque>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+
+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<ICameraDeviceCallback>&);
+    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<HalStream>* _aidl_return) override;
+    ndk::ScopedAStatus constructDefaultRequestSettings(RequestTemplate in_type,
+                                                       CameraMetadata* _aidl_return) override;
+    ndk::ScopedAStatus flush() override;
+    ndk::ScopedAStatus getCaptureRequestMetadataQueue(
+            MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) override;
+    ndk::ScopedAStatus getCaptureResultMetadataQueue(
+            MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) override;
+    ndk::ScopedAStatus isReconfigurationRequired(const CameraMetadata& in_oldSessionParams,
+                                                 const CameraMetadata& in_newSessionParams,
+                                                 bool* _aidl_return) override;
+    ndk::ScopedAStatus processCaptureRequest(const std::vector<CaptureRequest>& in_requests,
+                                             const std::vector<BufferCache>& in_cachesToRemove,
+                                             int32_t* _aidl_return) override;
+    ndk::ScopedAStatus signalStreamFlush(const std::vector<int32_t>& in_streamIds,
+                                         int32_t in_streamConfigCounter) override;
+    ndk::ScopedAStatus switchToOffline(
+            const std::vector<int32_t>& in_streamsToKeep,
+            CameraOfflineSessionInfo* out_offlineSessionInfo,
+            std::shared_ptr<ICameraOfflineSession>* _aidl_return) override;
+    ndk::ScopedAStatus repeatingRequestEnd(int32_t in_frameNumber,
+                                           const std::vector<int32_t>& 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<camera3_stream_t*>* 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<const camera_metadata_t*>* 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<int, Camera3Stream> mStreamMap;
+
+    std::map<int, std::string> mPhysicalCameraIdMap;
+    // Physical camera ids for the logical multi-camera. Empty if this
+    // is not a logical multi-camera.
+    std::unordered_set<std::string> mPhysicalCameraIds;
+
+    Mutex mStreamConfigCounterLock;
+    uint32_t mStreamConfigCounter = 1;
+
+    mutable Mutex mInflightLock;  // protecting mInflightBuffers and mCirculatingBuffers
+    // (streamID, frameNumber) -> inflight buffer cache
+    std::map<std::pair<int, uint32_t>, camera3_stream_buffer_t> mInflightBuffers;
+
+    // (frameNumber, AETriggerOverride) -> inflight request AETriggerOverrides
+    std::map<uint32_t, AETriggerCancelOverride> mInflightAETriggerOverrides;
+    ::android::hardware::camera::common::helper::CameraMetadata mOverridenResult;
+    std::map<uint32_t, bool> 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<uint64_t, buffer_handle_t> CirculatingBuffers;
+    // Stream ID -> circulating buffers map
+    std::map<int, CirculatingBuffers> 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<const buffer_handle_t, uint64_t, BufferHasher, BufferComparator>
+            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<int, BufferIdMap> mBufferIdMaps;
+
+    ::android::hardware::camera::common::helper::CameraMetadata mDeviceInfo;
+
+    using RequestMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+    std::unique_ptr<RequestMetadataQueue> mRequestMetadataQueue;
+    using ResultMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+    std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+    class ResultBatcher {
+      public:
+        ResultBatcher(const std::shared_ptr<ICameraDeviceCallback>& callback);
+        void setNumPartialResults(uint32_t n);
+        void setBatchedStreams(const std::vector<int>& streamsToBatch);
+        void setResultMetadataQueue(std::shared_ptr<ResultMetadataQueue> 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<NotifyMsg> 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<StreamBuffer> mBuffers;
+            };
+            // Stream ID -> VideoBatch
+            std::unordered_map<int, BufferBatch> mBatchBufs;
+
+            struct MetadataBatch {
+                //                   (frameNumber, metadata)
+                std::vector<std::pair<uint32_t, CameraMetadata>> mMds;
+            };
+            // Partial result IDs that has been delivered to framework
+            uint32_t mNumPartialResults;
+            uint32_t mPartialResultProgress = 0;
+            // partialResult -> MetadataBatch
+            std::map<uint32_t, MetadataBatch> 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<int, std::shared_ptr<InflightBatch>> 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<StreamBuffer>& dst);
+
+        void sendBatchMetadataLocked(std::shared_ptr<InflightBatch> 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<InflightBatch> batch);
+        // send buffers for all batched streams
+        void sendBatchBuffersLocked(std::shared_ptr<InflightBatch> batch);
+        // send buffers for specified streams
+        void sendBatchBuffersLocked(std::shared_ptr<InflightBatch> batch,
+                                    const std::vector<int>& streams);
+        // End of sendXXXX methods
+
+        // helper methods
+        void freeReleaseFences(std::vector<CaptureResult>&);
+        void notifySingleMsg(NotifyMsg& msg);
+        void processOneCaptureResult(CaptureResult& result);
+        void invokeProcessCaptureResultCallback(std::vector<CaptureResult>& 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<std::shared_ptr<InflightBatch>> mInflightBatches;
+        uint32_t mNumPartialResults;
+        std::vector<int> mStreamsToBatch;
+        const std::shared_ptr<ICameraDeviceCallback> mCallback;
+        std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+        // Protect against invokeProcessCaptureResultCallback()
+        Mutex mProcessCaptureResultLock;
+
+    } mResultBatcher;
+
+    std::vector<int> 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<buffer_handle_t*>& allBufPtrs,
+                                 std::vector<int>& allFences);
+
+    Status importRequestImpl(const CaptureRequest& request,
+                             std::vector<buffer_handle_t*>& allBufPtrs, std::vector<int>& 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<int>& allFences, size_t numFences);
+
+    void cleanupBuffersLocked(int id);
+
+    void updateBufferCaches(const std::vector<BufferCache>& 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 (file)
index 0000000..ae8eb87
--- /dev/null
@@ -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 <log/log.h>
+
+#include "convert.h"
+
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <hardware/camera_common.h>
+
+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<uint8_t>& 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<HalStream>* dst) {
+    dst->resize(src.num_streams);
+    for (uint32_t i = 0; i < src.num_streams; i++) {
+        convertToAidl(static_cast<Camera3Stream*>(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<Camera3Stream*>(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<NotifyMsg::Tag::error>(error);
+        } break;
+        case CAMERA3_MSG_SHUTTER: {
+            ShutterMsg shutter;
+            shutter.frameNumber = src->message.shutter.frame_number;
+            shutter.timestamp = src->message.shutter.timestamp;
+            dst->set<NotifyMsg::Tag::shutter>(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 (file)
index 0000000..8ecc2ae
--- /dev/null
@@ -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 <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/BufferStatus.h>
+#include <aidl/android/hardware/camera/device/CameraMetadata.h>
+#include <aidl/android/hardware/camera/device/HalStream.h>
+#include <aidl/android/hardware/camera/device/NotifyMsg.h>
+#include <aidl/android/hardware/camera/device/Stream.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <hardware/camera3.h>
+#include <system/camera_metadata.h>
+
+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<HalStream>* 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<int32_t>(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 (file)
index 0000000..87af078
--- /dev/null
@@ -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 (file)
index 0000000..8487201
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define LOG_TAG "CamPrvdr"
+
+#include "CameraProvider.h"
+
+#include <CameraDevice.h>
+#include <convert.h>
+#include <utils/Trace.h>
+
+#include <regex>
+
+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@<version>/internal/<id>"
+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<CameraProvider*>(static_cast<const CameraProvider*>(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<CameraProvider*>(static_cast<const CameraProvider*>(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<VendorTagDescriptor> 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<String8>* sectionNames = desc->getAllSectionNames();
+    size_t numSections = sectionNames->size();
+    std::vector<std::vector<VendorTag>> tagsBySection(numSections);
+    int tagCount = desc->getTagCount();
+    std::vector<uint32_t> 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<ICameraProviderCallback>& 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<CameraDeviceStatus>(statusPair.second);
+        if (id >= mNumberOfLegacyCameras && status != CameraDeviceStatus::NOT_PRESENT) {
+            addDeviceNames(id, status, true);
+        }
+    }
+
+    return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus CameraProvider::getVendorTags(std::vector<VendorTagSection>* _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<std::string>* _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<ICameraDevice>* _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<CameraDevice> deviceImpl =
+            ndk::SharedRefBase::make<CameraDevice>(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<uint64_t>(newState);
+    mModule->notifyDeviceStateChange(state);
+    return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus CameraProvider::getConcurrentCameraIds(
+        std::vector<ConcurrentCameraIdCombination>* _aidl_return) {
+    if (_aidl_return == nullptr) {
+        return fromStatus(Status::ILLEGAL_ARGUMENT);
+    }
+    *_aidl_return = {};
+    return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus CameraProvider::isConcurrentStreamCombinationSupported(
+        const std::vector<CameraIdAndStreamCombination>&, 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 (file)
index 0000000..cedc2df
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <CameraModule.h>
+#include <VendorTagDescriptor.h>
+#include <aidl/android/hardware/camera/device/ICameraDevice.h>
+#include <aidl/android/hardware/camera/provider/BnCameraProvider.h>
+
+#include <map>
+
+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<ICameraProviderCallback>& in_callback) override;
+    ndk::ScopedAStatus getVendorTags(std::vector<VendorTagSection>* _aidl_return) override;
+    ndk::ScopedAStatus getCameraIdList(std::vector<std::string>* _aidl_return) override;
+    ndk::ScopedAStatus getCameraDeviceInterface(
+            const std::string& in_cameraDeviceName,
+            std::shared_ptr<ICameraDevice>* _aidl_return) override;
+    ndk::ScopedAStatus notifyDeviceStateChange(int64_t in_deviceState) override;
+    ndk::ScopedAStatus getConcurrentCameraIds(
+            std::vector<ConcurrentCameraIdCombination>* _aidl_return) override;
+    ndk::ScopedAStatus isConcurrentStreamCombinationSupported(
+            const std::vector<CameraIdAndStreamCombination>& in_configs,
+            bool* _aidl_return) override;
+
+  protected:
+    Mutex mCbLock;
+    std::shared_ptr<ICameraProviderCallback> mCallbacks = nullptr;
+
+    sp<CameraModule> mModule;
+
+    int mNumberOfLegacyCameras;
+    std::map<std::string, camera_device_status_t> mCameraStatusMap;  // camera id -> status
+    SortedVector<std::string> mCameraIds;  // the "0"/"1" legacy camera Ids
+    // (cameraId string, hidl device name) pairs
+    SortedVector<std::pair<std::string, std::string>> mCameraDeviceNames;
+
+    // Must be queried before using any APIs.
+    // APIs will only work when this returns true
+    bool mInitFailed;
+    bool initialize();
+
+    std::vector<VendorTagSection> 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 (file)
index 0000000..c7b2a3e
--- /dev/null
@@ -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 (file)
index 0000000..acd7f19
--- /dev/null
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.camera.provider</name>
+        <fqname>ICameraProvider/internal/0</fqname>
+    </hal>
+</manifest>
diff --git a/aidl/camera/provider/service.cpp b/aidl/camera/provider/service.cpp
new file mode 100644 (file)
index 0000000..37fbb44
--- /dev/null
@@ -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 <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <log/log.h>
+
+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<CameraProvider> defaultProvider = ndk::SharedRefBase::make<CameraProvider>();
+    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
+}