/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "HidlCamera3-Device"
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
//#define LOG_NNDEBUG 0  // Per-frame verbose logging

#ifdef LOG_NNDEBUG
#define ALOGVV(...) ALOGV(__VA_ARGS__)
#else
#define ALOGVV(...) ((void)0)
#endif

// Convenience macro for transient errors
#define CLOGE(fmt, ...) ALOGE("Camera %s: %s: " fmt, mId.c_str(), __FUNCTION__, \
            ##__VA_ARGS__)

// Convenience macros for transitioning to the error state
#define SET_ERR(fmt, ...) setErrorState(   \
    "%s: " fmt, __FUNCTION__,              \
    ##__VA_ARGS__)
#define SET_ERR_L(fmt, ...) setErrorStateLocked( \
    "%s: " fmt, __FUNCTION__,                    \
    ##__VA_ARGS__)


#include <inttypes.h>

#include <utility>

#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/Timers.h>
#include <cutils/properties.h>
#include <camera/StringUtils.h>
#include <com_android_internal_camera_flags.h>

#include <android/hardware/camera/device/3.7/ICameraInjectionSession.h>
#include <android/hardware/camera2/ICameraDeviceUser.h>

#include "device3/hidl/HidlCamera3OutputUtils.h"
#include "device3/hidl/HidlCamera3OfflineSession.h"
#include "utils/SessionConfigurationUtilsHidl.h"
#include "utils/TraceHFR.h"
#include "utils/Utils.h"

#include "../../common/hidl/HidlProviderInfo.h"
#include "HidlCamera3Device.h"

#include <algorithm>
#include <tuple>

using namespace android::camera3;
using namespace android::hardware::camera;
using namespace android::hardware::camera::device::V3_2;
using android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
namespace flags = com::android::internal::camera::flags;

namespace android {

hardware::graphics::common::V1_0::PixelFormat HidlCamera3Device::mapToPixelFormat(
        int frameworkFormat) {
    return (hardware::graphics::common::V1_0::PixelFormat) frameworkFormat;
}

DataspaceFlags HidlCamera3Device::mapToHidlDataspace(
        android_dataspace dataSpace) {
    return dataSpace;
}

BufferUsageFlags HidlCamera3Device::mapToConsumerUsage(
        uint64_t usage) {
    return usage;
}

StreamRotation HidlCamera3Device::mapToStreamRotation(camera_stream_rotation_t rotation) {
    switch (rotation) {
        case CAMERA_STREAM_ROTATION_0:
            return StreamRotation::ROTATION_0;
        case CAMERA_STREAM_ROTATION_90:
            return StreamRotation::ROTATION_90;
        case CAMERA_STREAM_ROTATION_180:
            return StreamRotation::ROTATION_180;
        case CAMERA_STREAM_ROTATION_270:
            return StreamRotation::ROTATION_270;
    }
    ALOGE("%s: Unknown stream rotation %d", __FUNCTION__, rotation);
    return StreamRotation::ROTATION_0;
}

status_t HidlCamera3Device::mapToStreamConfigurationMode(
        camera_stream_configuration_mode_t operationMode, StreamConfigurationMode *mode) {
    if (mode == nullptr) return BAD_VALUE;
    if (operationMode < CAMERA_VENDOR_STREAM_CONFIGURATION_MODE_START) {
        switch(operationMode) {
            case CAMERA_STREAM_CONFIGURATION_NORMAL_MODE:
                *mode = StreamConfigurationMode::NORMAL_MODE;
                break;
            case CAMERA_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE:
                *mode = StreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE;
                break;
            default:
                ALOGE("%s: Unknown stream configuration mode %d", __FUNCTION__, operationMode);
                return BAD_VALUE;
        }
    } else {
        *mode = static_cast<StreamConfigurationMode>(operationMode);
    }
    return OK;
}

int HidlCamera3Device::mapToFrameworkFormat(
        hardware::graphics::common::V1_0::PixelFormat pixelFormat) {
    return static_cast<uint32_t>(pixelFormat);
}

android_dataspace HidlCamera3Device::mapToFrameworkDataspace(
        DataspaceFlags dataSpace) {
    return static_cast<android_dataspace>(dataSpace);
}

uint64_t HidlCamera3Device::mapConsumerToFrameworkUsage(
        BufferUsageFlags usage) {
    return usage;
}

uint64_t HidlCamera3Device::mapProducerToFrameworkUsage(
        BufferUsageFlags usage) {
    return usage;
}

status_t HidlCamera3Device::initialize(sp<CameraProviderManager> manager,
        const std::string& monitorTags) {
    ATRACE_CALL();
    Mutex::Autolock il(mInterfaceLock);
    Mutex::Autolock l(mLock);

    ALOGV("%s: Initializing HIDL device for camera %s", __FUNCTION__, mId.c_str());
    if (mStatus != STATUS_UNINITIALIZED) {
        CLOGE("Already initialized!");
        return INVALID_OPERATION;
    }
    if (manager == nullptr) return INVALID_OPERATION;

    sp<ICameraDeviceSession> session;
    ATRACE_BEGIN("CameraHal::openSession");
    status_t res = manager->openHidlSession(mId, this,
            /*out*/ &session);
    ATRACE_END();
    if (res != OK) {
        SET_ERR_L("Could not open camera session: %s (%d)", strerror(-res), res);
        return res;
    }

    res = manager->getCameraCharacteristics(mId, mOverrideForPerfClass, &mDeviceInfo,
            hardware::ICameraService::ROTATION_OVERRIDE_NONE);
    if (res != OK) {
        SET_ERR_L("Could not retrieve camera characteristics: %s (%d)", strerror(-res), res);
        session->close();
        return res;
    }
    mSupportNativeZoomRatio = manager->supportNativeZoomRatio(mId);

    std::vector<std::string> physicalCameraIds;
    bool isLogical = manager->isLogicalCamera(mId, &physicalCameraIds);
    if (isLogical) {
        for (auto& physicalId : physicalCameraIds) {
            // Do not override characteristics for physical cameras
            res = manager->getCameraCharacteristics(
                    physicalId, /*overrideForPerfClass*/false, &mPhysicalDeviceInfoMap[physicalId],
                    hardware::ICameraService::ROTATION_OVERRIDE_NONE);
            if (res != OK) {
                SET_ERR_L("Could not retrieve camera %s characteristics: %s (%d)",
                        physicalId.c_str(), strerror(-res), res);
                session->close();
                return res;
            }

            bool usePrecorrectArray =
                    DistortionMapper::isDistortionSupported(mPhysicalDeviceInfoMap[physicalId]);
            if (usePrecorrectArray) {
                res = mDistortionMappers[physicalId].setupStaticInfo(
                        mPhysicalDeviceInfoMap[physicalId]);
                if (res != OK) {
                    SET_ERR_L("Unable to read camera %s's calibration fields for distortion "
                            "correction", physicalId.c_str());
                    session->close();
                    return res;
                }
            }

            mZoomRatioMappers[physicalId] = ZoomRatioMapper(
                    &mPhysicalDeviceInfoMap[physicalId],
                    mSupportNativeZoomRatio, usePrecorrectArray);

            if (SessionConfigurationUtils::supportsUltraHighResolutionCapture(
                    mPhysicalDeviceInfoMap[physicalId])) {
                mUHRCropAndMeteringRegionMappers[physicalId] =
                        UHRCropAndMeteringRegionMapper(mPhysicalDeviceInfoMap[physicalId],
                                usePrecorrectArray);
            }
        }
    }

    std::shared_ptr<RequestMetadataQueue> queue;
    auto requestQueueRet = session->getCaptureRequestMetadataQueue(
        [&queue](const auto& descriptor) {
            queue = std::make_shared<RequestMetadataQueue>(descriptor);
            if (!queue->isValid() || queue->availableToWrite() <= 0) {
                ALOGE("HAL returns empty request metadata fmq, not use it");
                queue = nullptr;
                // don't use the queue onwards.
            }
        });
    if (!requestQueueRet.isOk()) {
        ALOGE("Transaction error when getting request metadata fmq: %s, not use it",
                requestQueueRet.description().c_str());
        return DEAD_OBJECT;
    }

    std::unique_ptr<ResultMetadataQueue>& resQueue = mResultMetadataQueue;
    auto resultQueueRet = session->getCaptureResultMetadataQueue(
        [&resQueue](const auto& descriptor) {
            resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
            if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
                ALOGE("HAL returns empty result metadata fmq, not use it");
                resQueue = nullptr;
                // Don't use the resQueue onwards.
            }
        });
    if (!resultQueueRet.isOk()) {
        ALOGE("Transaction error when getting result metadata queue from camera session: %s",
                resultQueueRet.description().c_str());
        return DEAD_OBJECT;
    }
    IF_ALOGV() {
        session->interfaceChain([](
            ::android::hardware::hidl_vec<::android::hardware::hidl_string> interfaceChain) {
                ALOGV("Session interface chain:");
                for (const auto& iface : interfaceChain) {
                    ALOGV("  %s", iface.c_str());
                }
            });
    }

    camera_metadata_entry bufMgrMode =
            mDeviceInfo.find(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION);
    if (bufMgrMode.count > 0) {
         mUseHalBufManager = (bufMgrMode.data.u8[0] ==
            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
    }

    camera_metadata_entry_t capabilities = mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
    for (size_t i = 0; i < capabilities.count; i++) {
        uint8_t capability = capabilities.data.u8[i];
        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING) {
            mSupportOfflineProcessing = true;
        }
    }

    mInterface = new HidlHalInterface(session, queue, mUseHalBufManager, mSupportOfflineProcessing);

    std::string providerType;
    mVendorTagId = manager->getProviderTagIdLocked(mId);
    mTagMonitor.initialize(mVendorTagId);
    if (!monitorTags.empty()) {
        mTagMonitor.parseTagsToMonitor(monitorTags);
    }

    // Metadata tags needs fixup for monochrome camera device version less
    // than 3.5.
    hardware::hidl_version maxVersion{0,0};
    IPCTransport transport = IPCTransport::HIDL;
    res = manager->getHighestSupportedVersion(mId, &maxVersion, &transport);
    if (res != OK) {
        ALOGE("%s: Error in getting camera device version id: %s (%d)",
                __FUNCTION__, strerror(-res), res);
        return res;
    }
    int deviceVersion = HARDWARE_DEVICE_API_VERSION(
            maxVersion.get_major(), maxVersion.get_minor());

    bool isMonochrome = false;
    for (size_t i = 0; i < capabilities.count; i++) {
        uint8_t capability = capabilities.data.u8[i];
        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
            isMonochrome = true;
        }
    }
    mNeedFixupMonochromeTags = (isMonochrome && deviceVersion < CAMERA_DEVICE_API_VERSION_3_5);

    return initializeCommonLocked(manager);
}

hardware::Return<void> HidlCamera3Device::requestStreamBuffers(
        const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
        requestStreamBuffers_cb _hidl_cb) {
    RequestBufferStates states {
        mId, mRequestBufferInterfaceLock, mUseHalBufManager, mHalBufManagedStreamIds,
        mOutputStreams, mSessionStatsBuilder,
        *this, *mInterface, *this};
    camera3::requestStreamBuffers(states, bufReqs, _hidl_cb);
    return hardware::Void();
}

hardware::Return<void> HidlCamera3Device::returnStreamBuffers(
        const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
    ReturnBufferStates states {
        mId, mUseHalBufManager, mHalBufManagedStreamIds, mOutputStreams,
        mSessionStatsBuilder, *mInterface};
    camera3::returnStreamBuffers(states, buffers);
    return hardware::Void();
}

hardware::Return<void> HidlCamera3Device::processCaptureResult_3_4(
        const hardware::hidl_vec<
                hardware::camera::device::V3_4::CaptureResult>& results) {
    // Ideally we should grab mLock, but that can lead to deadlock, and
    // it's not super important to get up to date value of mStatus for this
    // warning print, hence skipping the lock here
    if (mStatus == STATUS_ERROR) {
        // Per API contract, HAL should act as closed after device error
        // But mStatus can be set to error by framework as well, so just log
        // a warning here.
        ALOGW("%s: received capture result in error state.", __FUNCTION__);
    }

    sp<NotificationListener> listener;
    {
        std::lock_guard<std::mutex> l(mOutputLock);
        listener = mListener.promote();
    }

    if (mProcessCaptureResultLock.tryLock() != OK) {
        // This should never happen; it indicates a wrong client implementation
        // that doesn't follow the contract. But, we can be tolerant here.
        ALOGE("%s: callback overlapped! waiting 1s...",
                __FUNCTION__);
        if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) {
            ALOGE("%s: cannot acquire lock in 1s, dropping results",
                    __FUNCTION__);
            // really don't know what to do, so bail out.
            return hardware::Void();
        }
    }
    HidlCaptureOutputStates states {
       {
        mId,
        mInFlightLock, mLastCompletedRegularFrameNumber,
        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
        mInFlightMap, mOutputLock,  mResultQueue, mResultSignal,
        mNextShutterFrameNumber,
        mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
        mNextResultFrameNumber,
        mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
        mUseHalBufManager, mHalBufManagedStreamIds, mUsePartialResult, mNeedFixupMonochromeTags,
        mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
        mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
        mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
        *mInterface, mLegacyClient, mMinExpectedDuration, mIsFixedFps, mRotationOverride,
        mActivePhysicalId}, mResultMetadataQueue
    };

    //HidlCaptureOutputStates hidlStates {
    //}

    for (const auto& result : results) {
        processOneCaptureResultLocked(states, result.v3_2, result.physicalCameraMetadata);
    }
    mProcessCaptureResultLock.unlock();
    return hardware::Void();
}

// Only one processCaptureResult should be called at a time, so
// the locks won't block. The locks are present here simply to enforce this.
hardware::Return<void> HidlCamera3Device::processCaptureResult(
        const hardware::hidl_vec<
                hardware::camera::device::V3_2::CaptureResult>& results) {
    hardware::hidl_vec<hardware::camera::device::V3_4::PhysicalCameraMetadata> noPhysMetadata;

    // Ideally we should grab mLock, but that can lead to deadlock, and
    // it's not super important to get up to date value of mStatus for this
    // warning print, hence skipping the lock here
    if (mStatus == STATUS_ERROR) {
        // Per API contract, HAL should act as closed after device error
        // But mStatus can be set to error by framework as well, so just log
        // a warning here.
        ALOGW("%s: received capture result in error state.", __FUNCTION__);
    }

    sp<NotificationListener> listener;
    {
        std::lock_guard<std::mutex> l(mOutputLock);
        listener = mListener.promote();
    }

    if (mProcessCaptureResultLock.tryLock() != OK) {
        // This should never happen; it indicates a wrong client implementation
        // that doesn't follow the contract. But, we can be tolerant here.
        ALOGE("%s: callback overlapped! waiting 1s...",
                __FUNCTION__);
        if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) {
            ALOGE("%s: cannot acquire lock in 1s, dropping results",
                    __FUNCTION__);
            // really don't know what to do, so bail out.
            return hardware::Void();
        }
    }

    HidlCaptureOutputStates states {
      { mId,
        mInFlightLock, mLastCompletedRegularFrameNumber,
        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
        mInFlightMap, mOutputLock,  mResultQueue, mResultSignal,
        mNextShutterFrameNumber,
        mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
        mNextResultFrameNumber,
        mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
        mUseHalBufManager, mHalBufManagedStreamIds, mUsePartialResult, mNeedFixupMonochromeTags,
        mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
        mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
        mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
        *mInterface, mLegacyClient, mMinExpectedDuration, mIsFixedFps, mRotationOverride,
        mActivePhysicalId}, mResultMetadataQueue
    };

    for (const auto& result : results) {
        processOneCaptureResultLocked(states, result, noPhysMetadata);
    }
    mProcessCaptureResultLock.unlock();
    return hardware::Void();
}

hardware::Return<void> HidlCamera3Device::notify(
        const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
    return notifyHelper<hardware::camera::device::V3_2::NotifyMsg>(msgs);
}

template<typename NotifyMsgType>
hardware::Return<void> HidlCamera3Device::notifyHelper(
        const hardware::hidl_vec<NotifyMsgType>& msgs) {
    // Ideally we should grab mLock, but that can lead to deadlock, and
    // it's not super important to get up to date value of mStatus for this
    // warning print, hence skipping the lock here
    if (mStatus == STATUS_ERROR) {
        // Per API contract, HAL should act as closed after device error
        // But mStatus can be set to error by framework as well, so just log
        // a warning here.
        ALOGW("%s: received notify message in error state.", __FUNCTION__);
    }

    sp<NotificationListener> listener;
    {
        std::lock_guard<std::mutex> l(mOutputLock);
        listener = mListener.promote();
    }

    HidlCaptureOutputStates states {
      { mId,
        mInFlightLock, mLastCompletedRegularFrameNumber,
        mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
        mInFlightMap, mOutputLock,  mResultQueue, mResultSignal,
        mNextShutterFrameNumber,
        mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
        mNextResultFrameNumber,
        mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
        mUseHalBufManager, mHalBufManagedStreamIds, mUsePartialResult, mNeedFixupMonochromeTags,
        mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
        mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
        mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
        *mInterface, mLegacyClient, mMinExpectedDuration, mIsFixedFps, mRotationOverride,
        mActivePhysicalId}, mResultMetadataQueue
    };
    for (const auto& msg : msgs) {
        camera3::notify(states, msg);
    }
    return hardware::Void();
}

status_t HidlCamera3Device::switchToOffline(
        const std::vector<int32_t>& streamsToKeep,
        /*out*/ sp<CameraOfflineSessionBase>* session) {
    ATRACE_CALL();
    if (session == nullptr) {
        ALOGE("%s: session must not be null", __FUNCTION__);
        return BAD_VALUE;
    }

    Mutex::Autolock il(mInterfaceLock);

    bool hasInputStream = mInputStream != nullptr;
    int32_t inputStreamId = hasInputStream ? mInputStream->getId() : -1;
    bool inputStreamSupportsOffline = hasInputStream ?
            mInputStream->getOfflineProcessingSupport() : false;
    auto outputStreamIds = mOutputStreams.getStreamIds();
    auto streamIds = outputStreamIds;
    if (hasInputStream) {
        streamIds.push_back(mInputStream->getId());
    }

    // Check all streams in streamsToKeep supports offline mode
    for (auto id : streamsToKeep) {
        if (std::find(streamIds.begin(), streamIds.end(), id) == streamIds.end()) {
            ALOGE("%s: Unknown stream ID %d", __FUNCTION__, id);
            return BAD_VALUE;
        } else if (id == inputStreamId) {
            if (!inputStreamSupportsOffline) {
                ALOGE("%s: input stream %d cannot be switched to offline",
                        __FUNCTION__, id);
                return BAD_VALUE;
            }
        } else {
            sp<camera3::Camera3OutputStreamInterface> stream = mOutputStreams.get(id);
            if (!stream->getOfflineProcessingSupport()) {
                ALOGE("%s: output stream %d cannot be switched to offline",
                        __FUNCTION__, id);
                return BAD_VALUE;
            }
        }
    }
    // TODO: block surface sharing and surface group streams until we can support them

    // Stop repeating request, wait until all remaining requests are submitted, then call into
    // HAL switchToOffline
    hardware::camera::device::V3_6::CameraOfflineSessionInfo offlineSessionInfo;
    sp<hardware::camera::device::V3_6::ICameraOfflineSession> offlineSession;
    camera3::BufferRecords bufferRecords;
    status_t ret = static_cast<HidlRequestThread *>(mRequestThread.get())->switchToOffline(
            streamsToKeep, &offlineSessionInfo, &offlineSession, &bufferRecords);

    if (ret != OK) {
        SET_ERR("Switch to offline failed: %s (%d)", strerror(-ret), ret);
        return ret;
    }

    bool succ = mRequestBufferSM.onSwitchToOfflineSuccess();
    if (!succ) {
        SET_ERR("HAL must not be calling requestStreamBuffers call");
        // TODO: block ALL callbacks from HAL till app configured new streams?
        return UNKNOWN_ERROR;
    }

    // Verify offlineSessionInfo
    std::vector<int32_t> offlineStreamIds;
    offlineStreamIds.reserve(offlineSessionInfo.offlineStreams.size());
    for (auto offlineStream : offlineSessionInfo.offlineStreams) {
        // verify stream IDs
        int32_t id = offlineStream.id;
        if (std::find(streamIds.begin(), streamIds.end(), id) == streamIds.end()) {
            SET_ERR("stream ID %d not found!", id);
            return UNKNOWN_ERROR;
        }

        // When not using HAL buf manager, only allow streams requested by app to be preserved
        if (!mUseHalBufManager) {
            if (std::find(streamsToKeep.begin(), streamsToKeep.end(), id) == streamsToKeep.end()) {
                SET_ERR("stream ID %d must not be switched to offline!", id);
                return UNKNOWN_ERROR;
            }
        }

        offlineStreamIds.push_back(id);
        sp<Camera3StreamInterface> stream = (id == inputStreamId) ?
                static_cast<sp<Camera3StreamInterface>>(mInputStream) :
                static_cast<sp<Camera3StreamInterface>>(mOutputStreams.get(id));
        // Verify number of outstanding buffers
        if (stream->getOutstandingBuffersCount() != offlineStream.numOutstandingBuffers) {
            SET_ERR("Offline stream %d # of remaining buffer mismatch: (%zu,%d) (service/HAL)",
                    id, stream->getOutstandingBuffersCount(), offlineStream.numOutstandingBuffers);
            return UNKNOWN_ERROR;
        }
    }

    // Verify all streams to be deleted don't have any outstanding buffers
    if (hasInputStream && std::find(offlineStreamIds.begin(), offlineStreamIds.end(),
                inputStreamId) == offlineStreamIds.end()) {
        if (mInputStream->hasOutstandingBuffers()) {
            SET_ERR("Input stream %d still has %zu outstanding buffer!",
                    inputStreamId, mInputStream->getOutstandingBuffersCount());
            return UNKNOWN_ERROR;
        }
    }

    for (const auto& outStreamId : outputStreamIds) {
        if (std::find(offlineStreamIds.begin(), offlineStreamIds.end(),
                outStreamId) == offlineStreamIds.end()) {
            auto outStream = mOutputStreams.get(outStreamId);
            if (outStream->hasOutstandingBuffers()) {
                SET_ERR("Output stream %d still has %zu outstanding buffer!",
                        outStreamId, outStream->getOutstandingBuffersCount());
                return UNKNOWN_ERROR;
            }
        }
    }

    InFlightRequestMap offlineReqs;
    // Verify inflight requests and their pending buffers
    {
        std::lock_guard<std::mutex> l(mInFlightLock);
        for (auto offlineReq : offlineSessionInfo.offlineRequests) {
            int idx = mInFlightMap.indexOfKey(offlineReq.frameNumber);
            if (idx == NAME_NOT_FOUND) {
                SET_ERR("Offline request frame number %d not found!", offlineReq.frameNumber);
                return UNKNOWN_ERROR;
            }

            const auto& inflightReq = mInFlightMap.valueAt(idx);
            // TODO: check specific stream IDs
            size_t numBuffersLeft = static_cast<size_t>(inflightReq.numBuffersLeft);
            if (numBuffersLeft != offlineReq.pendingStreams.size()) {
                SET_ERR("Offline request # of remaining buffer mismatch: (%d,%d) (service/HAL)",
                        inflightReq.numBuffersLeft, offlineReq.pendingStreams.size());
                return UNKNOWN_ERROR;
            }
            offlineReqs.add(offlineReq.frameNumber, inflightReq);
        }
    }

    // Create Camera3OfflineSession and transfer object ownership
    //   (streams, inflight requests, buffer caches)
    camera3::StreamSet offlineStreamSet;
    sp<camera3::Camera3Stream> inputStream;
    for (auto offlineStream : offlineSessionInfo.offlineStreams) {
        int32_t id = offlineStream.id;
        if (mInputStream != nullptr && id == mInputStream->getId()) {
            inputStream = mInputStream;
        } else {
            offlineStreamSet.add(id, mOutputStreams.get(id));
        }
    }

    // TODO: check if we need to lock before copying states
    //       though technically no other thread should be talking to Camera3Device at this point
    Camera3OfflineStates offlineStates(
            mTagMonitor, mVendorTagId, mUseHalBufManager, mHalBufManagedStreamIds,
            mNeedFixupMonochromeTags, mUsePartialResult, mNumPartialResults,
            mLastCompletedRegularFrameNumber, mLastCompletedReprocessFrameNumber,
            mLastCompletedZslFrameNumber, mNextResultFrameNumber,
            mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
            mNextShutterFrameNumber, mNextReprocessShutterFrameNumber,
            mNextZslStillShutterFrameNumber, mDeviceInfo, mPhysicalDeviceInfoMap,
            mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers);

    *session = new HidlCamera3OfflineSession(mId, inputStream, offlineStreamSet,
            std::move(bufferRecords), offlineReqs, offlineStates, offlineSession);

    // Delete all streams that has been transferred to offline session
    Mutex::Autolock l(mLock);
    for (auto offlineStream : offlineSessionInfo.offlineStreams) {
        int32_t id = offlineStream.id;
        if (mInputStream != nullptr && id == mInputStream->getId()) {
            mInputStream.clear();
        } else {
            mOutputStreams.remove(id);
        }
    }

    // disconnect all other streams and switch to UNCONFIGURED state
    if (mInputStream != nullptr) {
        ret = mInputStream->disconnect();
        if (ret != OK) {
            SET_ERR_L("disconnect input stream failed!");
            return UNKNOWN_ERROR;
        }
    }

    for (auto streamId : mOutputStreams.getStreamIds()) {
        sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
        ret = stream->disconnect();
        if (ret != OK) {
            SET_ERR_L("disconnect output stream %d failed!", streamId);
            return UNKNOWN_ERROR;
        }
    }

    mInputStream.clear();
    mOutputStreams.clear();
    mNeedConfig = true;
    internalUpdateStatusLocked(STATUS_UNCONFIGURED);
    mOperatingMode = NO_MODE;
    mIsConstrainedHighSpeedConfiguration = false;
    mRequestThread->clearPreviousRequest();

    return OK;
    // TO be done by CameraDeviceClient/Camera3OfflineSession
    // register the offline client to camera service
    // Setup result passthing threads etc
    // Initialize offline session so HAL can start sending callback to it (result Fmq)
    // TODO: check how many onIdle callback will be sent
    // Java side to make sure the CameraCaptureSession is properly closed
}

void HidlCamera3Device::applyMaxBatchSizeLocked(
        RequestList* requestList, const sp<camera3::Camera3OutputStreamInterface>& stream) {
    int batchSize = requestList->size();

    (*requestList->begin())->mBatchSize = batchSize;
    stream->setBatchSize(batchSize);
}

sp<Camera3Device::RequestThread> HidlCamera3Device::createNewRequestThread(
                wp<Camera3Device> parent, sp<camera3::StatusTracker> statusTracker,
                sp<Camera3Device::HalInterface> interface,
                const Vector<int32_t>& sessionParamKeys,
                bool useHalBufManager,
                bool supportCameraMute,
                int rotationOverride,
                bool supportSettingsOverride) {
        return new HidlRequestThread(parent, statusTracker, interface, sessionParamKeys,
                useHalBufManager, supportCameraMute, rotationOverride,
                supportSettingsOverride);
};

sp<Camera3Device::Camera3DeviceInjectionMethods>
HidlCamera3Device::createCamera3DeviceInjectionMethods(wp<Camera3Device> parent) {
    return new HidlCamera3DeviceInjectionMethods(parent);
}

status_t HidlCamera3Device::injectionCameraInitialize(const std::string &injectedCamId,
            sp<CameraProviderManager> manager) {
        return (static_cast<HidlCamera3DeviceInjectionMethods *>(
                mInjectionMethods.get()))->injectionInitialize(injectedCamId, manager, this);
};


HidlCamera3Device::HidlHalInterface::HidlHalInterface(
            sp<device::V3_2::ICameraDeviceSession> &session,
            std::shared_ptr<RequestMetadataQueue> queue,
            bool useHalBufManager, bool supportOfflineProcessing) :
        HalInterface(useHalBufManager, supportOfflineProcessing),
        mHidlSession(session),
        mRequestMetadataQueue(queue) {
    // Check with hardware service manager if we can downcast these interfaces
    // Somewhat expensive, so cache the results at startup
    auto castResult_3_7 = device::V3_7::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_7.isOk()) {
        mHidlSession_3_7 = castResult_3_7;
    }
    auto castResult_3_6 = device::V3_6::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_6.isOk()) {
        mHidlSession_3_6 = castResult_3_6;
    }
    auto castResult_3_5 = device::V3_5::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_5.isOk()) {
        mHidlSession_3_5 = castResult_3_5;
    }
    auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_4.isOk()) {
        mHidlSession_3_4 = castResult_3_4;
    }
    auto castResult_3_3 = device::V3_3::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_3.isOk()) {
        mHidlSession_3_3 = castResult_3_3;
    }
}

bool HidlCamera3Device::HidlHalInterface::valid() {
    return (mHidlSession != nullptr);
}

void HidlCamera3Device::HidlHalInterface::clear() {
    mHidlSession_3_7.clear();
    mHidlSession_3_6.clear();
    mHidlSession_3_5.clear();
    mHidlSession_3_4.clear();
    mHidlSession_3_3.clear();
    mHidlSession.clear();
}

status_t HidlCamera3Device::HidlHalInterface::constructDefaultRequestSettings(
        camera_request_template_t templateId,
        /*out*/ camera_metadata_t **requestTemplate) {
    ATRACE_NAME("CameraHidlHal::constructDefaultRequestSettings");
    if (!valid()) return INVALID_OPERATION;
    status_t res = OK;

    common::V1_0::Status status;

    auto requestCallback = [&status, &requestTemplate]
            (common::V1_0::Status s, const device::V3_2::CameraMetadata& request) {
            status = s;
            if (status == common::V1_0::Status::OK) {
                const camera_metadata *r =
                        reinterpret_cast<const camera_metadata_t*>(request.data());
                size_t expectedSize = request.size();
                int ret = validate_camera_metadata_structure(r, &expectedSize);
                if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
                    *requestTemplate = clone_camera_metadata(r);
                    if (*requestTemplate == nullptr) {
                        ALOGE("%s: Unable to clone camera metadata received from HAL",
                                __FUNCTION__);
                        status = common::V1_0::Status::INTERNAL_ERROR;
                    }
                } else {
                    ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
                    status = common::V1_0::Status::INTERNAL_ERROR;
                }
            }
        };
    hardware::Return<void> err;
    RequestTemplate id;
    switch (templateId) {
        case CAMERA_TEMPLATE_PREVIEW:
            id = RequestTemplate::PREVIEW;
            break;
        case CAMERA_TEMPLATE_STILL_CAPTURE:
            id = RequestTemplate::STILL_CAPTURE;
            break;
        case CAMERA_TEMPLATE_VIDEO_RECORD:
            id = RequestTemplate::VIDEO_RECORD;
            break;
        case CAMERA_TEMPLATE_VIDEO_SNAPSHOT:
            id = RequestTemplate::VIDEO_SNAPSHOT;
            break;
        case CAMERA_TEMPLATE_ZERO_SHUTTER_LAG:
            id = RequestTemplate::ZERO_SHUTTER_LAG;
            break;
        case CAMERA_TEMPLATE_MANUAL:
            id = RequestTemplate::MANUAL;
            break;
        default:
            // Unknown template ID, or this HAL is too old to support it
            return BAD_VALUE;
    }
    err = mHidlSession->constructDefaultRequestSettings(id, requestCallback);

    if (!err.isOk()) {
        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
        res = DEAD_OBJECT;
    } else {
        res = HidlProviderInfo::mapToStatusT(status);
    }

    return res;
}

bool HidlCamera3Device::HidlHalInterface::isReconfigurationRequired(
        CameraMetadata& oldSessionParams, CameraMetadata& newSessionParams) {
    // We do reconfiguration by default;
    bool ret = true;
    if ((mHidlSession_3_5 != nullptr) && mIsReconfigurationQuerySupported) {
        android::hardware::hidl_vec<uint8_t> oldParams, newParams;
        camera_metadata_t* oldSessioMeta = const_cast<camera_metadata_t*>(
                oldSessionParams.getAndLock());
        camera_metadata_t* newSessioMeta = const_cast<camera_metadata_t*>(
                newSessionParams.getAndLock());
        oldParams.setToExternal(reinterpret_cast<uint8_t*>(oldSessioMeta),
                get_camera_metadata_size(oldSessioMeta));
        newParams.setToExternal(reinterpret_cast<uint8_t*>(newSessioMeta),
                get_camera_metadata_size(newSessioMeta));
        hardware::camera::common::V1_0::Status callStatus;
        bool required;
        auto hidlCb = [&callStatus, &required] (hardware::camera::common::V1_0::Status s,
                bool requiredFlag) {
            callStatus = s;
            required = requiredFlag;
        };
        auto err = mHidlSession_3_5->isReconfigurationRequired(oldParams, newParams, hidlCb);
        oldSessionParams.unlock(oldSessioMeta);
        newSessionParams.unlock(newSessioMeta);
        if (err.isOk()) {
            switch (callStatus) {
                case hardware::camera::common::V1_0::Status::OK:
                    ret = required;
                    break;
                case hardware::camera::common::V1_0::Status::METHOD_NOT_SUPPORTED:
                    mIsReconfigurationQuerySupported = false;
                    ret = true;
                    break;
                default:
                    ALOGV("%s: Reconfiguration query failed: %d", __FUNCTION__, eToI(callStatus));
                    ret = true;
            }
        } else {
            ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, err.description().c_str());
            ret = true;
        }
    }

    return ret;
}

status_t HidlCamera3Device::HidlHalInterface::configureStreams(
        const camera_metadata_t *sessionParams,
        camera_stream_configuration *config, const std::vector<uint32_t>& bufferSizes,
        int64_t /*logId*/) {
    ATRACE_NAME("CameraHal::configureStreams");
    if (!valid()) return INVALID_OPERATION;
    status_t res = OK;

    if (config->input_is_multi_resolution && mHidlSession_3_7 == nullptr) {
        ALOGE("%s: Camera device doesn't support multi-resolution input stream", __FUNCTION__);
        return BAD_VALUE;
    }

    // Convert stream config to HIDL
    std::set<int> activeStreams;
    device::V3_2::StreamConfiguration requestedConfiguration3_2;
    device::V3_4::StreamConfiguration requestedConfiguration3_4;
    device::V3_7::StreamConfiguration requestedConfiguration3_7;
    requestedConfiguration3_2.streams.resize(config->num_streams);
    requestedConfiguration3_4.streams.resize(config->num_streams);
    requestedConfiguration3_7.streams.resize(config->num_streams);
    mHalBufManagedStreamIds.clear();
    for (size_t i = 0; i < config->num_streams; i++) {
        device::V3_2::Stream &dst3_2 = requestedConfiguration3_2.streams[i];
        device::V3_4::Stream &dst3_4 = requestedConfiguration3_4.streams[i];
        device::V3_7::Stream &dst3_7 = requestedConfiguration3_7.streams[i];
        camera3::camera_stream_t *src = config->streams[i];

        Camera3Stream* cam3stream = Camera3Stream::cast(src);
        cam3stream->setBufferFreedListener(this);
        int streamId = cam3stream->getId();
        StreamType streamType;
        switch (src->stream_type) {
            case CAMERA_STREAM_OUTPUT:
                streamType = StreamType::OUTPUT;
                if (mUseHalBufManager) {
                    mHalBufManagedStreamIds.insert(streamId);
                }
                break;
            case CAMERA_STREAM_INPUT:
                streamType = StreamType::INPUT;
                break;
            default:
                ALOGE("%s: Stream %d: Unsupported stream type %d",
                        __FUNCTION__, streamId, config->streams[i]->stream_type);
                return BAD_VALUE;
        }

        dst3_2.id = streamId;
        dst3_2.streamType = streamType;
        dst3_2.width = src->width;
        dst3_2.height = src->height;
        dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage());
        dst3_2.rotation = mapToStreamRotation((camera_stream_rotation_t) src->rotation);
        // For HidlSession version 3.5 or newer, the format and dataSpace sent
        // to HAL are original, not the overridden ones.
        if (mHidlSession_3_5 != nullptr) {
            dst3_2.format = mapToPixelFormat(cam3stream->isFormatOverridden() ?
                    cam3stream->getOriginalFormat() : src->format);
            dst3_2.dataSpace = mapToHidlDataspace(cam3stream->isDataSpaceOverridden() ?
                    cam3stream->getOriginalDataSpace() : src->data_space);
        } else {
            dst3_2.format = mapToPixelFormat(src->format);
            dst3_2.dataSpace = mapToHidlDataspace(src->data_space);
        }
        dst3_4.v3_2 = dst3_2;
        dst3_4.bufferSize = bufferSizes[i];
        if (!src->physical_camera_id.empty()) {
            dst3_4.physicalCameraId = src->physical_camera_id;
        }
        dst3_7.v3_4 = dst3_4;
        dst3_7.groupId = cam3stream->getHalStreamGroupId();
        dst3_7.sensorPixelModesUsed.resize(src->sensor_pixel_modes_used.size());
        size_t j = 0;
        for (int mode : src->sensor_pixel_modes_used) {
            dst3_7.sensorPixelModesUsed[j++] =
                    static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
        }
        if (src->dynamic_range_profile !=
                    ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD) {
            ALOGE("%s: Camera device doesn't support non-standard dynamic range profiles: %" PRIx64,
                    __FUNCTION__, src->dynamic_range_profile);
            return BAD_VALUE;
        }
        if (src->use_case != ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
            ALOGE("%s: Camera device doesn't support non-default stream use case %" PRId64 "!",
                    __FUNCTION__, src->use_case);
            return BAD_VALUE;
        }
        activeStreams.insert(streamId);
        // Create Buffer ID map if necessary
        mBufferRecords.tryCreateBufferCache(streamId);
    }
    // remove BufferIdMap for deleted streams
    mBufferRecords.removeInactiveBufferCaches(activeStreams);

    StreamConfigurationMode operationMode;
    res = mapToStreamConfigurationMode(
            (camera_stream_configuration_mode_t) config->operation_mode,
            /*out*/ &operationMode);
    if (res != OK) {
        return res;
    }
    requestedConfiguration3_2.operationMode = operationMode;
    requestedConfiguration3_4.operationMode = operationMode;
    requestedConfiguration3_7.operationMode = operationMode;
    size_t sessionParamSize = get_camera_metadata_size(sessionParams);
    requestedConfiguration3_4.sessionParams.setToExternal(
            reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
            sessionParamSize);
    requestedConfiguration3_7.sessionParams.setToExternal(
            reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
            sessionParamSize);

    // Invoke configureStreams
    device::V3_3::HalStreamConfiguration finalConfiguration;
    device::V3_4::HalStreamConfiguration finalConfiguration3_4;
    device::V3_6::HalStreamConfiguration finalConfiguration3_6;
    common::V1_0::Status status;

    auto configStream34Cb = [&status, &finalConfiguration3_4]
            (common::V1_0::Status s, const device::V3_4::HalStreamConfiguration& halConfiguration) {
                finalConfiguration3_4 = halConfiguration;
                status = s;
            };

    auto configStream36Cb = [&status, &finalConfiguration3_6]
            (common::V1_0::Status s, const device::V3_6::HalStreamConfiguration& halConfiguration) {
                finalConfiguration3_6 = halConfiguration;
                status = s;
            };

    auto postprocConfigStream34 = [&finalConfiguration, &finalConfiguration3_4]
            (hardware::Return<void>& err) -> status_t {
                if (!err.isOk()) {
                    ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
                    return DEAD_OBJECT;
                }
                finalConfiguration.streams.resize(finalConfiguration3_4.streams.size());
                for (size_t i = 0; i < finalConfiguration3_4.streams.size(); i++) {
                    finalConfiguration.streams[i] = finalConfiguration3_4.streams[i].v3_3;
                }
                return OK;
            };

    auto postprocConfigStream36 = [&finalConfiguration, &finalConfiguration3_6]
            (hardware::Return<void>& err) -> status_t {
                if (!err.isOk()) {
                    ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
                    return DEAD_OBJECT;
                }
                finalConfiguration.streams.resize(finalConfiguration3_6.streams.size());
                for (size_t i = 0; i < finalConfiguration3_6.streams.size(); i++) {
                    finalConfiguration.streams[i] = finalConfiguration3_6.streams[i].v3_4.v3_3;
                }
                return OK;
            };

    if (mHidlSession_3_7 != nullptr) {
        ALOGV("%s: v3.7 device found", __FUNCTION__);
        requestedConfiguration3_7.streamConfigCounter = mNextStreamConfigCounter++;
        requestedConfiguration3_7.multiResolutionInputImage = config->input_is_multi_resolution;
        auto err = mHidlSession_3_7->configureStreams_3_7(
                requestedConfiguration3_7, configStream36Cb);
        res = postprocConfigStream36(err);
        if (res != OK) {
            return res;
        }
    } else if (mHidlSession_3_6 != nullptr) {
        ALOGV("%s: v3.6 device found", __FUNCTION__);
        device::V3_5::StreamConfiguration requestedConfiguration3_5;
        requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
        requestedConfiguration3_5.streamConfigCounter = mNextStreamConfigCounter++;
        auto err = mHidlSession_3_6->configureStreams_3_6(
                requestedConfiguration3_5, configStream36Cb);
        res = postprocConfigStream36(err);
        if (res != OK) {
            return res;
        }
    } else if (mHidlSession_3_5 != nullptr) {
        ALOGV("%s: v3.5 device found", __FUNCTION__);
        device::V3_5::StreamConfiguration requestedConfiguration3_5;
        requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
        requestedConfiguration3_5.streamConfigCounter = mNextStreamConfigCounter++;
        auto err = mHidlSession_3_5->configureStreams_3_5(
                requestedConfiguration3_5, configStream34Cb);
        res = postprocConfigStream34(err);
        if (res != OK) {
            return res;
        }
    } else if (mHidlSession_3_4 != nullptr) {
        // We do; use v3.4 for the call
        ALOGV("%s: v3.4 device found", __FUNCTION__);
        auto err = mHidlSession_3_4->configureStreams_3_4(
                requestedConfiguration3_4, configStream34Cb);
        res = postprocConfigStream34(err);
        if (res != OK) {
            return res;
        }
    } else if (mHidlSession_3_3 != nullptr) {
        // We do; use v3.3 for the call
        ALOGV("%s: v3.3 device found", __FUNCTION__);
        auto err = mHidlSession_3_3->configureStreams_3_3(requestedConfiguration3_2,
            [&status, &finalConfiguration]
            (common::V1_0::Status s, const device::V3_3::HalStreamConfiguration& halConfiguration) {
                finalConfiguration = halConfiguration;
                status = s;
            });
        if (!err.isOk()) {
            ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
            return DEAD_OBJECT;
        }
    } else {
        // We don't; use v3.2 call and construct a v3.3 HalStreamConfiguration
        ALOGV("%s: v3.2 device found", __FUNCTION__);
        HalStreamConfiguration finalConfiguration_3_2;
        auto err = mHidlSession->configureStreams(requestedConfiguration3_2,
                [&status, &finalConfiguration_3_2]
                (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
                    finalConfiguration_3_2 = halConfiguration;
                    status = s;
                });
        if (!err.isOk()) {
            ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
            return DEAD_OBJECT;
        }
        finalConfiguration.streams.resize(finalConfiguration_3_2.streams.size());
        for (size_t i = 0; i < finalConfiguration_3_2.streams.size(); i++) {
            finalConfiguration.streams[i].v3_2 = finalConfiguration_3_2.streams[i];
            finalConfiguration.streams[i].overrideDataSpace =
                    requestedConfiguration3_2.streams[i].dataSpace;
        }
    }

    if (status != common::V1_0::Status::OK ) {
        return HidlProviderInfo::mapToStatusT(status);
    }

    // And convert output stream configuration from HIDL

    for (size_t i = 0; i < config->num_streams; i++) {
        camera3::camera_stream_t *dst = config->streams[i];
        int streamId = Camera3Stream::cast(dst)->getId();

        // Start scan at i, with the assumption that the stream order matches
        size_t realIdx = i;
        bool found = false;
        size_t halStreamCount = finalConfiguration.streams.size();
        for (size_t idx = 0; idx < halStreamCount; idx++) {
            if (finalConfiguration.streams[realIdx].v3_2.id == streamId) {
                found = true;
                break;
            }
            realIdx = (realIdx >= halStreamCount - 1) ? 0 : realIdx + 1;
        }
        if (!found) {
            ALOGE("%s: Stream %d not found in stream configuration response from HAL",
                    __FUNCTION__, streamId);
            return INVALID_OPERATION;
        }
        device::V3_3::HalStream &src = finalConfiguration.streams[realIdx];
        device::V3_6::HalStream &src_36 = finalConfiguration3_6.streams[realIdx];

        Camera3Stream* dstStream = Camera3Stream::cast(dst);
        int overrideFormat = mapToFrameworkFormat(src.v3_2.overrideFormat);
        android_dataspace overrideDataSpace = mapToFrameworkDataspace(src.overrideDataSpace);

        if (mHidlSession_3_6 != nullptr) {
            dstStream->setOfflineProcessingSupport(src_36.supportOffline);
        }

        if (dstStream->getOriginalFormat() != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
            dstStream->setFormatOverride(false);
            dstStream->setDataSpaceOverride(false);
            if (dst->format != overrideFormat) {
                ALOGE("%s: Stream %d: Format override not allowed for format 0x%x", __FUNCTION__,
                        streamId, dst->format);
            }
            if (dst->data_space != overrideDataSpace) {
                ALOGE("%s: Stream %d: DataSpace override not allowed for format 0x%x", __FUNCTION__,
                        streamId, dst->format);
            }
        } else {
            bool needFormatOverride =
                    requestedConfiguration3_2.streams[i].format != src.v3_2.overrideFormat;
            bool needDataspaceOverride =
                    requestedConfiguration3_2.streams[i].dataSpace != src.overrideDataSpace;
            // Override allowed with IMPLEMENTATION_DEFINED
            dstStream->setFormatOverride(needFormatOverride);
            dstStream->setDataSpaceOverride(needDataspaceOverride);
            dst->format = overrideFormat;
            dst->data_space = overrideDataSpace;
        }

        if (dst->stream_type == CAMERA_STREAM_INPUT) {
            if (src.v3_2.producerUsage != 0) {
                ALOGE("%s: Stream %d: INPUT streams must have 0 for producer usage",
                        __FUNCTION__, streamId);
                return INVALID_OPERATION;
            }
            dstStream->setUsage(
                    mapConsumerToFrameworkUsage(src.v3_2.consumerUsage));
        } else {
            // OUTPUT
            if (src.v3_2.consumerUsage != 0) {
                ALOGE("%s: Stream %d: OUTPUT streams must have 0 for consumer usage",
                        __FUNCTION__, streamId);
                return INVALID_OPERATION;
            }
            dstStream->setUsage(
                    mapProducerToFrameworkUsage(src.v3_2.producerUsage));
        }
        dst->max_buffers = src.v3_2.maxBuffers;
    }

    return res;
}

status_t HidlCamera3Device::HidlHalInterface::configureInjectedStreams(
        const camera_metadata_t* sessionParams, camera_stream_configuration* config,
        const std::vector<uint32_t>& bufferSizes,
        const CameraMetadata& cameraCharacteristics) {
    ATRACE_NAME("InjectionCameraHal::configureStreams");
    if (!valid()) return INVALID_OPERATION;
    status_t res = OK;

    if (config->input_is_multi_resolution) {
        ALOGE("%s: Injection camera device doesn't support multi-resolution input "
                "stream", __FUNCTION__);
        return BAD_VALUE;
    }

    // Convert stream config to HIDL
    std::set<int> activeStreams;
    device::V3_2::StreamConfiguration requestedConfiguration3_2;
    device::V3_4::StreamConfiguration requestedConfiguration3_4;
    device::V3_7::StreamConfiguration requestedConfiguration3_7;
    requestedConfiguration3_2.streams.resize(config->num_streams);
    requestedConfiguration3_4.streams.resize(config->num_streams);
    requestedConfiguration3_7.streams.resize(config->num_streams);
    for (size_t i = 0; i < config->num_streams; i++) {
        device::V3_2::Stream& dst3_2 = requestedConfiguration3_2.streams[i];
        device::V3_4::Stream& dst3_4 = requestedConfiguration3_4.streams[i];
        device::V3_7::Stream& dst3_7 = requestedConfiguration3_7.streams[i];
        camera3::camera_stream_t* src = config->streams[i];

        Camera3Stream* cam3stream = Camera3Stream::cast(src);
        cam3stream->setBufferFreedListener(this);
        int streamId = cam3stream->getId();
        StreamType streamType;
        switch (src->stream_type) {
            case CAMERA_STREAM_OUTPUT:
                streamType = StreamType::OUTPUT;
                break;
            case CAMERA_STREAM_INPUT:
                streamType = StreamType::INPUT;
                break;
            default:
                ALOGE("%s: Stream %d: Unsupported stream type %d", __FUNCTION__,
                        streamId, config->streams[i]->stream_type);
            return BAD_VALUE;
        }
        dst3_2.id = streamId;
        dst3_2.streamType = streamType;
        dst3_2.width = src->width;
        dst3_2.height = src->height;
        dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage());
        dst3_2.rotation =
                mapToStreamRotation((camera_stream_rotation_t)src->rotation);
        // For HidlSession version 3.5 or newer, the format and dataSpace sent
        // to HAL are original, not the overridden ones.
        if (mHidlSession_3_5 != nullptr) {
            dst3_2.format = mapToPixelFormat(cam3stream->isFormatOverridden()
                                            ? cam3stream->getOriginalFormat()
                                            : src->format);
            dst3_2.dataSpace =
                    mapToHidlDataspace(cam3stream->isDataSpaceOverridden()
                                    ? cam3stream->getOriginalDataSpace()
                                    : src->data_space);
        } else {
            dst3_2.format = mapToPixelFormat(src->format);
            dst3_2.dataSpace = mapToHidlDataspace(src->data_space);
        }
        dst3_4.v3_2 = dst3_2;
        dst3_4.bufferSize = bufferSizes[i];
        if (!src->physical_camera_id.empty()) {
            dst3_4.physicalCameraId = src->physical_camera_id;
        }
        dst3_7.v3_4 = dst3_4;
        dst3_7.groupId = cam3stream->getHalStreamGroupId();
        dst3_7.sensorPixelModesUsed.resize(src->sensor_pixel_modes_used.size());
        size_t j = 0;
        for (int mode : src->sensor_pixel_modes_used) {
            dst3_7.sensorPixelModesUsed[j++] =
                    static_cast<CameraMetadataEnumAndroidSensorPixelMode>(mode);
        }
        activeStreams.insert(streamId);
        // Create Buffer ID map if necessary
        mBufferRecords.tryCreateBufferCache(streamId);
    }
    // remove BufferIdMap for deleted streams
    mBufferRecords.removeInactiveBufferCaches(activeStreams);

    StreamConfigurationMode operationMode;
    res = mapToStreamConfigurationMode(
            (camera_stream_configuration_mode_t)config->operation_mode,
            /*out*/ &operationMode);
    if (res != OK) {
        return res;
    }
    requestedConfiguration3_7.operationMode = operationMode;
    size_t sessionParamSize = get_camera_metadata_size(sessionParams);
    requestedConfiguration3_7.operationMode = operationMode;
    requestedConfiguration3_7.sessionParams.setToExternal(
            reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
            sessionParamSize);

    // See which version of HAL we have
    if (mHidlSession_3_7 != nullptr) {
        requestedConfiguration3_7.streamConfigCounter = mNextStreamConfigCounter++;
        requestedConfiguration3_7.multiResolutionInputImage =
                config->input_is_multi_resolution;

        const camera_metadata_t* rawMetadata = cameraCharacteristics.getAndLock();
        ::android::hardware::camera::device::V3_2::CameraMetadata hidlChars = {};
        hidlChars.setToExternal(
                reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(rawMetadata)),
                get_camera_metadata_size(rawMetadata));
        cameraCharacteristics.unlock(rawMetadata);

        sp<hardware::camera::device::V3_7::ICameraInjectionSession>
                hidlInjectionSession_3_7;
        auto castInjectionResult_3_7 =
                device::V3_7::ICameraInjectionSession::castFrom(mHidlSession_3_7);
        if (castInjectionResult_3_7.isOk()) {
            hidlInjectionSession_3_7 = castInjectionResult_3_7;
        } else {
            ALOGE("%s: Transaction error: %s", __FUNCTION__,
                    castInjectionResult_3_7.description().c_str());
            return DEAD_OBJECT;
        }

        auto err = hidlInjectionSession_3_7->configureInjectionStreams(
                requestedConfiguration3_7, hidlChars);
        if (!err.isOk()) {
            ALOGE("%s: Transaction error: %s", __FUNCTION__,
                    err.description().c_str());
            return DEAD_OBJECT;
        }
    } else {
        ALOGE("%s: mHidlSession_3_7 does not exist, the lowest version of injection "
                "session is 3.7", __FUNCTION__);
        return DEAD_OBJECT;
    }

    return res;
}

status_t HidlCamera3Device::HidlHalInterface::wrapAsHidlRequest(camera_capture_request_t* request,
        /*out*/device::V3_2::CaptureRequest* captureRequest,
        /*out*/std::vector<native_handle_t*>* handlesCreated,
        /*out*/std::vector<std::pair<int32_t, int32_t>>* inflightBuffers) {
    ATRACE_CALL();
    if (captureRequest == nullptr || handlesCreated == nullptr || inflightBuffers == nullptr) {
        ALOGE("%s: captureRequest (%p), handlesCreated (%p), and inflightBuffers(%p) "
                "must not be null", __FUNCTION__, captureRequest, handlesCreated, inflightBuffers);
        return BAD_VALUE;
    }

    captureRequest->frameNumber = request->frame_number;

    captureRequest->fmqSettingsSize = 0;

    {
        if (request->input_buffer != nullptr) {
            int32_t streamId = Camera3Stream::cast(request->input_buffer->stream)->getId();
            buffer_handle_t buf = *(request->input_buffer->buffer);
            auto pair = getBufferId(buf, streamId);
            bool isNewBuffer = pair.first;
            uint64_t bufferId = pair.second;
            captureRequest->inputBuffer.streamId = streamId;
            captureRequest->inputBuffer.bufferId = bufferId;
            captureRequest->inputBuffer.buffer = (isNewBuffer) ? buf : nullptr;
            captureRequest->inputBuffer.status = BufferStatus::OK;
            native_handle_t *acquireFence = nullptr;
            if (request->input_buffer->acquire_fence != -1) {
                acquireFence = native_handle_create(1,0);
                acquireFence->data[0] = request->input_buffer->acquire_fence;
                handlesCreated->push_back(acquireFence);
            }
            captureRequest->inputBuffer.acquireFence = acquireFence;
            captureRequest->inputBuffer.releaseFence = nullptr;

            mBufferRecords.pushInflightBuffer(captureRequest->frameNumber, streamId,
                    request->input_buffer->buffer);
            inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId));
        } else {
            captureRequest->inputBuffer.streamId = -1;
            captureRequest->inputBuffer.bufferId = BUFFER_ID_NO_BUFFER;
        }

        captureRequest->outputBuffers.resize(request->num_output_buffers);
        for (size_t i = 0; i < request->num_output_buffers; i++) {
            const camera_stream_buffer_t *src = request->output_buffers + i;
            StreamBuffer &dst = captureRequest->outputBuffers[i];
            int32_t streamId = Camera3Stream::cast(src->stream)->getId();
            if (src->buffer != nullptr) {
                buffer_handle_t buf = *(src->buffer);
                auto pair = getBufferId(buf, streamId);
                bool isNewBuffer = pair.first;
                dst.bufferId = pair.second;
                dst.buffer = isNewBuffer ? buf : nullptr;
                native_handle_t *acquireFence = nullptr;
                if (src->acquire_fence != -1) {
                    acquireFence = native_handle_create(1,0);
                    acquireFence->data[0] = src->acquire_fence;
                    handlesCreated->push_back(acquireFence);
                }
                dst.acquireFence = acquireFence;
            } else if (mUseHalBufManager) {
                // HAL buffer management path
                dst.bufferId = BUFFER_ID_NO_BUFFER;
                dst.buffer = nullptr;
                dst.acquireFence = nullptr;
            } else {
                ALOGE("%s: cannot send a null buffer in capture request!", __FUNCTION__);
                return BAD_VALUE;
            }
            dst.streamId = streamId;
            dst.status = BufferStatus::OK;
            dst.releaseFence = nullptr;

            // Output buffers are empty when using HAL buffer manager
            if (!mUseHalBufManager) {
                mBufferRecords.pushInflightBuffer(
                        captureRequest->frameNumber, streamId, src->buffer);
                inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId));
            }
        }
    }
    return OK;
}

status_t HidlCamera3Device::HidlHalInterface::flush() {
    ATRACE_NAME("CameraHal::flush");
    if (!valid()) return INVALID_OPERATION;
    status_t res = OK;

    auto err = mHidlSession->flush();
    if (!err.isOk()) {
        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
        res = DEAD_OBJECT;
    } else {
        res = HidlProviderInfo::mapToStatusT(err);
    }

    return res;
}

status_t HidlCamera3Device::HidlHalInterface::dump(int /*fd*/) {
    ATRACE_NAME("CameraHal::dump");
    if (!valid()) return INVALID_OPERATION;

    // Handled by CameraProviderManager::dump

    return OK;
}

status_t HidlCamera3Device::HidlHalInterface::repeatingRequestEnd(uint32_t /*frameNumber*/,
        const std::vector<int32_t> &/*streamIds*/) {
    ATRACE_NAME("CameraHal::repeatingRequestEnd");
    return INVALID_OPERATION;
}

status_t HidlCamera3Device::HidlHalInterface::close() {
    ATRACE_NAME("CameraHal::close()");
    if (!valid()) return INVALID_OPERATION;
    status_t res = OK;

    auto err = mHidlSession->close();
    // Interface will be dead shortly anyway, so don't log errors
    if (!err.isOk()) {
        res = DEAD_OBJECT;
    }

    return res;
}

void HidlCamera3Device::HidlHalInterface::signalPipelineDrain(const std::vector<int>& streamIds) {
    ATRACE_NAME("CameraHal::signalPipelineDrain");
    if (!valid() || mHidlSession_3_5 == nullptr) {
        ALOGE("%s called on invalid camera!", __FUNCTION__);
        return;
    }

    auto err = mHidlSession_3_5->signalStreamFlush(streamIds, mNextStreamConfigCounter - 1);
    if (!err.isOk()) {
        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
        return;
    }
}

status_t HidlCamera3Device::HidlHalInterface::processBatchCaptureRequests(
        std::vector<camera_capture_request_t*>& requests,/*out*/uint32_t* numRequestProcessed) {
    ATRACE_NAME("CameraHal::processBatchCaptureRequests");
    if (!valid()) return INVALID_OPERATION;

    sp<device::V3_4::ICameraDeviceSession> hidlSession_3_4;
    sp<device::V3_7::ICameraDeviceSession> hidlSession_3_7;
    auto castResult_3_7 = device::V3_7::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_7.isOk()) {
        hidlSession_3_7 = castResult_3_7;
    }
    auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
    if (castResult_3_4.isOk()) {
        hidlSession_3_4 = castResult_3_4;
    }

    hardware::hidl_vec<device::V3_2::CaptureRequest> captureRequests;
    hardware::hidl_vec<device::V3_4::CaptureRequest> captureRequests_3_4;
    hardware::hidl_vec<device::V3_7::CaptureRequest> captureRequests_3_7;
    size_t batchSize = requests.size();
    if (hidlSession_3_7 != nullptr) {
        captureRequests_3_7.resize(batchSize);
    } else if (hidlSession_3_4 != nullptr) {
        captureRequests_3_4.resize(batchSize);
    } else {
        captureRequests.resize(batchSize);
    }
    std::vector<native_handle_t*> handlesCreated;
    std::vector<std::pair<int32_t, int32_t>> inflightBuffers;

    status_t res = OK;
    for (size_t i = 0; i < batchSize; i++) {
        if (hidlSession_3_7 != nullptr) {
            res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_7[i].v3_4.v3_2,
                    /*out*/&handlesCreated, /*out*/&inflightBuffers);
        } else if (hidlSession_3_4 != nullptr) {
            res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
                    /*out*/&handlesCreated, /*out*/&inflightBuffers);
        } else {
            res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i],
                    /*out*/&handlesCreated, /*out*/&inflightBuffers);
        }
        if (res != OK) {
            mBufferRecords.popInflightBuffers(inflightBuffers);
            cleanupNativeHandles(&handlesCreated);
            return res;
        }
    }

    std::vector<device::V3_2::BufferCache> cachesToRemove;
    {
        std::lock_guard<std::mutex> lock(mFreedBuffersLock);
        for (auto& pair : mFreedBuffers) {
            // The stream might have been removed since onBufferFreed
            if (mBufferRecords.isStreamCached(pair.first)) {
                cachesToRemove.push_back({pair.first, pair.second});
            }
        }
        mFreedBuffers.clear();
    }

    common::V1_0::Status status = common::V1_0::Status::INTERNAL_ERROR;
    *numRequestProcessed = 0;

    // Write metadata to FMQ.
    for (size_t i = 0; i < batchSize; i++) {
        camera_capture_request_t* request = requests[i];
        device::V3_2::CaptureRequest* captureRequest;
        if (hidlSession_3_7 != nullptr) {
            captureRequest = &captureRequests_3_7[i].v3_4.v3_2;
        } else if (hidlSession_3_4 != nullptr) {
            captureRequest = &captureRequests_3_4[i].v3_2;
        } else {
            captureRequest = &captureRequests[i];
        }

        if (request->settings != nullptr) {
            size_t settingsSize = get_camera_metadata_size(request->settings);
            if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
                    reinterpret_cast<const uint8_t*>(request->settings), settingsSize)) {
                captureRequest->settings.resize(0);
                captureRequest->fmqSettingsSize = settingsSize;
            } else {
                if (mRequestMetadataQueue != nullptr) {
                    ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
                }
                captureRequest->settings.setToExternal(
                        reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
                                request->settings)),
                        get_camera_metadata_size(request->settings));
                captureRequest->fmqSettingsSize = 0u;
            }
        } else {
            // A null request settings maps to a size-0 CameraMetadata
            captureRequest->settings.resize(0);
            captureRequest->fmqSettingsSize = 0u;
        }

        // hidl session 3.7 specific handling.
        if (hidlSession_3_7 != nullptr) {
            captureRequests_3_7[i].inputWidth = request->input_width;
            captureRequests_3_7[i].inputHeight = request->input_height;
        }

        // hidl session 3.7 and 3.4 specific handling.
        if (hidlSession_3_7 != nullptr || hidlSession_3_4 != nullptr) {
            hardware::hidl_vec<device::V3_4::PhysicalCameraSetting>& physicalCameraSettings =
                    (hidlSession_3_7 != nullptr) ?
                    captureRequests_3_7[i].v3_4.physicalCameraSettings :
                    captureRequests_3_4[i].physicalCameraSettings;
            physicalCameraSettings.resize(request->num_physcam_settings);
            for (size_t j = 0; j < request->num_physcam_settings; j++) {
                if (request->physcam_settings != nullptr) {
                    size_t settingsSize = get_camera_metadata_size(request->physcam_settings[j]);
                    if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
                                reinterpret_cast<const uint8_t*>(request->physcam_settings[j]),
                                settingsSize)) {
                        physicalCameraSettings[j].settings.resize(0);
                        physicalCameraSettings[j].fmqSettingsSize = settingsSize;
                    } else {
                        if (mRequestMetadataQueue != nullptr) {
                            ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
                        }
                        physicalCameraSettings[j].settings.setToExternal(
                                reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
                                        request->physcam_settings[j])),
                                get_camera_metadata_size(request->physcam_settings[j]));
                        physicalCameraSettings[j].fmqSettingsSize = 0u;
                    }
                } else {
                    physicalCameraSettings[j].fmqSettingsSize = 0u;
                    physicalCameraSettings[j].settings.resize(0);
                }
                physicalCameraSettings[j].physicalCameraId = request->physcam_id[j];
            }
        }
    }

    hardware::details::return_status err;
    auto resultCallback =
        [&status, &numRequestProcessed] (auto s, uint32_t n) {
                status = s;
                *numRequestProcessed = n;
        };
    if (hidlSession_3_7 != nullptr) {
        err = hidlSession_3_7->processCaptureRequest_3_7(captureRequests_3_7, cachesToRemove,
                                                         resultCallback);
    } else if (hidlSession_3_4 != nullptr) {
        err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove,
                                                         resultCallback);
    } else {
        err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
                                                  resultCallback);
    }
    if (!err.isOk()) {
        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
        status = common::V1_0::Status::CAMERA_DISCONNECTED;
    }

    if (status == common::V1_0::Status::OK && *numRequestProcessed != batchSize) {
        ALOGE("%s: processCaptureRequest returns OK but processed %d/%zu requests",
                __FUNCTION__, *numRequestProcessed, batchSize);
        status = common::V1_0::Status::INTERNAL_ERROR;
    }

    res = HidlProviderInfo::mapToStatusT(status);
    if (res == OK) {
        if (mHidlSession->isRemote()) {
            // Only close acquire fence FDs when the HIDL transaction succeeds (so the FDs have been
            // sent to camera HAL processes)
            cleanupNativeHandles(&handlesCreated, /*closeFd*/true);
        } else {
            // In passthrough mode the FDs are now owned by HAL
            cleanupNativeHandles(&handlesCreated);
        }
    } else {
        mBufferRecords.popInflightBuffers(inflightBuffers);
        cleanupNativeHandles(&handlesCreated);
    }
    return res;
}

status_t HidlCamera3Device::HidlHalInterface::switchToOffline(
        const std::vector<int32_t>& streamsToKeep,
        /*out*/hardware::camera::device::V3_6::CameraOfflineSessionInfo* offlineSessionInfo,
        /*out*/sp<hardware::camera::device::V3_6::ICameraOfflineSession>* offlineSession,
        /*out*/camera3::BufferRecords* bufferRecords) {
    ATRACE_NAME("CameraHal::switchToOffline");
    if (!valid() || mHidlSession_3_6 == nullptr) {
        ALOGE("%s called on invalid camera!", __FUNCTION__);
        return INVALID_OPERATION;
    }

    if (offlineSessionInfo == nullptr || offlineSession == nullptr || bufferRecords == nullptr) {
        ALOGE("%s: output arguments must not be null!", __FUNCTION__);
        return INVALID_OPERATION;
    }

    common::V1_0::Status status = common::V1_0::Status::INTERNAL_ERROR;
    auto resultCallback =
        [&status, &offlineSessionInfo, &offlineSession] (auto s, auto info, auto session) {
                status = s;
                *offlineSessionInfo = info;
                *offlineSession = session;
        };
    auto err = mHidlSession_3_6->switchToOffline(streamsToKeep, resultCallback);

    if (!err.isOk()) {
        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
        return DEAD_OBJECT;
    }

    status_t ret = HidlProviderInfo::mapToStatusT(status);
    if (ret != OK) {
        return ret;
    }

    return verifyBufferCaches(offlineSessionInfo, bufferRecords);
}

HidlCamera3Device::HidlRequestThread::HidlRequestThread(wp<Camera3Device> parent,
                sp<camera3::StatusTracker> statusTracker,
                sp<HalInterface> interface,
                const Vector<int32_t>& sessionParamKeys,
                bool useHalBufManager,
                bool supportCameraMute,
                int rotationOverride,
                bool supportSettingsOverride) :
          RequestThread(parent, statusTracker, interface, sessionParamKeys, useHalBufManager,
                  supportCameraMute, rotationOverride, supportSettingsOverride) {}

status_t HidlCamera3Device::HidlRequestThread::switchToOffline(
        const std::vector<int32_t>& streamsToKeep,
        /*out*/hardware::camera::device::V3_6::CameraOfflineSessionInfo* offlineSessionInfo,
        /*out*/sp<hardware::camera::device::V3_6::ICameraOfflineSession>* offlineSession,
        /*out*/camera3::BufferRecords* bufferRecords) {
    Mutex::Autolock l(mRequestLock);
    clearRepeatingRequestsLocked(/*lastFrameNumber*/nullptr);

    // Wait until request thread is fully stopped
    // TBD: check if request thread is being paused by other APIs (shouldn't be)

    // We could also check for mRepeatingRequests.empty(), but the API interface
    // is serialized by Camera3Device::mInterfaceLock so no one should be able to submit any
    // new requests during the call; hence skip that check.
    bool queueEmpty = mNextRequests.empty() && mRequestQueue.empty();
    while (!queueEmpty) {
        status_t res = mRequestSubmittedSignal.waitRelative(mRequestLock, kRequestSubmitTimeout);
        if (res == TIMED_OUT) {
            ALOGE("%s: request thread failed to submit one request within timeout!", __FUNCTION__);
            return res;
        } else if (res != OK) {
            ALOGE("%s: request thread failed to submit a request: %s (%d)!",
                    __FUNCTION__, strerror(-res), res);
            return res;
        }
        queueEmpty = mNextRequests.empty() && mRequestQueue.empty();
    }
    return (static_cast<HidlHalInterface *>(mInterface.get()))->switchToOffline(
            streamsToKeep, offlineSessionInfo, offlineSession, bufferRecords);
}

status_t HidlCamera3Device::HidlCamera3DeviceInjectionMethods::injectionInitialize(
        const std::string& injectedCamId, sp<CameraProviderManager> manager,
        const sp<android::hardware::camera::device::V3_2::ICameraDeviceCallback>&
                callback) {
    ATRACE_CALL();
    Mutex::Autolock lock(mInjectionLock);

    if (manager == nullptr) {
        ALOGE("%s: manager does not exist!", __FUNCTION__);
        return INVALID_OPERATION;
    }

    sp<Camera3Device> parent = mParent.promote();
    if (parent == nullptr) {
        ALOGE("%s: parent does not exist!", __FUNCTION__);
        return INVALID_OPERATION;
    }

    mInjectedCamId = injectedCamId;
    sp<ICameraDeviceSession> session;
    ATRACE_BEGIN("Injection CameraHal::openSession");
    status_t res = manager->openHidlSession(injectedCamId, callback,
                                          /*out*/ &session);
    ATRACE_END();
    if (res != OK) {
        ALOGE("Injection camera could not open camera session: %s (%d)",
                strerror(-res), res);
        return res;
    }

    std::shared_ptr<RequestMetadataQueue> queue;
    auto requestQueueRet =
        session->getCaptureRequestMetadataQueue([&queue](const auto& descriptor) {
            queue = std::make_shared<RequestMetadataQueue>(descriptor);
            if (!queue->isValid() || queue->availableToWrite() <= 0) {
                ALOGE("Injection camera HAL returns empty request metadata fmq, not "
                        "use it");
                queue = nullptr;
                // don't use the queue onwards.
            }
        });
    if (!requestQueueRet.isOk()) {
        ALOGE("Injection camera transaction error when getting request metadata fmq: "
                "%s, not use it", requestQueueRet.description().c_str());
        return DEAD_OBJECT;
    }

    std::unique_ptr<ResultMetadataQueue>& resQueue = mInjectionResultMetadataQueue;
    auto resultQueueRet = session->getCaptureResultMetadataQueue(
        [&resQueue](const auto& descriptor) {
            resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
            if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
                ALOGE("Injection camera HAL returns empty result metadata fmq, not use "
                        "it");
                resQueue = nullptr;
                // Don't use the resQueue onwards.
            }
        });
    if (!resultQueueRet.isOk()) {
        ALOGE("Injection camera transaction error when getting result metadata queue "
                "from camera session: %s", resultQueueRet.description().c_str());
        return DEAD_OBJECT;
    }
    IF_ALOGV() {
        session->interfaceChain(
                [](::android::hardware::hidl_vec<::android::hardware::hidl_string>
                        interfaceChain) {
                        ALOGV("Injection camera session interface chain:");
                        for (const auto& iface : interfaceChain) {
                            ALOGV("  %s", iface.c_str());
                        }
                });
    }

    ALOGV("%s: Injection camera interface = new HalInterface()", __FUNCTION__);

    mInjectedCamHalInterface =
            new HidlHalInterface(session, queue, parent->mUseHalBufManager,
                       parent->mSupportOfflineProcessing);
    if (mInjectedCamHalInterface == nullptr) {
        ALOGE("%s: mInjectedCamHalInterface does not exist!", __FUNCTION__);
        return DEAD_OBJECT;
    }

    return OK;
}

status_t HidlCamera3Device::HidlCamera3DeviceInjectionMethods::replaceHalInterface(
        sp<HalInterface> newHalInterface, bool keepBackup) {
    Mutex::Autolock lock(mInjectionLock);
    if (newHalInterface.get() == nullptr) {
        ALOGE("%s: The newHalInterface does not exist, to stop replacing.",
                __FUNCTION__);
        return DEAD_OBJECT;
    }

    sp<Camera3Device> parent = mParent.promote();
    if (parent == nullptr) {
        ALOGE("%s: parent does not exist!", __FUNCTION__);
        return INVALID_OPERATION;
    }
    if (newHalInterface->getTransportType() != IPCTransport::HIDL) {
        ALOGE("%s Replacing HIDL HalInterface with another transport unsupported", __FUNCTION__);
        return INVALID_OPERATION;
    }

    HidlCamera3Device *hidlParent = static_cast<HidlCamera3Device *>(parent.get());
    if (keepBackup) {
        if (mBackupHalInterface == nullptr) {
            mBackupHalInterface = parent->mInterface;
        }
        if (mBackupResultMetadataQueue == nullptr) {
            mBackupResultMetadataQueue = std::move(hidlParent->mResultMetadataQueue);
            hidlParent->mResultMetadataQueue = std::move(mInjectionResultMetadataQueue);
        }
    } else {
        mBackupHalInterface = nullptr;
        hidlParent->mResultMetadataQueue = std::move(mBackupResultMetadataQueue);
        mBackupResultMetadataQueue = nullptr;
    }
    parent->mInterface = newHalInterface;

    return OK;
}

}; // namespace android
