Camera: implement external camera flush

Test: CTS abort capture test
Bug: 72261676
Change-Id: I0c3af8693a885672953ff394121c40c5ade59964
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 5de1442..b6b4626 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -281,6 +281,12 @@
 }
 
 Return<Status> ExternalCameraDeviceSession::flush() {
+    Mutex::Autolock _il(mInterfaceLock);
+    Status status = initStatus();
+    if (status != Status::OK) {
+        return status;
+    }
+    mOutputThread->flush();
     return Status::OK;
 }
 
@@ -492,7 +498,7 @@
 }
 
 //TODO: refactor with processCaptureResult
-Status ExternalCameraDeviceSession::processCaptureRequestError(HalRequest& req) {
+Status ExternalCameraDeviceSession::processCaptureRequestError(const HalRequest& req) {
     // Return V4L2 buffer to V4L2 buffer queue
     enqueueV4l2Frame(req.frameIn);
 
@@ -1494,19 +1500,23 @@
         return true;
     }
 
+    auto onDeviceError = [&](auto... args) {
+        ALOGE(args...);
+        parent->notifyError(
+                req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+        signalRequestDone();
+        return false;
+    };
+
     if (req.frameIn->mFourcc != V4L2_PIX_FMT_MJPEG) {
-        ALOGE("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__,
+        return onDeviceError("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__,
                 req.frameIn->mFourcc & 0xFF,
                 (req.frameIn->mFourcc >> 8) & 0xFF,
                 (req.frameIn->mFourcc >> 16) & 0xFF,
                 (req.frameIn->mFourcc >> 24) & 0xFF);
-        parent->notifyError(
-                /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
-        return false;
     }
 
-    std::unique_lock<std::mutex> lk(mLock);
-
+    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
     uint8_t* inData;
@@ -1531,11 +1541,9 @@
         lk.unlock();
         Status st = parent->processCaptureRequestError(req);
         if (st != Status::OK) {
-            ALOGE("%s: failed to process capture request error!", __FUNCTION__);
-            parent->notifyError(
-                    /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
-            return false;
+            return onDeviceError("%s: failed to process capture request error!", __FUNCTION__);
         }
+        signalRequestDone();
         return true;
     }
 
@@ -1562,15 +1570,9 @@
                 int ret = createJpegLocked(halBuf, req);
 
                 if(ret != 0) {
-                    ALOGE("%s: createJpegLocked failed with %d",
-                          __FUNCTION__, ret);
                     lk.unlock();
-                    parent->notifyError(
-                            /*frameNum*/req.frameNumber,
-                            /*stream*/-1,
-                            ErrorCode::ERROR_DEVICE);
-
-                    return false;
+                    return onDeviceError("%s: createJpegLocked failed with %d",
+                          __FUNCTION__, ret);
                 }
             } break;
             case PixelFormat::YCBCR_420_888:
@@ -1598,21 +1600,15 @@
                         Size { halBuf.width, halBuf.height },
                         &cropAndScaled);
                 if (ret != 0) {
-                    ALOGE("%s: crop and scale failed!", __FUNCTION__);
                     lk.unlock();
-                    parent->notifyError(
-                            /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
-                    return false;
+                    return onDeviceError("%s: crop and scale failed!", __FUNCTION__);
                 }
 
                 Size sz {halBuf.width, halBuf.height};
                 ret = formatConvertLocked(cropAndScaled, outLayout, sz, outputFourcc);
                 if (ret != 0) {
-                    ALOGE("%s: format coversion failed!", __FUNCTION__);
                     lk.unlock();
-                    parent->notifyError(
-                            /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
-                    return false;
+                    return onDeviceError("%s: format coversion failed!", __FUNCTION__);
                 }
                 int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
                 if (relFence > 0) {
@@ -1620,11 +1616,8 @@
                 }
             } break;
             default:
-                ALOGE("%s: unknown output format %x", __FUNCTION__, halBuf.format);
                 lk.unlock();
-                parent->notifyError(
-                        /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
-                return false;
+                return onDeviceError("%s: unknown output format %x", __FUNCTION__, halBuf.format);
         }
     } // for each buffer
     mScaledYu12Frames.clear();
@@ -1633,18 +1626,16 @@
     lk.unlock();
     Status st = parent->processCaptureResult(req);
     if (st != Status::OK) {
-        ALOGE("%s: failed to process capture result!", __FUNCTION__);
-        parent->notifyError(
-                /*frameNum*/req.frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
-        return false;
+        return onDeviceError("%s: failed to process capture result!", __FUNCTION__);
     }
+    signalRequestDone();
     return true;
 }
 
 Status ExternalCameraDeviceSession::OutputThread::allocateIntermediateBuffers(
         const Size& v4lSize, const Size& thumbSize,
         const hidl_vec<Stream>& streams) {
-    std::lock_guard<std::mutex> lk(mLock);
+    std::lock_guard<std::mutex> lk(mBufferLock);
     if (mScaledYu12Frames.size() != 0) {
         ALOGE("%s: intermediate buffer pool has %zu inflight buffers! (expect 0)",
                 __FUNCTION__, mScaledYu12Frames.size());
@@ -1716,17 +1707,36 @@
 }
 
 Status ExternalCameraDeviceSession::OutputThread::submitRequest(const HalRequest& req) {
-    std::lock_guard<std::mutex> lk(mLock);
+    std::unique_lock<std::mutex> lk(mRequestListLock);
     // TODO: reduce object copy in this path
     mRequestList.push_back(req);
+    lk.unlock();
     mRequestCond.notify_one();
     return Status::OK;
 }
 
 void ExternalCameraDeviceSession::OutputThread::flush() {
-    std::lock_guard<std::mutex> lk(mLock);
-    // TODO: send buffer/request errors back to framework
+    auto parent = mParent.promote();
+    if (parent == nullptr) {
+       ALOGE("%s: session has been disconnected!", __FUNCTION__);
+       return;
+    }
+
+    std::unique_lock<std::mutex> lk(mRequestListLock);
+    std::list<HalRequest> reqs = mRequestList;
     mRequestList.clear();
+    if (mProcessingRequest) {
+        std::chrono::seconds timeout = std::chrono::seconds(kReqWaitTimeoutSec);
+        auto st = mRequestDoneCond.wait_for(lk, timeout);
+        if (st == std::cv_status::timeout) {
+            ALOGE("%s: wait for inflight request finish timeout!", __FUNCTION__);
+        }
+    }
+
+    lk.unlock();
+    for (const auto& req : reqs) {
+        parent->processCaptureRequestError(req);
+    }
 }
 
 void ExternalCameraDeviceSession::OutputThread::waitForNextRequest(HalRequest* out) {
@@ -1735,7 +1745,7 @@
         return;
     }
 
-    std::unique_lock<std::mutex> lk(mLock);
+    std::unique_lock<std::mutex> lk(mRequestListLock);
     while (mRequestList.empty()) {
         std::chrono::seconds timeout = std::chrono::seconds(kReqWaitTimeoutSec);
         auto st = mRequestCond.wait_for(lk, timeout);
@@ -1746,6 +1756,14 @@
     }
     *out = mRequestList.front();
     mRequestList.pop_front();
+    mProcessingRequest = true;
+}
+
+void ExternalCameraDeviceSession::OutputThread::signalRequestDone() {
+    std::unique_lock<std::mutex> lk(mRequestListLock);
+    mProcessingRequest = false;
+    lk.unlock();
+    mRequestDoneCond.notify_one();
 }
 
 void ExternalCameraDeviceSession::cleanupBuffersLocked(int id) {
@@ -2068,8 +2086,8 @@
     {
         std::lock_guard<std::mutex> lk(mV4l2BufferLock);
         mNumDequeuedV4l2Buffers--;
-        mV4L2BufferReturned.notify_one();
     }
+    mV4L2BufferReturned.notify_one();
 }
 
 Status ExternalCameraDeviceSession::configureStreams(
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 cced3f7..62b6c27 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
@@ -203,7 +203,7 @@
     Status processOneCaptureRequest(const CaptureRequest& request);
 
     Status processCaptureResult(HalRequest&);
-    Status processCaptureRequestError(HalRequest&);
+    Status processCaptureRequestError(const HalRequest&);
     void notifyShutter(uint32_t frameNumber, nsecs_t shutterTs);
     void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec);
     void invokeProcessCaptureResultCallback(
@@ -239,6 +239,8 @@
         static const int kReqWaitTimeoutSec = 3;
 
         void waitForNextRequest(HalRequest* out);
+        void signalRequestDone();
+
         int cropAndScaleLocked(
                 sp<AllocatedFrame>& in, const Size& outSize,
                 YCbCrLayout* out);
@@ -258,15 +260,20 @@
 
         int createJpegLocked(HalStreamBuffer &halBuf, HalRequest &req);
 
-        mutable std::mutex mLock;
-        std::condition_variable mRequestCond;
-        wp<ExternalCameraDeviceSession> mParent;
-        CroppingType mCroppingType;
+        const wp<ExternalCameraDeviceSession> mParent;
+        const CroppingType mCroppingType;
+
+        mutable std::mutex mRequestListLock;      // Protect acccess to mRequestList
+        std::condition_variable mRequestCond;     // signaled when a new request is submitted
+        std::condition_variable mRequestDoneCond; // signaled when a request is done processing
         std::list<HalRequest> mRequestList;
+        bool mProcessingRequest = false;
+
         // V4L2 frameIn
         // (MJPG decode)-> mYu12Frame
         // (Scale)-> mScaledYu12Frames
         // (Format convert) -> output gralloc frames
+        mutable std::mutex mBufferLock; // Protect access to intermediate buffers
         sp<AllocatedFrame> mYu12Frame;
         sp<AllocatedFrame> mYu12ThumbFrame;
         std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
@@ -303,6 +310,7 @@
     std::condition_variable mV4L2BufferReturned;
     size_t mNumDequeuedV4l2Buffers = 0;
 
+    // Not protected by mLock (but might be used when mLock is locked)
     sp<OutputThread> mOutputThread;
 
     // Stream ID -> Camera3Stream cache