Merge "MediaCas: add CAS support to MPEG2TSExtractor and MediaCodec"
diff --git a/media/libaudiohal/StreamHalHidl.cpp b/media/libaudiohal/StreamHalHidl.cpp
index 2a7a67f..77ba716 100644
--- a/media/libaudiohal/StreamHalHidl.cpp
+++ b/media/libaudiohal/StreamHalHidl.cpp
@@ -315,6 +315,10 @@
             WriteCommand::WRITE, "write", static_cast<const uint8_t*>(buffer), bytes,
             [&] (const WriteStatus& writeStatus) {
                 *written = writeStatus.reply.written;
+                // Diagnostics of the cause of b/35813113.
+                ALOGE_IF(*written > bytes,
+                        "hal reports more bytes written than asked for: %lld > %lld",
+                        (long long)*written, (long long)bytes);
             });
 }
 
@@ -328,8 +332,8 @@
     if (data != nullptr) {
         size_t availableToWrite = mDataMQ->availableToWrite();
         if (dataSize > availableToWrite) {
-            ALOGW("truncating write data from %d to %d due to insufficient data queue space",
-                    (int32_t)dataSize, (int32_t)availableToWrite);
+            ALOGW("truncating write data from %lld to %lld due to insufficient data queue space",
+                    (long long)dataSize, (long long)availableToWrite);
             dataSize = availableToWrite;
         }
         if (!mDataMQ->write(data, dataSize)) {
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index 82a2627..0bf7854 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -277,19 +277,20 @@
 }
 
 void MtpDevice::print() {
-    if (mDeviceInfo) {
-        mDeviceInfo->print();
+    if (!mDeviceInfo)
+        return;
 
-        if (mDeviceInfo->mDeviceProperties) {
-            ALOGI("***** DEVICE PROPERTIES *****\n");
-            int count = mDeviceInfo->mDeviceProperties->size();
-            for (int i = 0; i < count; i++) {
-                MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
-                MtpProperty* property = getDevicePropDesc(propCode);
-                if (property) {
-                    property->print();
-                    delete property;
-                }
+    mDeviceInfo->print();
+
+    if (mDeviceInfo->mDeviceProperties) {
+        ALOGI("***** DEVICE PROPERTIES *****\n");
+        int count = mDeviceInfo->mDeviceProperties->size();
+        for (int i = 0; i < count; i++) {
+            MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+            MtpProperty* property = getDevicePropDesc(propCode);
+            if (property) {
+                property->print();
+                delete property;
             }
         }
     }
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index e710f0a..79e7ff0 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -507,6 +507,14 @@
             return res;
 
         if (!isStreamInfoValid) {
+            // Streaming sharing is only supported for IMPLEMENTATION_DEFINED
+            // formats.
+            if (isShared && streamInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+                String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
+                        "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
+                ALOGW("%s: %s", __FUNCTION__, msg.string());
+                return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+            }
             isStreamInfoValid = true;
         }
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 068a2b3..f20556d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -3956,7 +3956,8 @@
                 }
             }
 
-            res = outputStream->getBuffer(&outputBuffers->editItemAt(i));
+            res = outputStream->getBuffer(&outputBuffers->editItemAt(i),
+                    captureRequest->mOutputSurfaces[i]);
             if (res != OK) {
                 // Can't get output buffer from gralloc queue - this could be due to
                 // abandoned queue or other consumer misbehavior, so not a fatal
@@ -3968,13 +3969,6 @@
             }
             halRequest->num_output_buffers++;
 
-            res = outputStream->notifyRequestedSurfaces(halRequest->frame_number,
-                    captureRequest->mOutputSurfaces[i]);
-            if (res != OK) {
-                ALOGE("RequestThread: Cannot register output surfaces: %s (%d)",
-                      strerror(-res), res);
-                return INVALID_OPERATION;
-            }
         }
         totalNumBuffers += halRequest->num_output_buffers;
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 1a730d6..9c951b7 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -36,7 +36,8 @@
 
 }
 
-status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *) {
+status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *,
+        const std::vector<size_t>&) {
     ATRACE_CALL();
     ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
     return INVALID_OPERATION;
@@ -83,14 +84,6 @@
     return OK;
 }
 
-status_t Camera3DummyStream::notifyRequestedSurfaces(uint32_t frame_number,
-        const std::vector<size_t>& surface_ids) {
-    (void) frame_number;
-    (void) surface_ids;
-    // Do nothing
-    return OK;
-}
-
 status_t Camera3DummyStream::configureQueueLocked() {
     // Do nothing
     return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index b6ec99c..35a6a18 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -56,9 +56,6 @@
 
     virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
 
-    virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
-            const std::vector<size_t>& surface_ids);
-
     /**
      * Return if this output stream is for video encoding.
      */
@@ -102,7 +99,8 @@
     /**
      * Internal Camera3Stream interface
      */
-    virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+    virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>());
     virtual status_t returnBufferLocked(
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index f971116..51dc20a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -148,73 +148,17 @@
     disconnectLocked();
 }
 
-status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) {
+status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+        const std::vector<size_t>&) {
     ATRACE_CALL();
-    status_t res;
-
-    if ((res = getBufferPreconditionCheckLocked()) != OK) {
-        return res;
-    }
 
     ANativeWindowBuffer* anb;
     int fenceFd = -1;
-    bool gotBufferFromManager = false;
 
-    if (mUseBufferManager) {
-        sp<GraphicBuffer> gb;
-        res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, &fenceFd);
-        if (res == OK) {
-            // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
-            // successful return.
-            anb = gb.get();
-            res = mConsumer->attachBuffer(anb);
-            if (res != OK) {
-                ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
-                        __FUNCTION__, mId, strerror(-res), res);
-                return res;
-            }
-            gotBufferFromManager = true;
-            ALOGV("Stream %d: Attached new buffer", getId());
-        } else if (res == ALREADY_EXISTS) {
-            // Have sufficient free buffers already attached, can just
-            // dequeue from buffer queue
-            ALOGV("Stream %d: Reusing attached buffer", getId());
-            gotBufferFromManager = false;
-        } else if (res != OK) {
-            ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
-                    __FUNCTION__, mId, strerror(-res), res);
-            return res;
-        }
-    }
-    if (!gotBufferFromManager) {
-        /**
-         * Release the lock briefly to avoid deadlock for below scenario:
-         * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
-         * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
-         * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
-         * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
-         * StreamingProcessor lock.
-         * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
-         * and try to lock bufferQueue lock.
-         * Then there is circular locking dependency.
-         */
-        sp<ANativeWindow> currentConsumer = mConsumer;
-        mLock.unlock();
-
-        res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
-        mLock.lock();
-        if (res != OK) {
-            ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
-                    __FUNCTION__, mId, strerror(-res), res);
-
-            // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
-            // let prepareNextBuffer handle the error.)
-            if (res == NO_INIT && mState == STATE_CONFIGURED) {
-                mState = STATE_ABANDONED;
-            }
-
-            return res;
-        }
+    status_t res;
+    res = getBufferLockedCommon(&anb, &fenceFd);
+    if (res != OK) {
+        return res;
     }
 
     /**
@@ -227,6 +171,11 @@
     return OK;
 }
 
+status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
+            ANativeWindowBuffer* buffer, int anwReleaseFence) {
+    return consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+}
+
 status_t Camera3OutputStream::returnBufferLocked(
         const camera3_stream_buffer &buffer,
         nsecs_t timestamp) {
@@ -269,6 +218,7 @@
     sp<ANativeWindow> currentConsumer = mConsumer;
     mLock.unlock();
 
+    ANativeWindowBuffer *anwBuffer = container_of(buffer.buffer, ANativeWindowBuffer, handle);
     /**
      * Return buffer back to ANativeWindow
      */
@@ -276,13 +226,14 @@
         // Cancel buffer
         ALOGW("A frame is dropped for stream %d", mId);
         res = currentConsumer->cancelBuffer(currentConsumer.get(),
-                container_of(buffer.buffer, ANativeWindowBuffer, handle),
+                anwBuffer,
                 anwReleaseFence);
         if (res != OK) {
             ALOGE("%s: Stream %d: Error cancelling buffer to native window:"
                   " %s (%d)", __FUNCTION__, mId, strerror(-res), res);
         }
 
+        notifyBufferReleased(anwBuffer);
         if (mUseBufferManager) {
             // Return this buffer back to buffer manager.
             mBufferReleasedListener->onBufferReleased();
@@ -308,9 +259,7 @@
             return res;
         }
 
-        res = currentConsumer->queueBuffer(currentConsumer.get(),
-                container_of(buffer.buffer, ANativeWindowBuffer, handle),
-                anwReleaseFence);
+        res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence);
         if (res != OK) {
             ALOGE("%s: Stream %d: Error queueing buffer to native window: "
                   "%s (%d)", __FUNCTION__, mId, strerror(-res), res);
@@ -527,6 +476,76 @@
     return OK;
 }
 
+status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd) {
+    ATRACE_CALL();
+    status_t res;
+
+    if ((res = getBufferPreconditionCheckLocked()) != OK) {
+        return res;
+    }
+
+    bool gotBufferFromManager = false;
+
+    if (mUseBufferManager) {
+        sp<GraphicBuffer> gb;
+        res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, fenceFd);
+        if (res == OK) {
+            // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
+            // successful return.
+            *anb = gb.get();
+            res = mConsumer->attachBuffer(*anb);
+            if (res != OK) {
+                ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
+                        __FUNCTION__, mId, strerror(-res), res);
+                return res;
+            }
+            gotBufferFromManager = true;
+            ALOGV("Stream %d: Attached new buffer", getId());
+        } else if (res == ALREADY_EXISTS) {
+            // Have sufficient free buffers already attached, can just
+            // dequeue from buffer queue
+            ALOGV("Stream %d: Reusing attached buffer", getId());
+            gotBufferFromManager = false;
+        } else if (res != OK) {
+            ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
+                    __FUNCTION__, mId, strerror(-res), res);
+            return res;
+        }
+    }
+    if (!gotBufferFromManager) {
+        /**
+         * Release the lock briefly to avoid deadlock for below scenario:
+         * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+         * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+         * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+         * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+         * StreamingProcessor lock.
+         * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+         * and try to lock bufferQueue lock.
+         * Then there is circular locking dependency.
+         */
+        sp<ANativeWindow> currentConsumer = mConsumer;
+        mLock.unlock();
+
+        res = currentConsumer->dequeueBuffer(currentConsumer.get(), anb, fenceFd);
+        mLock.lock();
+        if (res != OK) {
+            ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
+                    __FUNCTION__, mId, strerror(-res), res);
+
+            // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+            // let prepareNextBuffer handle the error.)
+            if (res == NO_INIT && mState == STATE_CONFIGURED) {
+                mState = STATE_ABANDONED;
+            }
+
+            return res;
+        }
+    }
+
+    return res;
+}
+
 status_t Camera3OutputStream::disconnectLocked() {
     status_t res;
 
@@ -702,8 +721,7 @@
     return OK;
 }
 
-status_t Camera3OutputStream::notifyRequestedSurfaces(uint32_t /*frame_number*/,
-        const std::vector<size_t>& /*surface_ids*/) {
+status_t Camera3OutputStream::notifyBufferReleased(ANativeWindowBuffer* /*anwBuffer*/) {
     return OK;
 }
 
@@ -717,6 +735,7 @@
 }
 
 status_t Camera3OutputStream::setConsumers(const std::vector<sp<Surface>>& consumers) {
+    Mutex::Autolock l(mLock);
     if (consumers.size() != 1) {
         ALOGE("%s: it's illegal to set %zu consumer surfaces!",
                   __FUNCTION__, consumers.size());
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 080c721..24e4e05 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -158,8 +158,11 @@
 
     virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
 
-    virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
-            const std::vector<size_t>& surface_ids);
+    /**
+     * Notify that the buffer is being released to the buffer queue instead of
+     * being queued to the consumer.
+     */
+    virtual status_t notifyBufferReleased(ANativeWindowBuffer *anwBuffer);
 
     /**
      * Set the graphic buffer manager to get/return the stream buffers.
@@ -198,6 +201,9 @@
 
     static const nsecs_t       kDequeueBufferTimeout   = 1000000000; // 1 sec
 
+    status_t getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd);
+
+
   private:
 
     int               mTransform;
@@ -243,11 +249,16 @@
     /**
      * Internal Camera3Stream interface
      */
-    virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+    virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+            const std::vector<size_t>& surface_ids);
+
     virtual status_t returnBufferLocked(
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp);
 
+    virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
+            ANativeWindowBuffer* buffer, int anwReleaseFence);
+
     virtual status_t configureQueueLocked();
 
     virtual status_t getEndpointUsage(uint32_t *usage) const;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 11868e7..8107dd0 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -59,20 +59,6 @@
      *
      */
     virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) = 0;
-
-    /**
-     * Notify which surfaces are requested for a particular frame number.
-     *
-     * Mulitple surfaces could share the same output stream, but a request may
-     * be only for a subset of surfaces. In this case, the
-     * Camera3OutputStreamInterface object needs to manage the output surfaces on
-     * a per request basis.
-     *
-     * If there is only one surface for this output stream, calling this
-     * function is a no-op.
-     */
-    virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
-            const std::vector<size_t>& surface_ids) = 0;
 };
 
 } // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 0d6a96c..2ae5660 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -44,7 +44,7 @@
     uint32_t usage;
     getEndpointUsage(&usage);
 
-    res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, mConsumer);
+    res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, &mConsumer);
     if (res != OK) {
         ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
                 __FUNCTION__, strerror(-res), res);
@@ -54,13 +54,13 @@
     return res;
 }
 
-status_t Camera3SharedOutputStream::notifyRequestedSurfaces(uint32_t /*frame_number*/,
-        const std::vector<size_t>& surface_ids) {
+status_t Camera3SharedOutputStream::notifyBufferReleased(ANativeWindowBuffer *anwBuffer) {
     Mutex::Autolock l(mLock);
     status_t res = OK;
+    const sp<GraphicBuffer> buffer(static_cast<GraphicBuffer*>(anwBuffer));
 
     if (mStreamSplitter != nullptr) {
-        res = mStreamSplitter->notifyRequestedSurfaces(surface_ids);
+        res = mStreamSplitter->notifyBufferReleased(buffer);
     }
 
     return res;
@@ -72,6 +72,7 @@
 }
 
 status_t Camera3SharedOutputStream::setConsumers(const std::vector<sp<Surface>>& surfaces) {
+    Mutex::Autolock l(mLock);
     if (surfaces.size() == 0) {
         ALOGE("%s: it's illegal to set zero consumer surfaces!", __FUNCTION__);
         return INVALID_OPERATION;
@@ -88,7 +89,7 @@
 
         // Only call addOutput if the splitter has been connected.
         if (mStreamSplitter != nullptr) {
-            ret = mStreamSplitter->addOutput(surface, camera3_stream::max_buffers);
+            ret = mStreamSplitter->addOutput(surface);
             if (ret != OK) {
                 ALOGE("%s: addOutput failed with error code %d", __FUNCTION__, ret);
                 return ret;
@@ -99,6 +100,64 @@
     return ret;
 }
 
+status_t Camera3SharedOutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+        const std::vector<size_t>& surface_ids) {
+    ANativeWindowBuffer* anb;
+    int fenceFd = -1;
+
+    status_t res;
+    res = getBufferLockedCommon(&anb, &fenceFd);
+    if (res != OK) {
+        return res;
+    }
+
+    // Attach the buffer to the splitter output queues. This could block if
+    // the output queue doesn't have any empty slot. So unlock during the course
+    // of attachBufferToOutputs.
+    sp<Camera3StreamSplitter> splitter = mStreamSplitter;
+    mLock.unlock();
+    res = splitter->attachBufferToOutputs(anb, surface_ids);
+    mLock.lock();
+    if (res != OK) {
+        ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
+                __FUNCTION__, mId, strerror(-res), res);
+        // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+        // let prepareNextBuffer handle the error.)
+        if (res == NO_INIT && mState == STATE_CONFIGURED) {
+            mState = STATE_ABANDONED;
+        }
+
+        return res;
+    }
+
+    /**
+     * FenceFD now owned by HAL except in case of error,
+     * in which case we reassign it to acquire_fence
+     */
+    handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd,
+                        /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true);
+
+    return OK;
+}
+
+status_t Camera3SharedOutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
+            ANativeWindowBuffer* buffer, int anwReleaseFence) {
+    status_t res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+
+    // After queuing buffer to the internal consumer queue, check whether the buffer is
+    // successfully queued to the output queues.
+    if (res == OK) {
+        res = mStreamSplitter->getOnFrameAvailableResult();
+        if (res != OK) {
+            ALOGE("%s: getOnFrameAvailable returns %d", __FUNCTION__, res);
+        }
+    } else {
+        ALOGE("%s: queueBufer failed %d", __FUNCTION__, res);
+    }
+
+    return res;
+}
+
 status_t Camera3SharedOutputStream::configureQueueLocked() {
     status_t res;
 
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index cc96076..7be0940 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -40,8 +40,7 @@
 
     virtual ~Camera3SharedOutputStream();
 
-    virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
-            const std::vector<size_t>& surface_ids);
+    virtual status_t notifyBufferReleased(ANativeWindowBuffer *buffer);
 
     virtual bool isConsumerConfigurationDeferred(size_t surface_id) const;
 
@@ -62,6 +61,15 @@
      */
     status_t connectStreamSplitterLocked();
 
+    /**
+     * Internal Camera3Stream interface
+     */
+    virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+            const std::vector<size_t>& surface_ids);
+
+    virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
+            ANativeWindowBuffer* buffer, int anwReleaseFence);
+
     virtual status_t configureQueueLocked();
 
     virtual status_t disconnectLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index c3b7565..53a3168 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -443,7 +443,8 @@
     return OK;
 }
 
-status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) {
+status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer,
+        const std::vector<size_t>& surface_ids) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
     status_t res = OK;
@@ -470,7 +471,7 @@
         }
     }
 
-    res = getBufferLocked(buffer);
+    res = getBufferLocked(buffer, surface_ids);
     if (res == OK) {
         fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true);
         if (buffer->buffer) {
@@ -745,7 +746,8 @@
     return res;
 }
 
-status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *) {
+status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *,
+        const std::vector<size_t>&) {
     ALOGE("%s: This type of stream does not support output", __FUNCTION__);
     return INVALID_OPERATION;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 471b393..56cb827 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -277,12 +277,18 @@
      * Fill in the camera3_stream_buffer with the next valid buffer for this
      * stream, to hand over to the HAL.
      *
+     * Multiple surfaces could share the same HAL stream, but a request may
+     * be only for a subset of surfaces. In this case, the
+     * Camera3StreamInterface object needs the surface ID information to acquire
+     * buffers for those surfaces.
+     *
      * This method may only be called once finishConfiguration has been called.
      * For bidirectional streams, this method applies to the output-side
      * buffers.
      *
      */
-    status_t         getBuffer(camera3_stream_buffer *buffer);
+    status_t         getBuffer(camera3_stream_buffer *buffer,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>());
 
     /**
      * Return a buffer to the stream after use by the HAL.
@@ -412,7 +418,8 @@
     // cast to camera3_stream*, implementations must increment the
     // refcount of the stream manually in getBufferLocked, and decrement it in
     // returnBufferLocked.
-    virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+    virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>());
     virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer,
             nsecs_t timestamp);
     virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index ceea08a..f7b092f 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -200,12 +200,19 @@
      * Fill in the camera3_stream_buffer with the next valid buffer for this
      * stream, to hand over to the HAL.
      *
+     * Multiple surfaces could share the same HAL stream, but a request may
+     * be only for a subset of surfaces. In this case, the
+     * Camera3StreamInterface object needs the surface ID information to acquire
+     * buffers for those surfaces. For the case of single surface for a HAL
+     * stream, surface_ids parameter has no effect.
+     *
      * This method may only be called once finishConfiguration has been called.
      * For bidirectional streams, this method applies to the output-side
      * buffers.
      *
      */
-    virtual status_t getBuffer(camera3_stream_buffer *buffer) = 0;
+    virtual status_t getBuffer(camera3_stream_buffer *buffer,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>()) = 0;
 
     /**
      * Return a buffer to the stream after use by the HAL.
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index c9f43aa..deb6735 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -37,10 +37,10 @@
 namespace android {
 
 status_t Camera3StreamSplitter::connect(const std::vector<sp<Surface> >& surfaces,
-                                           uint32_t consumerUsage, size_t hal_max_buffers,
-                                           sp<Surface>& consumer) {
-    if (consumer != nullptr) {
-        ALOGE("%s: output Surface is not NULL", __FUNCTION__);
+        uint32_t consumerUsage, size_t halMaxBuffers, sp<Surface>* consumer) {
+    ATRACE_CALL();
+    if (consumer == nullptr) {
+        SP_LOGE("%s: consumer pointer is NULL", __FUNCTION__);
         return BAD_VALUE;
     }
 
@@ -48,129 +48,147 @@
     status_t res = OK;
 
     if (mOutputs.size() > 0 || mConsumer != nullptr) {
-        ALOGE("%s: StreamSplitter already connected", __FUNCTION__);
+        SP_LOGE("%s: already connected", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    if (mBuffers.size() > 0) {
+        SP_LOGE("%s: still has %zu pending buffers", __FUNCTION__, mBuffers.size());
         return BAD_VALUE;
     }
 
+    mMaxHalBuffers = halMaxBuffers;
+    mConsumerName = getUniqueConsumerName();
     // Add output surfaces. This has to be before creating internal buffer queue
     // in order to get max consumer side buffers.
     for (size_t i = 0; i < surfaces.size(); i++) {
         if (surfaces[i] == nullptr) {
-            ALOGE("%s: Fatal: surface is NULL", __FUNCTION__);
+            SP_LOGE("%s: Fatal: surface is NULL", __FUNCTION__);
             return BAD_VALUE;
         }
-        res = addOutputLocked(surfaces[i], hal_max_buffers, OutputType::NonDeferred);
+        res = addOutputLocked(surfaces[i]);
         if (res != OK) {
-            ALOGE("%s: Failed to add output surface: %s(%d)",
+            SP_LOGE("%s: Failed to add output surface: %s(%d)",
                     __FUNCTION__, strerror(-res), res);
             return res;
         }
     }
 
-    // Create buffer queue for input
+    // Create BufferQueue for input
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
 
+    // Allocate 1 extra buffer to handle the case where all buffers are detached
+    // from input, and attached to the outputs. In this case, the input queue's
+    // dequeueBuffer can still allocate 1 extra buffer before being blocked by
+    // the output's attachBuffer().
     mBufferItemConsumer = new BufferItemConsumer(mConsumer, consumerUsage,
-                                                 mMaxConsumerBuffers);
+                                                 mMaxConsumerBuffers+1);
     if (mBufferItemConsumer == nullptr) {
         return NO_MEMORY;
     }
-    mConsumer->setConsumerName(getUniqueConsumerName());
+    mConsumer->setConsumerName(mConsumerName);
 
-    mSurface = new Surface(mProducer);
-    if (mSurface == nullptr) {
+    *consumer = new Surface(mProducer);
+    if (*consumer == nullptr) {
         return NO_MEMORY;
     }
-    consumer = mSurface;
 
     res = mConsumer->consumerConnect(this, /* controlledByApp */ false);
 
+    SP_LOGV("%s: connected", __FUNCTION__);
     return res;
 }
 
+status_t Camera3StreamSplitter::getOnFrameAvailableResult() {
+    ATRACE_CALL();
+    return mOnFrameAvailableRes.load();
+}
+
 void Camera3StreamSplitter::disconnect() {
+    ATRACE_CALL();
     Mutex::Autolock lock(mMutex);
 
+    for (auto& notifier : mNotifiers) {
+        sp<IGraphicBufferProducer> producer = notifier.first;
+        sp<OutputListener> listener = notifier.second;
+        IInterface::asBinder(producer)->unlinkToDeath(listener);
+    }
+    mNotifiers.clear();
+
     for (auto& output : mOutputs) {
         output->disconnect(NATIVE_WINDOW_API_CAMERA);
     }
     mOutputs.clear();
+    mOutputSlots.clear();
 
-    if (mConsumer != nullptr) {
-        mConsumer->consumerDisconnect();
-        mConsumer.clear();
-    }
+    mConsumer->consumerDisconnect();
 
     if (mBuffers.size() > 0) {
-        ALOGI("%zu buffers still being tracked", mBuffers.size());
+        SP_LOGW("%zu buffers still being tracked", mBuffers.size());
+        mBuffers.clear();
     }
+
+    mMaxHalBuffers = 0;
+    mMaxConsumerBuffers = 0;
+    SP_LOGV("%s: Disconnected", __FUNCTION__);
 }
 
+
 Camera3StreamSplitter::~Camera3StreamSplitter() {
     disconnect();
 }
 
-status_t Camera3StreamSplitter::addOutput(
-        const sp<Surface>& outputQueue, size_t hal_max_buffers) {
+status_t Camera3StreamSplitter::addOutput(const sp<Surface>& outputQueue) {
+    ATRACE_CALL();
     Mutex::Autolock lock(mMutex);
-    return addOutputLocked(outputQueue, hal_max_buffers, OutputType::Deferred);
+    status_t res = addOutputLocked(outputQueue);
+
+    if (res != OK) {
+        SP_LOGE("%s: addOutputLocked failed %d", __FUNCTION__, res);
+        return res;
+    }
+
+    res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers+1);
+
+    return res;
 }
 
-status_t Camera3StreamSplitter::addOutputLocked(
-        const sp<Surface>& outputQueue, size_t hal_max_buffers,
-        OutputType outputType) {
+status_t Camera3StreamSplitter::addOutputLocked(const sp<Surface>& outputQueue) {
+    ATRACE_CALL();
     if (outputQueue == nullptr) {
-        ALOGE("addOutput: outputQueue must not be NULL");
-        return BAD_VALUE;
-    }
-    if (hal_max_buffers < 1) {
-        ALOGE("%s: Camera HAL requested max_buffer count: %zu, requires at least 1",
-                __FUNCTION__, hal_max_buffers);
+        SP_LOGE("addOutput: outputQueue must not be NULL");
         return BAD_VALUE;
     }
 
     sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
     // Connect to the buffer producer
-    IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
     sp<OutputListener> listener(new OutputListener(this, gbp));
     IInterface::asBinder(gbp)->linkToDeath(listener);
-    status_t status = gbp->connect(listener, NATIVE_WINDOW_API_CAMERA,
-            /* producerControlledByApp */ true, &queueBufferOutput);
-    if (status != NO_ERROR) {
-        ALOGE("addOutput: failed to connect (%d)", status);
-       return status;
+    status_t res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
+    if (res != NO_ERROR) {
+        SP_LOGE("addOutput: failed to connect (%d)", res);
+        return res;
     }
 
     // Query consumer side buffer count, and update overall buffer count
     int maxConsumerBuffers = 0;
-    status = static_cast<ANativeWindow*>(outputQueue.get())->query(
+    res = static_cast<ANativeWindow*>(outputQueue.get())->query(
             outputQueue.get(),
             NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
-    if (status != OK) {
-        ALOGE("%s: Unable to query consumer undequeued buffer count"
+    if (res != OK) {
+        SP_LOGE("%s: Unable to query consumer undequeued buffer count"
               " for surface", __FUNCTION__);
-        return status;
+        return res;
     }
 
-    if (maxConsumerBuffers > mMaxConsumerBuffers) {
-        if (outputType == OutputType::Deferred) {
-            ALOGE("%s: Fatal: Deferred surface has higher consumer buffer count"
-                  " %d than what's already configured %d", __FUNCTION__,
-                  maxConsumerBuffers, mMaxConsumerBuffers);
-            return BAD_VALUE;
-        }
-        mMaxConsumerBuffers = maxConsumerBuffers;
-    }
-
-    ALOGV("%s: Consumer wants %d buffers, HAL wants %zu", __FUNCTION__,
-            maxConsumerBuffers, hal_max_buffers);
-    size_t totalBufferCount = maxConsumerBuffers + hal_max_buffers;
-    status = native_window_set_buffer_count(outputQueue.get(),
+    SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
+            maxConsumerBuffers, mMaxHalBuffers);
+    size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
+    res = native_window_set_buffer_count(outputQueue.get(),
             totalBufferCount);
-    if (status != OK) {
-        ALOGE("%s: Unable to set buffer count for surface %p",
+    if (res != OK) {
+        SP_LOGE("%s: Unable to set buffer count for surface %p",
                 __FUNCTION__, outputQueue.get());
-        return status;
+        return res;
     }
 
     // Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture.
@@ -183,157 +201,239 @@
         outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
     }
 
-    status = gbp->allowAllocation(false);
-    if (status != OK) {
-        ALOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
-        return status;
+    res = gbp->allowAllocation(false);
+    if (res != OK) {
+        SP_LOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
+        return res;
     }
 
     // Add new entry into mOutputs
     mOutputs.push_back(gbp);
+    mNotifiers[gbp] = listener;
+    mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);
+
+    mMaxConsumerBuffers += maxConsumerBuffers;
     return NO_ERROR;
 }
 
+status_t Camera3StreamSplitter::outputBufferLocked(const sp<IGraphicBufferProducer>& output,
+        const BufferItem& bufferItem) {
+    ATRACE_CALL();
+    status_t res;
+    IGraphicBufferProducer::QueueBufferInput queueInput(
+            bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
+            bufferItem.mDataSpace, bufferItem.mCrop,
+            static_cast<int32_t>(bufferItem.mScalingMode),
+            bufferItem.mTransform, bufferItem.mFence);
+
+    IGraphicBufferProducer::QueueBufferOutput queueOutput;
+
+    uint64_t bufferId = bufferItem.mGraphicBuffer->getId();
+    const BufferTracker& tracker = *(mBuffers[bufferId]);
+    int slot = getSlotForOutputLocked(output, tracker.getBuffer());
+
+    // In case the output BufferQueue has its own lock, if we hold splitter lock while calling
+    // queueBuffer (which will try to acquire the output lock), the output could be holding its
+    // own lock calling releaseBuffer (which  will try to acquire the splitter lock), running into
+    // circular lock situation.
+    mMutex.unlock();
+    res = output->queueBuffer(slot, queueInput, &queueOutput);
+    mMutex.lock();
+
+    SP_LOGV("%s: Queuing buffer to buffer queue %p slot %d returns %d",
+            __FUNCTION__, output.get(), slot, res);
+    if (res != OK) {
+        if (res != NO_INIT && res != DEAD_OBJECT) {
+            SP_LOGE("Queuing buffer to output failed (%d)", res);
+        }
+        // If we just discovered that this output has been abandoned, note
+        // that, increment the release count so that we still release this
+        // buffer eventually, and move on to the next output
+        onAbandonedLocked();
+        decrementBufRefCountLocked(bufferItem.mGraphicBuffer->getId(), output);
+        return res;
+    }
+
+    // If the queued buffer replaces a pending buffer in the async
+    // queue, no onBufferReleased is called by the buffer queue.
+    // Proactively trigger the callback to avoid buffer loss.
+    if (queueOutput.bufferReplaced) {
+        onBufferReleasedByOutputLocked(output);
+    }
+
+    return res;
+}
+
 String8 Camera3StreamSplitter::getUniqueConsumerName() {
     static volatile int32_t counter = 0;
     return String8::format("Camera3StreamSplitter-%d", android_atomic_inc(&counter));
 }
 
-status_t Camera3StreamSplitter::notifyRequestedSurfaces(
-        const std::vector<size_t>& surfaces) {
+status_t Camera3StreamSplitter::notifyBufferReleased(const sp<GraphicBuffer>& buffer) {
     ATRACE_CALL();
+    status_t res = OK;
+
     Mutex::Autolock lock(mMutex);
 
-    mRequestedSurfaces.push_back(surfaces);
-    return OK;
-}
+    uint64_t bufferId = buffer->getId();
+    std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[bufferId]);
+    mBuffers.erase(bufferId);
 
-
-void Camera3StreamSplitter::onFrameAvailable(const BufferItem& /* item */) {
-    ATRACE_CALL();
-    Mutex::Autolock lock(mMutex);
-
-    // The current policy is that if any one consumer is consuming buffers too
-    // slowly, the splitter will stall the rest of the outputs by not acquiring
-    // any more buffers from the input. This will cause back pressure on the
-    // input queue, slowing down its producer.
-
-    // If there are too many outstanding buffers, we block until a buffer is
-    // released back to the input in onBufferReleased
-    while (mOutstandingBuffers >= mMaxConsumerBuffers) {
-        mReleaseCondition.wait(mMutex);
-
-        // If the splitter is abandoned while we are waiting, the release
-        // condition variable will be broadcast, and we should just return
-        // without attempting to do anything more (since the input queue will
-        // also be abandoned).
-        if (mIsAbandoned) {
-            return;
+    for (const auto surface : tracker_ptr->requestedSurfaces()) {
+        sp<IGraphicBufferProducer>& gbp = mOutputs[surface];
+        OutputSlots& outputSlots = *(mOutputSlots[gbp]);
+        int slot = getSlotForOutputLocked(gbp, buffer);
+        if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+             gbp->detachBuffer(slot);
+             outputSlots[slot].clear();
         }
     }
-    // If the splitter is abandoned without reaching mMaxConsumerBuffers, just
-    // return without attempting to do anything more.
-    if (mIsAbandoned) {
-        return;
+
+    return res;
+}
+
+status_t Camera3StreamSplitter::attachBufferToOutputs(ANativeWindowBuffer* anb,
+        const std::vector<size_t>& surface_ids) {
+    ATRACE_CALL();
+    status_t res = OK;
+
+    Mutex::Autolock lock(mMutex);
+
+    sp<GraphicBuffer> gb(static_cast<GraphicBuffer*>(anb));
+    uint64_t bufferId = gb->getId();
+
+    // Initialize buffer tracker for this input buffer
+    auto tracker = std::make_unique<BufferTracker>(gb, surface_ids);
+
+    for (auto& surface_id : surface_ids) {
+        sp<IGraphicBufferProducer>& gbp = mOutputs[surface_id];
+        int slot = BufferItem::INVALID_BUFFER_SLOT;
+        //Temporarly Unlock the mutex when trying to attachBuffer to the output
+        //queue, because attachBuffer could block in case of a slow consumer. If
+        //we block while holding the lock, onFrameAvailable and onBufferReleased
+        //will block as well because they need to acquire the same lock.
+        mMutex.unlock();
+        res = gbp->attachBuffer(&slot, gb);
+        mMutex.lock();
+        if (res != OK) {
+            SP_LOGE("%s: Cannot acquireBuffer from GraphicBufferProducer %p: %s (%d)",
+                    __FUNCTION__, gbp.get(), strerror(-res), res);
+            return res;
+        }
+        auto& outputSlots = *mOutputSlots[gbp];
+        if (outputSlots[slot] != nullptr) {
+            // If the buffer is attached to a slot which already contains a buffer,
+            // the previous buffer will be removed from the output queue. Decrement
+            // the reference count accordingly.
+            decrementBufRefCountLocked(outputSlots[slot]->getId(), gbp);
+        }
+        SP_LOGV("%s: Attached buffer %p to slot %d on output %p.",__FUNCTION__, gb.get(),
+                slot, gbp.get());
+        outputSlots[slot] = gb;
     }
 
-    ++mOutstandingBuffers;
+    mBuffers[bufferId] = std::move(tracker);
+
+    return res;
+}
+
+void Camera3StreamSplitter::onFrameAvailable(const BufferItem& /*item*/) {
+    ATRACE_CALL();
+    Mutex::Autolock lock(mMutex);
 
     // Acquire and detach the buffer from the input
     BufferItem bufferItem;
-    status_t status = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
-    LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-            "acquiring buffer from input failed (%d)", status);
+    status_t res = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
+    if (res != NO_ERROR) {
+        SP_LOGE("%s: Acquiring buffer from input failed (%d)", __FUNCTION__, res);
+        mOnFrameAvailableRes.store(res);
+        return;
+    }
+    if (mBuffers.find(bufferItem.mGraphicBuffer->getId()) == mBuffers.end()) {
+        SP_LOGE("%s: Acquired buffer doesn't exist in attached buffer map",
+                __FUNCTION__);
+        mOnFrameAvailableRes.store(INVALID_OPERATION);
+        return;
+    }
 
-    ALOGV("acquired buffer %#" PRIx64 " from input",
-            bufferItem.mGraphicBuffer->getId());
+    SP_LOGV("acquired buffer %" PRId64 " from input at slot %d",
+            bufferItem.mGraphicBuffer->getId(), bufferItem.mSlot);
 
-    status = mConsumer->detachBuffer(bufferItem.mSlot);
-    LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-            "detaching buffer from input failed (%d)", status);
-
-    IGraphicBufferProducer::QueueBufferInput queueInput(
-            bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
-            bufferItem.mDataSpace, bufferItem.mCrop,
-            static_cast<int32_t>(bufferItem.mScalingMode),
-            bufferItem.mTransform, bufferItem.mFence);
+    res = mConsumer->detachBuffer(bufferItem.mSlot);
+    if (res != NO_ERROR) {
+        SP_LOGE("%s: detaching buffer from input failed (%d)", __FUNCTION__, res);
+        mOnFrameAvailableRes.store(res);
+        return;
+    }
 
     // Attach and queue the buffer to each of the outputs
-    std::vector<std::vector<size_t> >::iterator surfaces = mRequestedSurfaces.begin();
-    if (surfaces != mRequestedSurfaces.end()) {
+    BufferTracker& tracker = *(mBuffers[bufferItem.mGraphicBuffer->getId()]);
 
-        LOG_ALWAYS_FATAL_IF(surfaces->size() == 0,
-                "requested surface ids shouldn't be empty");
+    SP_LOGV("%s: BufferTracker for buffer %" PRId64 ", number of requests %zu",
+           __FUNCTION__, bufferItem.mGraphicBuffer->getId(), tracker.requestedSurfaces().size());
+    for (const auto id : tracker.requestedSurfaces()) {
 
-        // Initialize our reference count for this buffer
-        mBuffers[bufferItem.mGraphicBuffer->getId()] =
-                std::unique_ptr<BufferTracker>(
-                new BufferTracker(bufferItem.mGraphicBuffer, surfaces->size()));
+        LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
+                "requested surface id exceeding max registered ids");
 
-        for (auto id : *surfaces) {
-
-            LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
-                    "requested surface id exceeding max registered ids");
-
-            int slot = BufferItem::INVALID_BUFFER_SLOT;
-            status = mOutputs[id]->attachBuffer(&slot, bufferItem.mGraphicBuffer);
-            if (status == NO_INIT) {
-                // If we just discovered that this output has been abandoned, note
-                // that, decrement the reference count so that we still release this
-                // buffer eventually, and move on to the next output
-                onAbandonedLocked();
-                mBuffers[bufferItem.mGraphicBuffer->getId()]->
-                        decrementReferenceCountLocked();
-                continue;
-            } else if (status == WOULD_BLOCK) {
-                // If the output is async, attachBuffer may return WOULD_BLOCK
-                // indicating number of dequeued buffers has reached limit. In
-                // this case, simply decrement the reference count, and move on
-                // to the next output.
-                // TODO: Do we need to report BUFFER_ERROR for this result?
-                mBuffers[bufferItem.mGraphicBuffer->getId()]->
-                        decrementReferenceCountLocked();
-                continue;
-            } else if (status == TIMED_OUT) {
-                // If attachBuffer times out due to the value set by
-                // setDequeueTimeout, simply decrement the reference count, and
-                // move on to the next output.
-                // TODO: Do we need to report BUFFER_ERROR for this result?
-                mBuffers[bufferItem.mGraphicBuffer->getId()]->
-                        decrementReferenceCountLocked();
-                continue;
-            } else {
-                LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                        "attaching buffer to output failed (%d)", status);
-            }
-
-            IGraphicBufferProducer::QueueBufferOutput queueOutput;
-            status = mOutputs[id]->queueBuffer(slot, queueInput, &queueOutput);
-            if (status == NO_INIT) {
-                // If we just discovered that this output has been abandoned, note
-                // that, increment the release count so that we still release this
-                // buffer eventually, and move on to the next output
-                onAbandonedLocked();
-                mBuffers[bufferItem.mGraphicBuffer->getId()]->
-                        decrementReferenceCountLocked();
-                continue;
-            } else {
-                LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                        "queueing buffer to output failed (%d)", status);
-            }
-
-            // If the queued buffer replaces a pending buffer in the async
-            // queue, no onBufferReleased is called by the buffer queue.
-            // Proactively trigger the callback to avoid buffer loss.
-            if (queueOutput.bufferReplaced) {
-                onBufferReleasedByOutputLocked(mOutputs[id]);
-            }
-
-            ALOGV("queued buffer %#" PRIx64 " to output %p",
-                    bufferItem.mGraphicBuffer->getId(), mOutputs[id].get());
+        res = outputBufferLocked(mOutputs[id], bufferItem);
+        if (res != OK) {
+            SP_LOGE("%s: outputBufferLocked failed %d", __FUNCTION__, res);
+            mOnFrameAvailableRes.store(res);
+            // If we fail to send buffer to certain output, keep sending to
+            // other outputs.
+            continue;
         }
+    }
 
-        mRequestedSurfaces.erase(surfaces);
+    mOnFrameAvailableRes.store(res);
+}
+
+void Camera3StreamSplitter::decrementBufRefCountLocked(uint64_t id,
+        const sp<IGraphicBufferProducer>& from) {
+    ATRACE_CALL();
+    size_t referenceCount = mBuffers[id]->decrementReferenceCountLocked();
+
+    removeSlotForOutputLocked(from, mBuffers[id]->getBuffer());
+    if (referenceCount > 0) {
+        return;
+    }
+
+    // We no longer need to track the buffer now that it is being returned to the
+    // input. Note that this should happen before we unlock the mutex and call
+    // releaseBuffer, to avoid the case where the same bufferId is acquired in
+    // attachBufferToOutputs resulting in a new BufferTracker with same bufferId
+    // overwrites the current one.
+    std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[id]);
+    mBuffers.erase(id);
+
+    // Attach and release the buffer back to the input
+    int consumerSlot = BufferItem::INVALID_BUFFER_SLOT;
+    status_t res = mConsumer->attachBuffer(&consumerSlot, tracker_ptr->getBuffer());
+    if (res != NO_ERROR) {
+        SP_LOGE("%s: attaching buffer to input failed (%d)", __FUNCTION__, res);
+        return;
+    }
+
+    // Temporarily unlock mutex to avoid circular lock:
+    // 1. This function holds splitter lock, calls releaseBuffer which triggers
+    // onBufferReleased in Camera3OutputStream. onBufferReleased waits on the
+    // OutputStream lock
+    // 2. Camera3SharedOutputStream::getBufferLocked calls
+    // attachBufferToOutputs, which holds the stream lock, and waits for the
+    // splitter lock.
+    sp<IGraphicBufferConsumer> consumer(mConsumer);
+    mMutex.unlock();
+    if (consumer != nullptr) {
+        res = consumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
+                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker_ptr->getMergedFence());
+    } else {
+        SP_LOGE("%s: consumer has become null!", __FUNCTION__);
+    }
+    mMutex.lock();
+    // If the producer of this queue is disconnected, -22 error will occur
+    if (res != NO_ERROR) {
+        SP_LOGE("%s: releaseBuffer returns %d", __FUNCTION__, res);
     }
 }
 
@@ -347,73 +447,79 @@
 
 void Camera3StreamSplitter::onBufferReleasedByOutputLocked(
         const sp<IGraphicBufferProducer>& from) {
-
+    ATRACE_CALL();
     sp<GraphicBuffer> buffer;
     sp<Fence> fence;
-    status_t status = from->detachNextBuffer(&buffer, &fence);
-    if (status == NO_INIT) {
+    status_t res = from->detachNextBuffer(&buffer, &fence);
+    if (res == NO_INIT) {
         // If we just discovered that this output has been abandoned, note that,
         // but we can't do anything else, since buffer is invalid
         onAbandonedLocked();
         return;
+    } else if (res == NO_MEMORY) {
+        SP_LOGV("%s: No free buffers", __FUNCTION__);
+        return;
     } else {
-        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                "detaching buffer from output failed (%d)", status);
+        LOG_ALWAYS_FATAL_IF(res != NO_ERROR,
+                "detaching buffer from output failed (%d)", res);
     }
 
-    ALOGV("detached buffer %#" PRIx64 " from output %p",
-          buffer->getId(), from.get());
-
     BufferTracker& tracker = *(mBuffers[buffer->getId()]);
-
     // Merge the release fence of the incoming buffer so that the fence we send
     // back to the input includes all of the outputs' fences
-    tracker.mergeFence(fence);
+    if (fence != nullptr && fence->isValid()) {
+        tracker.mergeFence(fence);
+    }
+    SP_LOGV("detached buffer %" PRId64 " %p from output %p",
+            buffer->getId(), buffer.get(), from.get());
 
     // Check to see if this is the last outstanding reference to this buffer
-    size_t referenceCount = tracker.decrementReferenceCountLocked();
-    ALOGV("buffer %#" PRIx64 " reference count %zu", buffer->getId(),
-            referenceCount);
-    if (referenceCount > 0) {
-        return;
-    }
-
-    // If we've been abandoned, we can't return the buffer to the input, so just
-    // stop tracking it and move on
-    if (mIsAbandoned) {
-        mBuffers.erase(buffer->getId());
-        return;
-    }
-
-    // Attach and release the buffer back to the input
-    int consumerSlot = BufferItem::INVALID_BUFFER_SLOT;
-    status = mConsumer->attachBuffer(&consumerSlot, tracker.getBuffer());
-    LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-            "attaching buffer to input failed (%d)", status);
-
-    status = mConsumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
-            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker.getMergedFence());
-    LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-            "releasing buffer to input failed (%d)", status);
-
-    ALOGV("released buffer %#" PRIx64 " to input", buffer->getId());
-
-    // We no longer need to track the buffer once it has been returned to the
-    // input
-    mBuffers.erase(buffer->getId());
-
-    // Notify any waiting onFrameAvailable calls
-    --mOutstandingBuffers;
-    mReleaseCondition.signal();
+    decrementBufRefCountLocked(buffer->getId(), from);
 }
 
 void Camera3StreamSplitter::onAbandonedLocked() {
-    ALOGE("one of my outputs has abandoned me");
-    if (!mIsAbandoned && mConsumer != nullptr) {
-        mConsumer->consumerDisconnect();
+    // If this is called from binderDied callback, it means the app process
+    // holding the binder has died. CameraService will be notified of the binder
+    // death, and camera device will be closed, which in turn calls
+    // disconnect().
+    //
+    // If this is called from onBufferReleasedByOutput or onFrameAvailable, one
+    // consumer being abanoned shouldn't impact the other consumer. So we won't
+    // stop the buffer flow.
+    //
+    // In both cases, we don't need to do anything here.
+    SP_LOGV("One of my outputs has abandoned me");
+}
+
+int Camera3StreamSplitter::getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+        const sp<GraphicBuffer>& gb) {
+    auto& outputSlots = *mOutputSlots[gbp];
+
+    for (size_t i = 0; i < outputSlots.size(); i++) {
+        if (outputSlots[i] == gb) {
+            return (int)i;
+        }
     }
-    mIsAbandoned = true;
-    mReleaseCondition.broadcast();
+
+    SP_LOGE("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
+            gbp.get());
+    return BufferItem::INVALID_BUFFER_SLOT;
+}
+
+status_t Camera3StreamSplitter::removeSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+        const sp<GraphicBuffer>& gb) {
+    auto& outputSlots = *mOutputSlots[gbp];
+
+    for (size_t i = 0; i < outputSlots.size(); i++) {
+        if (outputSlots[i] == gb) {
+           outputSlots[i].clear();
+           return NO_ERROR;
+        }
+    }
+
+    SP_LOGE("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
+            gbp.get());
+    return BAD_VALUE;
 }
 
 Camera3StreamSplitter::OutputListener::OutputListener(
@@ -422,6 +528,7 @@
       : mSplitter(splitter), mOutput(output) {}
 
 void Camera3StreamSplitter::OutputListener::onBufferReleased() {
+    ATRACE_CALL();
     sp<Camera3StreamSplitter> splitter = mSplitter.promote();
     sp<IGraphicBufferProducer> output = mOutput.promote();
     if (splitter != nullptr && output != nullptr) {
@@ -438,9 +545,9 @@
 }
 
 Camera3StreamSplitter::BufferTracker::BufferTracker(
-        const sp<GraphicBuffer>& buffer, size_t referenceCount)
-      : mBuffer(buffer), mMergedFence(Fence::NO_FENCE),
-        mReferenceCount(referenceCount) {}
+        const sp<GraphicBuffer>& buffer, const std::vector<size_t>& requestedSurfaces)
+      : mBuffer(buffer), mMergedFence(Fence::NO_FENCE), mRequestedSurfaces(requestedSurfaces),
+        mReferenceCount(requestedSurfaces.size()) {}
 
 void Camera3StreamSplitter::BufferTracker::mergeFence(const sp<Fence>& with) {
     mMergedFence = Fence::merge(String8("Camera3StreamSplitter"), mMergedFence, with);
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 92371ff..cc623e0 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -26,6 +26,11 @@
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
+#define SP_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+
 namespace android {
 
 class GraphicBuffer;
@@ -47,8 +52,8 @@
     // Connect to the stream splitter by creating buffer queue and connecting it
     // with output surfaces.
     status_t connect(const std::vector<sp<Surface> >& surfaces,
-            uint32_t consumerUsage, size_t hal_max_buffers,
-            sp<Surface>& consumer);
+            uint32_t consumerUsage, size_t halMaxBuffers,
+            sp<Surface>* consumer);
 
     // addOutput adds an output BufferQueue to the splitter. The splitter
     // connects to outputQueue as a CPU producer, and any buffers queued
@@ -61,13 +66,22 @@
     // outputQueue has not been added to the splitter. BAD_VALUE is returned if
     // outputQueue is NULL. See IGraphicBufferProducer::connect for explanations
     // of other error codes.
-    status_t addOutput(const sp<Surface>& outputQueue, size_t hal_max_buffers);
+    status_t addOutput(const sp<Surface>& outputQueue);
 
-    // Request surfaces for a particular frame number. The requested surfaces
-    // are stored in a FIFO queue. And when the buffer becomes available from the
-    // input queue, the registered surfaces are used to decide which output is
-    // the buffer sent to.
-    status_t notifyRequestedSurfaces(const std::vector<size_t>& surfaces);
+    // Notification that the graphic buffer has been released to the input
+    // BufferQueue. The buffer should be reused by the camera device instead of
+    // queuing to the outputs.
+    status_t notifyBufferReleased(const sp<GraphicBuffer>& buffer);
+
+    // Attach a buffer to the specified outputs. This call reserves a buffer
+    // slot in the output queue.
+    status_t attachBufferToOutputs(ANativeWindowBuffer* anb,
+            const std::vector<size_t>& surface_ids);
+
+    // Get return value of onFrameAvailable to work around problem that
+    // onFrameAvailable is void. This function should be called by the producer
+    // right after calling queueBuffer().
+    status_t getOnFrameAvailableResult();
 
     // Disconnect the buffer queue from output surfaces.
     void disconnect();
@@ -115,6 +129,10 @@
     // acquire. This must be called with mMutex locked.
     void onAbandonedLocked();
 
+    // Decrement the buffer's reference count. Once the reference count becomes
+    // 0, return the buffer back to the input BufferQueue.
+    void decrementBufRefCountLocked(uint64_t id, const sp<IGraphicBufferProducer>& from);
+
     // This is a thin wrapper class that lets us determine which BufferQueue
     // the IProducerListener::onBufferReleased callback is associated with. We
     // create one of these per output BufferQueue, and then pass the producer
@@ -139,7 +157,8 @@
 
     class BufferTracker {
     public:
-        BufferTracker(const sp<GraphicBuffer>& buffer, size_t referenceCount);
+        BufferTracker(const sp<GraphicBuffer>& buffer,
+                const std::vector<size_t>& requestedSurfaces);
         ~BufferTracker() = default;
 
         const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
@@ -151,6 +170,8 @@
         // Only called while mMutex is held
         size_t decrementReferenceCountLocked();
 
+        const std::vector<size_t> requestedSurfaces() const { return mRequestedSurfaces; }
+
     private:
 
         // Disallow copying
@@ -159,39 +180,43 @@
 
         sp<GraphicBuffer> mBuffer; // One instance that holds this native handle
         sp<Fence> mMergedFence;
+
+        // Request surfaces for a particular buffer. And when the buffer becomes
+        // available from the input queue, the registered surfaces are used to decide
+        // which output is the buffer sent to.
+        std::vector<size_t> mRequestedSurfaces;
         size_t mReferenceCount;
     };
 
-    // A deferred output is an output being added to the splitter after
-    // connect() call, whereas a non deferred output is added within connect()
-    // call.
-    enum class OutputType { NonDeferred, Deferred };
-
     // Must be accessed through RefBase
     virtual ~Camera3StreamSplitter();
 
-    status_t addOutputLocked(const sp<Surface>& outputQueue,
-                             size_t hal_max_buffers, OutputType outputType);
+    status_t addOutputLocked(const sp<Surface>& outputQueue);
+
+    // Send a buffer to particular output, and increment the reference count
+    // of the buffer. If this output is abandoned, the buffer's reference count
+    // won't be incremented.
+    status_t outputBufferLocked(const sp<IGraphicBufferProducer>& output,
+            const BufferItem& bufferItem);
 
     // Get unique name for the buffer queue consumer
-    static String8 getUniqueConsumerName();
+    String8 getUniqueConsumerName();
 
-    // Max consumer side buffers for deferred surface. This will be used as a
-    // lower bound for overall consumer side max buffers.
-    static const int MAX_BUFFERS_DEFERRED_OUTPUT = 2;
-    int mMaxConsumerBuffers = MAX_BUFFERS_DEFERRED_OUTPUT;
+    // Helper function to get the BufferQueue slot where a particular buffer is attached to.
+    int getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+            const sp<GraphicBuffer>& gb);
+    // Helper function to remove the buffer from the BufferQueue slot
+    status_t removeSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+            const sp<GraphicBuffer>& gb);
+
+
+    // Sum of max consumer buffers for all outputs
+    size_t mMaxConsumerBuffers = 0;
+    size_t mMaxHalBuffers = 0;
 
     static const nsecs_t kDequeueBufferTimeout   = s2ns(1); // 1 sec
 
-    // mIsAbandoned is set to true when an output dies. Once the Camera3StreamSplitter
-    // has been abandoned, it will continue to detach buffers from other
-    // outputs, but it will disconnect from the input and not attempt to
-    // communicate with it further.
-    bool mIsAbandoned = false;
-
     Mutex mMutex;
-    Condition mReleaseCondition;
-    int mOutstandingBuffers = 0;
 
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
@@ -199,14 +224,28 @@
     sp<Surface> mSurface;
 
     std::vector<sp<IGraphicBufferProducer> > mOutputs;
-    // Tracking which outputs should the buffer be attached and queued
-    // to for each input buffer.
-    std::vector<std::vector<size_t> > mRequestedSurfaces;
-
     // Map of GraphicBuffer IDs (GraphicBuffer::getId()) to buffer tracking
     // objects (which are mostly for counting how many outputs have released the
     // buffer, but also contain merged release fences).
     std::unordered_map<uint64_t, std::unique_ptr<BufferTracker> > mBuffers;
+
+    struct GBPHash {
+        std::size_t operator()(const sp<IGraphicBufferProducer>& producer) const {
+            return std::hash<IGraphicBufferProducer *>{}(producer.get());
+        }
+    };
+
+    std::unordered_map<sp<IGraphicBufferProducer>, sp<OutputListener>,
+            GBPHash> mNotifiers;
+
+    typedef std::vector<sp<GraphicBuffer>> OutputSlots;
+    std::unordered_map<sp<IGraphicBufferProducer>, std::unique_ptr<OutputSlots>,
+            GBPHash> mOutputSlots;
+
+    // Latest onFrameAvailable return value
+    std::atomic<status_t> mOnFrameAvailableRes{0};
+
+    String8 mConsumerName;
 };
 
 } // namespace android