Camera: add external camera@3.5

External camera@3.5 supports the new buffer management
API that allows HAL to request buffer when needed to
optimize the memory usage.

Test: smoke test TestingCamera
Bug: 109829698
Change-Id: Icfbb75a26d8e92e4eb82a680e94e183041a5174d
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index e7361dd..b96f574 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -48,24 +48,31 @@
 } // anonymous namespace
 
 ExternalCameraDevice::ExternalCameraDevice(
-            const std::string& cameraId, const ExternalCameraConfig& cfg) :
+        const std::string& cameraId, const ExternalCameraConfig& cfg) :
         mCameraId(cameraId),
-        mCfg(cfg) {
-
-    status_t ret = initCameraCharacteristics();
-    if (ret != OK) {
-        ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret);
-        mInitFailed = true;
-    }
-}
+        mCfg(cfg) {}
 
 ExternalCameraDevice::~ExternalCameraDevice() {}
 
 bool ExternalCameraDevice::isInitFailed() {
+    Mutex::Autolock _l(mLock);
+    return isInitFailedLocked();
+}
+
+bool ExternalCameraDevice::isInitFailedLocked() {
+    if (!mInitialized) {
+        status_t ret = initCameraCharacteristics();
+        if (ret != OK) {
+            ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret);
+            mInitFailed = true;
+        }
+        mInitialized = true;
+    }
     return mInitFailed;
 }
 
-Return<void> ExternalCameraDevice::getResourceCost(getResourceCost_cb _hidl_cb) {
+Return<void> ExternalCameraDevice::getResourceCost(
+        ICameraDevice::getResourceCost_cb _hidl_cb) {
     CameraResourceCost resCost;
     resCost.resourceCost = 100;
     _hidl_cb(Status::OK, resCost);
@@ -73,11 +80,11 @@
 }
 
 Return<void> ExternalCameraDevice::getCameraCharacteristics(
-        getCameraCharacteristics_cb _hidl_cb) {
+        ICameraDevice::getCameraCharacteristics_cb _hidl_cb) {
     Mutex::Autolock _l(mLock);
     V3_2::CameraMetadata hidlChars;
 
-    if (isInitFailed()) {
+    if (isInitFailedLocked()) {
         _hidl_cb(Status::INTERNAL_ERROR, hidlChars);
         return Void();
     }
@@ -94,7 +101,7 @@
 }
 
 Return<void> ExternalCameraDevice::open(
-        const sp<ICameraDeviceCallback>& callback, open_cb _hidl_cb) {
+        const sp<ICameraDeviceCallback>& callback, ICameraDevice::open_cb _hidl_cb) {
     Status status = Status::OK;
     sp<ExternalCameraDeviceSession> session = nullptr;
 
@@ -143,7 +150,7 @@
         }
     }
 
-    session = new ExternalCameraDeviceSession(
+    session = createSession(
             callback, mCfg, mSupportedFormats, mCroppingType,
             mCameraCharacteristics, mCameraId, std::move(fd));
     if (session == nullptr) {
@@ -479,52 +486,9 @@
     UPDATE(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, availableResultKeys,
            ARRAY_SIZE(availableResultKeys));
 
-    const int32_t availableCharacteristicsKeys[] = {
-        ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
-        ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
-        ANDROID_CONTROL_AE_AVAILABLE_MODES,
-        ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-        ANDROID_CONTROL_AE_COMPENSATION_RANGE,
-        ANDROID_CONTROL_AE_COMPENSATION_STEP,
-        ANDROID_CONTROL_AE_LOCK_AVAILABLE,
-        ANDROID_CONTROL_AF_AVAILABLE_MODES,
-        ANDROID_CONTROL_AVAILABLE_EFFECTS,
-        ANDROID_CONTROL_AVAILABLE_MODES,
-        ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
-        ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
-        ANDROID_CONTROL_AWB_AVAILABLE_MODES,
-        ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
-        ANDROID_CONTROL_MAX_REGIONS,
-        ANDROID_FLASH_INFO_AVAILABLE,
-        ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
-        ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
-        ANDROID_LENS_FACING,
-        ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
-        ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
-        ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
-        ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
-        ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
-        ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
-        ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
-        ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
-        ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
-        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
-        ANDROID_SCALER_CROPPING_TYPE,
-        ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
-        ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
-        ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
-        ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
-        ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
-        ANDROID_SENSOR_ORIENTATION,
-        ANDROID_SHADING_AVAILABLE_MODES,
-        ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
-        ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
-        ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
-        ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
-        ANDROID_SYNC_MAX_LATENCY};
     UPDATE(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
-           availableCharacteristicsKeys,
-           ARRAY_SIZE(availableCharacteristicsKeys));
+           AVAILABLE_CHARACTERISTICS_KEYS_3_4.data(),
+           AVAILABLE_CHARACTERISTICS_KEYS_3_4.size());
 
     return OK;
 }
@@ -931,6 +895,18 @@
     }
 }
 
+sp<ExternalCameraDeviceSession> ExternalCameraDevice::createSession(
+        const sp<ICameraDeviceCallback>& cb,
+        const ExternalCameraConfig& cfg,
+        const std::vector<SupportedV4L2Format>& sortedFormats,
+        const CroppingType& croppingType,
+        const common::V1_0::helper::CameraMetadata& chars,
+        const std::string& cameraId,
+        unique_fd v4l2Fd) {
+    return new ExternalCameraDeviceSession(
+            cb, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd));
+}
+
 }  // namespace implementation
 }  // namespace V3_4
 }  // namespace device
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 350f48b..5824be0 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -81,6 +81,8 @@
     return locked;
 }
 
+buffer_handle_t sEmptyBuffer = nullptr;
+
 } // Anonymous namespace
 
 // Static instances
@@ -103,11 +105,8 @@
         mCroppingType(croppingType),
         mCameraId(cameraId),
         mV4l2Fd(std::move(v4l2Fd)),
-        mOutputThread(new OutputThread(this, mCroppingType)),
         mMaxThumbResolution(getMaxThumbResolution()),
-        mMaxJpegResolution(getMaxJpegResolution()) {
-    mInitFail = initialize();
-}
+        mMaxJpegResolution(getMaxJpegResolution()) {}
 
 bool ExternalCameraDeviceSession::initialize() {
     if (mV4l2Fd.get() < 0) {
@@ -142,6 +141,12 @@
             model = card;
         }
     }
+
+    initOutputThread();
+    if (mOutputThread == nullptr) {
+        ALOGE("%s: init OutputThread failed!", __FUNCTION__);
+        return true;
+    }
     mOutputThread->setExifMakeModel(make, model);
 
     status_t status = initDefaultRequests();
@@ -168,6 +173,32 @@
     return false;
 }
 
+bool ExternalCameraDeviceSession::isInitFailed() {
+    Mutex::Autolock _l(mLock);
+    if (!mInitialized) {
+        mInitFail = initialize();
+        mInitialized = true;
+    }
+    return mInitFail;
+}
+
+void ExternalCameraDeviceSession::initOutputThread() {
+    mOutputThread = new OutputThread(this, mCroppingType);
+}
+
+void ExternalCameraDeviceSession::closeOutputThread() {
+    closeOutputThreadImpl();
+}
+
+void ExternalCameraDeviceSession::closeOutputThreadImpl() {
+    if (mOutputThread) {
+        mOutputThread->flush();
+        mOutputThread->requestExit();
+        mOutputThread->join();
+        mOutputThread.clear();
+    }
+}
+
 Status ExternalCameraDeviceSession::initStatus() const {
     Mutex::Autolock _l(mLock);
     Status status = Status::OK;
@@ -181,7 +212,7 @@
 ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {
     if (!isClosed()) {
         ALOGE("ExternalCameraDeviceSession deleted before close!");
-        close();
+        close(/*callerIsDtor*/true);
     }
 }
 
@@ -442,18 +473,23 @@
     return Status::OK;
 }
 
-Return<void> ExternalCameraDeviceSession::close() {
+Return<void> ExternalCameraDeviceSession::close(bool callerIsDtor) {
     Mutex::Autolock _il(mInterfaceLock);
     bool closed = isClosed();
     if (!closed) {
-        mOutputThread->flush();
-        mOutputThread->requestExit();
-        mOutputThread->join();
+        if (callerIsDtor) {
+            closeOutputThreadImpl();
+        } else {
+            closeOutputThread();
+        }
 
         Mutex::Autolock _l(mLock);
         // free all buffers
-        for(auto pair : mStreamMap) {
-            cleanupBuffersLocked(/*Stream ID*/pair.first);
+        {
+            Mutex::Autolock _l(mCbsLock);
+            for(auto pair : mStreamMap) {
+                cleanupBuffersLocked(/*Stream ID*/pair.first);
+            }
         }
         v4l2StreamOffLocked();
         ALOGV("%s: closing V4L2 camera FD %d", __FUNCTION__, mV4l2Fd.get());
@@ -463,10 +499,61 @@
     return Void();
 }
 
-Status ExternalCameraDeviceSession::importRequest(
+Status ExternalCameraDeviceSession::importRequestLocked(
+    const CaptureRequest& request,
+    hidl_vec<buffer_handle_t*>& allBufPtrs,
+    hidl_vec<int>& allFences) {
+    return importRequestLockedImpl(request, allBufPtrs, allFences);
+}
+
+Status ExternalCameraDeviceSession::importBuffer(int32_t streamId,
+        uint64_t bufId, buffer_handle_t buf,
+        /*out*/buffer_handle_t** outBufPtr,
+        bool allowEmptyBuf) {
+    Mutex::Autolock _l(mCbsLock);
+    return importBufferLocked(streamId, bufId, buf, outBufPtr, allowEmptyBuf);
+}
+
+Status ExternalCameraDeviceSession::importBufferLocked(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;
+        }
+    }
+
+    CirculatingBuffers& cbs = mCirculatingBuffers[streamId];
+    if (cbs.count(bufId) == 0) {
+        if (buf == nullptr) {
+            ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+            return Status::ILLEGAL_ARGUMENT;
+        }
+        // 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 ExternalCameraDeviceSession::importRequestLockedImpl(
         const CaptureRequest& request,
         hidl_vec<buffer_handle_t*>& allBufPtrs,
-        hidl_vec<int>& allFences) {
+        hidl_vec<int>& allFences,
+        bool allowEmptyBuf) {
     size_t numOutputBufs = request.outputBuffers.size();
     size_t numBufs = numOutputBufs;
     // Validate all I/O buffers
@@ -485,26 +572,17 @@
         streamIds[i] = request.outputBuffers[i].streamId;
     }
 
-    for (size_t i = 0; i < numBufs; i++) {
-        buffer_handle_t buf = allBufs[i];
-        uint64_t bufId = allBufIds[i];
-        CirculatingBuffers& cbs = mCirculatingBuffers[streamIds[i]];
-        if (cbs.count(bufId) == 0) {
-            if (buf == nullptr) {
-                ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
-                return Status::ILLEGAL_ARGUMENT;
-            }
-            // Register a newly seen buffer
-            buffer_handle_t importedBuf = buf;
-            sHandleImporter.importBuffer(importedBuf);
-            if (importedBuf == nullptr) {
-                ALOGE("%s: output buffer %zu is invalid!", __FUNCTION__, i);
-                return Status::INTERNAL_ERROR;
-            } else {
-                cbs[bufId] = importedBuf;
+    {
+        Mutex::Autolock _l(mCbsLock);
+        for (size_t i = 0; i < numBufs; i++) {
+            Status st = importBufferLocked(
+                    streamIds[i], allBufIds[i], allBufs[i], &allBufPtrs[i],
+                    allowEmptyBuf);
+            if (st != Status::OK) {
+                // Detailed error logs printed in importBuffer
+                return st;
             }
         }
-        allBufPtrs[i] = &cbs[bufId];
     }
 
     // All buffers are imported. Now validate output buffer acquire fences
@@ -652,7 +730,7 @@
         }
     }
 
-    status = importRequest(request, allBufPtrs, allFences);
+    status = importRequestLocked(request, allBufPtrs, allFences);
     if (status != Status::OK) {
         return status;
     }
@@ -775,9 +853,11 @@
         result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
         if (req->buffers[i].fenceTimeout) {
             result.outputBuffers[i].status = BufferStatus::ERROR;
-            native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
-            handle->data[0] = req->buffers[i].acquireFence;
-            result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+            if (req->buffers[i].acquireFence > 0) {
+                native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+                handle->data[0] = req->buffers[i].acquireFence;
+                result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+            }
             notifyError(req->frameNumber, req->buffers[i].streamId, ErrorCode::ERROR_BUFFER);
         } else {
             result.outputBuffers[i].status = BufferStatus::OK;
@@ -1747,6 +1827,12 @@
                 (req->frameIn->mFourcc >> 24) & 0xFF);
     }
 
+    int res = requestBufferStart(req->buffers);
+    if (res != 0) {
+        ALOGE("%s: send BufferRequest failed! res %d", __FUNCTION__, res);
+        return onDeviceError("%s: failed to send buffer request!", __FUNCTION__);
+    }
+
     std::unique_lock<std::mutex> lk(mBufferLock);
     // Convert input V4L2 frame to YU12 of the same size
     // TODO: see if we can save some computation by converting to YV12 here
@@ -1755,7 +1841,7 @@
     req->frameIn->map(&inData, &inDataSize);
     // TODO: in some special case maybe we can decode jpg directly to gralloc output?
     ATRACE_BEGIN("MJPGtoI420");
-    int res = libyuv::MJPGToI420(
+    res = libyuv::MJPGToI420(
             inData, inDataSize,
             static_cast<uint8_t*>(mYu12FrameLayout.y),
             mYu12FrameLayout.yStride,
@@ -1779,10 +1865,23 @@
         return true;
     }
 
+    ATRACE_BEGIN("Wait for BufferRequest done");
+    res = waitForBufferRequestDone(&req->buffers);
+    ATRACE_END();
+
+    if (res != 0) {
+        ALOGE("%s: wait for BufferRequest done failed! res %d", __FUNCTION__, res);
+        lk.unlock();
+        return onDeviceError("%s: failed to process buffer request error!", __FUNCTION__);
+    }
+
     ALOGV("%s processing new request", __FUNCTION__);
     const int kSyncWaitTimeoutMs = 500;
     for (auto& halBuf : req->buffers) {
-        if (halBuf.acquireFence != -1) {
+        if (*(halBuf.bufPtr) == nullptr) {
+            ALOGW("%s: buffer for stream %d missing", __FUNCTION__, halBuf.streamId);
+            halBuf.fenceTimeout = true;
+        } else if (halBuf.acquireFence != -1) {
             int ret = sync_wait(halBuf.acquireFence, kSyncWaitTimeoutMs);
             if (ret) {
                 halBuf.fenceTimeout = true;
@@ -2041,7 +2140,7 @@
 }
 
 void ExternalCameraDeviceSession::updateBufferCaches(const hidl_vec<BufferCache>& cachesToRemove) {
-    Mutex::Autolock _l(mLock);
+    Mutex::Autolock _l(mCbsLock);
     for (auto& cache : cachesToRemove) {
         auto cbsIt = mCirculatingBuffers.find(cache.streamId);
         if (cbsIt == mCirculatingBuffers.end()) {
@@ -2494,30 +2593,33 @@
     }
 
     Mutex::Autolock _l(mLock);
-    // Add new streams
-    for (const auto& stream : config.streams) {
-        if (mStreamMap.count(stream.id) == 0) {
-            mStreamMap[stream.id] = stream;
-            mCirculatingBuffers.emplace(stream.id, CirculatingBuffers{});
-        }
-    }
-
-    // Cleanup removed streams
-    for(auto it = mStreamMap.begin(); it != mStreamMap.end();) {
-        int id = it->first;
-        bool found = false;
+    {
+        Mutex::Autolock _l(mCbsLock);
+        // Add new streams
         for (const auto& stream : config.streams) {
-            if (id == stream.id) {
-                found = true;
-                break;
+            if (mStreamMap.count(stream.id) == 0) {
+                mStreamMap[stream.id] = stream;
+                mCirculatingBuffers.emplace(stream.id, CirculatingBuffers{});
             }
         }
-        if (!found) {
-            // Unmap all buffers of deleted stream
-            cleanupBuffersLocked(id);
-            it = mStreamMap.erase(it);
-        } else {
-            ++it;
+
+        // Cleanup removed streams
+        for(auto it = mStreamMap.begin(); it != mStreamMap.end();) {
+            int id = it->first;
+            bool found = false;
+            for (const auto& stream : config.streams) {
+                if (id == stream.id) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                // Unmap all buffers of deleted stream
+                cleanupBuffersLocked(id);
+                it = mStreamMap.erase(it);
+            } else {
+                ++it;
+            }
         }
     }
 
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 6e56596..cabeaa4 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -97,7 +97,7 @@
     // Call by CameraDevice to dump active device states
     void dumpState(const native_handle_t*);
     // Caller must use this method to check if CameraDeviceSession ctor failed
-    bool isInitFailed() { return mInitFail; }
+    bool isInitFailed();
     bool isClosed();
 
     // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when
@@ -134,7 +134,7 @@
             ICameraDeviceSession::processCaptureRequest_cb);
 
     Return<Status> flush();
-    Return<void> close();
+    Return<void> close(bool callerIsDtor = false);
 
     Return<void> configureStreams_3_3(
             const V3_2::StreamConfiguration&,
@@ -170,10 +170,17 @@
         std::vector<HalStreamBuffer> buffers;
     };
 
+    static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+
     Status constructDefaultRequestSettingsRaw(RequestTemplate type,
             V3_2::CameraMetadata *outMetadata);
 
     bool initialize();
+    // To init/close different version of output thread
+    virtual void initOutputThread();
+    virtual void closeOutputThread();
+    void closeOutputThreadImpl();
+
     Status initStatus() const;
     status_t initDefaultRequests();
     status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp);
@@ -195,10 +202,28 @@
     bool isSupported(const Stream&);
 
     // Validate and import request's output buffers and acquire fence
-    Status importRequest(
+    virtual Status importRequestLocked(
             const CaptureRequest& request,
             hidl_vec<buffer_handle_t*>& allBufPtrs,
             hidl_vec<int>& allFences);
+
+    Status importRequestLockedImpl(
+            const CaptureRequest& request,
+            hidl_vec<buffer_handle_t*>& allBufPtrs,
+            hidl_vec<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);
+
+    Status importBufferLocked(int32_t streamId,
+            uint64_t bufId, buffer_handle_t buf,
+            /*out*/buffer_handle_t** outBufPtr,
+            bool allowEmptyBuf);
+
     static void cleanupInflightFences(
             hidl_vec<int>& allFences, size_t numFences);
     void cleanupBuffersLocked(int id);
@@ -224,7 +249,7 @@
     class OutputThread : public android::Thread {
     public:
         OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
-        ~OutputThread();
+        virtual ~OutputThread();
 
         Status allocateIntermediateBuffers(
                 const Size& v4lSize, const Size& thumbSize,
@@ -236,7 +261,14 @@
         virtual bool threadLoop() override;
 
         void setExifMakeModel(const std::string& make, const std::string& model);
-    private:
+
+    protected:
+        // Methods to request output buffer in parallel
+        // No-op for device@3.4. Implemented in device@3.5
+        virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
+        virtual int waitForBufferRequestDone(
+                /*out*/std::vector<HalStreamBuffer>*) { return 0; }
+
         static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
                 static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
                 static_cast<uint32_t>('X') << 24;
@@ -319,6 +351,7 @@
     //    - init failed
     //    - camera disconnected
     bool mClosed = false;
+    bool mInitialized = false;
     bool mInitFail = false;
     bool mFirstRequest = false;
     common::V1_0::helper::CameraMetadata mLatestReqSetting;
@@ -351,6 +384,8 @@
     typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
     // Stream ID -> circulating buffers map
     std::map<int, CirculatingBuffers> mCirculatingBuffers;
+    // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
+    mutable Mutex mCbsLock;
 
     std::mutex mAfTriggerLock; // protect mAfTrigger
     bool mAfTrigger = false;
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
index ff0cfb3..719a3ed 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
@@ -25,6 +25,8 @@
 #include <hidl/MQDescriptor.h>
 #include "ExternalCameraDeviceSession.h"
 
+#include <vector>
+
 namespace android {
 namespace hardware {
 namespace camera {
@@ -49,7 +51,7 @@
 /*
  * The camera device HAL implementation is opened lazily (via the open call)
  */
-struct ExternalCameraDevice : public ICameraDevice {
+struct ExternalCameraDevice : public virtual RefBase {
 
     // Called by external camera provider HAL.
     // Provider HAL must ensure the uniqueness of CameraDevice object per cameraId, or there could
@@ -57,33 +59,54 @@
     // to keep track of all CameraDevice objects in order to notify CameraDevice when the underlying
     // camera is detached.
     ExternalCameraDevice(const std::string& cameraId, const ExternalCameraConfig& cfg);
-    ~ExternalCameraDevice();
+    virtual ~ExternalCameraDevice();
+
+    // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when
+    // dealing with minor version revs and simultaneous implementation and interface inheritance
+    virtual sp<ICameraDevice> getInterface() {
+        return new TrampolineDeviceInterface_3_4(this);
+    }
 
     // Caller must use this method to check if CameraDevice ctor failed
     bool isInitFailed();
+    bool isInitFailedLocked();
 
     /* Methods from ::android::hardware::camera::device::V3_2::ICameraDevice follow. */
     // The following method can be called without opening the actual camera device
-    Return<void> getResourceCost(getResourceCost_cb _hidl_cb) override;
+    Return<void> getResourceCost(ICameraDevice::getResourceCost_cb _hidl_cb);
 
-    Return<void> getCameraCharacteristics(getCameraCharacteristics_cb _hidl_cb) override;
+    Return<void> getCameraCharacteristics(
+            ICameraDevice::getCameraCharacteristics_cb _hidl_cb);
 
-    Return<Status> setTorchMode(TorchMode) override;
+    Return<Status> setTorchMode(TorchMode);
 
     // Open the device HAL and also return a default capture session
-    Return<void> open(const sp<ICameraDeviceCallback>&, open_cb) override;
+    Return<void> open(const sp<ICameraDeviceCallback>&, ICameraDevice::open_cb);
 
     // Forward the dump call to the opened session, or do nothing
-    Return<void> dumpState(const ::android::hardware::hidl_handle&) override;
+    Return<void> dumpState(const ::android::hardware::hidl_handle&);
     /* End of Methods from ::android::hardware::camera::device::V3_2::ICameraDevice */
 
 protected:
+    // Overridden by child implementations for returning different versions of
+    // ExternalCameraDeviceSession
+    virtual sp<ExternalCameraDeviceSession> createSession(
+            const sp<ICameraDeviceCallback>&,
+            const ExternalCameraConfig& cfg,
+            const std::vector<SupportedV4L2Format>& sortedFormats,
+            const CroppingType& croppingType,
+            const common::V1_0::helper::CameraMetadata& chars,
+            const std::string& cameraId,
+            unique_fd v4l2Fd);
+
     // Init supported w/h/format/fps in mSupportedFormats. Caller still owns fd
     void initSupportedFormatsLocked(int fd);
 
+    // Calls into virtual member function. Do not use it in constructor
     status_t initCameraCharacteristics();
     // Init non-device dependent keys
-    status_t initDefaultCharsKeys(::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+    virtual status_t initDefaultCharsKeys(
+            ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
     // Init camera control chars keys. Caller still owns fd
     status_t initCameraControlsCharsKeys(int fd,
             ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
@@ -103,6 +126,7 @@
             /*inout*/std::vector<SupportedV4L2Format>* pFmts);
 
     Mutex mLock;
+    bool mInitialized = false;
     bool mInitFailed = false;
     std::string mCameraId;
     const ExternalCameraConfig& mCfg;
@@ -112,6 +136,84 @@
     wp<ExternalCameraDeviceSession> mSession = nullptr;
 
     ::android::hardware::camera::common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+
+    const std::vector<int32_t> AVAILABLE_CHARACTERISTICS_KEYS_3_4 = {
+        ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+        ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+        ANDROID_CONTROL_AE_AVAILABLE_MODES,
+        ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+        ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+        ANDROID_CONTROL_AE_COMPENSATION_STEP,
+        ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+        ANDROID_CONTROL_AF_AVAILABLE_MODES,
+        ANDROID_CONTROL_AVAILABLE_EFFECTS,
+        ANDROID_CONTROL_AVAILABLE_MODES,
+        ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+        ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+        ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+        ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+        ANDROID_CONTROL_MAX_REGIONS,
+        ANDROID_FLASH_INFO_AVAILABLE,
+        ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+        ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+        ANDROID_LENS_FACING,
+        ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+        ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+        ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+        ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+        ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+        ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+        ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+        ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+        ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+        ANDROID_SCALER_CROPPING_TYPE,
+        ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+        ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+        ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+        ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+        ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+        ANDROID_SENSOR_ORIENTATION,
+        ANDROID_SHADING_AVAILABLE_MODES,
+        ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+        ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+        ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+        ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+        ANDROID_SYNC_MAX_LATENCY};
+
+private:
+
+    struct TrampolineDeviceInterface_3_4 : public ICameraDevice {
+        TrampolineDeviceInterface_3_4(sp<ExternalCameraDevice> parent) :
+            mParent(parent) {}
+
+        virtual Return<void> getResourceCost(V3_2::ICameraDevice::getResourceCost_cb _hidl_cb)
+                override {
+            return mParent->getResourceCost(_hidl_cb);
+        }
+
+        virtual Return<void> getCameraCharacteristics(
+                V3_2::ICameraDevice::getCameraCharacteristics_cb _hidl_cb) override {
+            return mParent->getCameraCharacteristics(_hidl_cb);
+        }
+
+        virtual Return<Status> setTorchMode(TorchMode mode) override {
+            return mParent->setTorchMode(mode);
+        }
+
+        virtual Return<void> open(const sp<V3_2::ICameraDeviceCallback>& callback,
+                V3_2::ICameraDevice::open_cb _hidl_cb) override {
+            return mParent->open(callback, _hidl_cb);
+        }
+
+        virtual Return<void> dumpState(const hidl_handle& fd) override {
+            return mParent->dumpState(fd);
+        }
+
+    private:
+        sp<ExternalCameraDevice> mParent;
+    };
+
 };
 
 }  // namespace implementation