Merge "Move mediacodec to vendor partition."
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 9f41403..1466222 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -228,11 +228,11 @@
 void CryptoHal::setHeapBase(const sp<IMemoryHeap>& heap) {
     native_handle_t* nativeHandle = native_handle_create(1, 0);
     if (!nativeHandle) {
-        ALOGE("setHeapBase(), failed to create native handle");
+        ALOGE("setSharedBufferBase(), failed to create native handle");
         return;
     }
     if (heap == NULL) {
-        ALOGE("setHeapBase(): heap is NULL");
+        ALOGE("setSharedBufferBase(): heap is NULL");
         return;
     }
     int fd = heap->getHeapID();
@@ -244,10 +244,6 @@
     ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
 }
 
-void CryptoHal::clearHeapBase(const sp<IMemoryHeap>& heap) {
-    mHeapBases.removeItem(heap->getBase());
-}
-
 status_t CryptoHal::toSharedBuffer(const sp<IMemory>& memory, ::SharedBuffer* buffer) {
     ssize_t offset;
     size_t size;
@@ -261,8 +257,9 @@
         return UNEXPECTED_NULL;
     }
 
-    // memory must be in the declared heap
-    CHECK(mHeapBases.indexOfKey(heap->getBase()) >= 0);
+    if (mHeapBases.indexOfKey(heap->getBase()) < 0) {
+        setHeapBase(heap);
+    }
 
     buffer->bufferId = mHeapBases.valueFor(heap->getBase());
     buffer->offset = offset >= 0 ? offset : 0;
diff --git a/drm/libmediadrm/ICrypto.cpp b/drm/libmediadrm/ICrypto.cpp
index 7b70205..92c9548 100644
--- a/drm/libmediadrm/ICrypto.cpp
+++ b/drm/libmediadrm/ICrypto.cpp
@@ -36,8 +36,6 @@
     DECRYPT,
     NOTIFY_RESOLUTION,
     SET_MEDIADRM_SESSION,
-    SET_HEAP,
-    UNSET_HEAP,
 };
 
 struct BpCrypto : public BpInterface<ICrypto> {
@@ -179,23 +177,6 @@
         return reply.readInt32();
     }
 
-    virtual void setHeap(const sp<IMemoryHeap> &heap) {
-        Parcel data, reply;
-        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
-        data.writeStrongBinder(IInterface::asBinder(heap));
-        remote()->transact(SET_HEAP, data, &reply);
-        return;
-    }
-
-    virtual void unsetHeap(const sp<IMemoryHeap>& heap) {
-        Parcel data, reply;
-        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
-        data.writeStrongBinder(IInterface::asBinder(heap));
-        remote()->transact(UNSET_HEAP, data, &reply);
-        return;
-    }
-
-
 private:
     void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
         uint32_t size = reply.readInt32();
@@ -423,24 +404,6 @@
             return OK;
         }
 
-        case SET_HEAP:
-        {
-            CHECK_INTERFACE(ICrypto, data, reply);
-            sp<IMemoryHeap> heap =
-                interface_cast<IMemoryHeap>(data.readStrongBinder());
-            setHeap(heap);
-            return OK;
-        }
-
-        case UNSET_HEAP:
-        {
-            CHECK_INTERFACE(ICrypto, data, reply);
-            sp<IMemoryHeap> heap =
-                interface_cast<IMemoryHeap>(data.readStrongBinder());
-            unsetHeap(heap);
-            return OK;
-        }
-
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/include/ndk/NdkImage.h b/include/ndk/NdkImage.h
index 40c1699..3d1bacc 100644
--- a/include/ndk/NdkImage.h
+++ b/include/ndk/NdkImage.h
@@ -40,6 +40,10 @@
 
 #include "NdkMediaError.h"
 
+#if __ANDROID_API__ >= 26
+#include <android/hardware_buffer.h>
+#endif /* __ANDROID_API__ >= 26 */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -707,6 +711,53 @@
 
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 26
+
+/*
+ * Return the image back the the system and delete the AImage object from memory asynchronously.
+ *
+ * <p>Similar to {@link AImage_delete}, do NOT use the image pointer after this method returns.
+ * However, the caller can still hold on to the {@link AHardwareBuffer} returned from this image and
+ * signal the release of the hardware buffer back to the {@link AImageReader}'s queue using
+ * releaseFenceFd.</p>
+ *
+ * @param image The {@link AImage} to be deleted.
+ * @param releaseFenceFd A sync fence fd defined in {@link sync.h}, which signals the release of
+ *         underlying {@link AHardwareBuffer}.
+ *
+ * @see sync.h
+ */
+void AImage_deleteAsync(AImage* image, int releaseFenceFd);
+
+/**
+ * Get the hardware buffer handle of the input image intended for GPU and/or hardware access.
+ *
+ * <p>Note that no reference on the returned {@link AHardwareBuffer} handle is acquired
+ * automatically. Once the {@link AImage} or the parent {@link AImageReader} is deleted, the
+ * {@link AHardwareBuffer} handle from previous {@link AImage_getHardwareBuffer} becomes
+ * invalid.</p>
+ *
+ * <p>If the caller ever needs to hold on a reference to the {@link AHardwareBuffer} handle after
+ * the {@link AImage} or the parent {@link AImageReader} is deleted, it must call {@link
+ * AHardwareBuffer_acquire} to acquire an extra reference, and call {@link AHardwareBuffer_release}
+ * once it has finished using it in order to properly deallocate the underlying memory managed by
+ * {@link AHardwareBuffer}. If the caller has acquired extra reference on an {@link AHardwareBuffer}
+ * returned from this function, it must also listen to {@link onBufferFreed} callback to be
+ * notified when the buffer is no longer used by {@link AImageReader}.</p>
+ *
+ * @param image the {@link AImage} of interest.
+ * @param outBuffer The memory area pointed to by buffer will contain the acquired AHardwareBuffer
+ *         handle.
+ * @return <ul>
+ *         <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ *         <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if image or buffer is NULL</li></ul>
+ *
+ * @see AImageReader_ImageCallback
+ */
+media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer);
+
+#endif /* __ANDROID_API__ >= 26 */
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/include/ndk/NdkImageReader.h b/include/ndk/NdkImageReader.h
index 8d72c28..a158da9 100644
--- a/include/ndk/NdkImageReader.h
+++ b/include/ndk/NdkImageReader.h
@@ -300,6 +300,127 @@
 
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 26
+
+/**
+ * AImageReader constructor similar to {@link AImageReader_new} that takes an additional parameter
+ * for the consumer usage. All other parameters and the return values are identical to those passed
+ * to {@line AImageReader_new}.
+ *
+ * @param usage0 specifies how the consumer will access the AImage, using combination of the
+ *            AHARDWAREBUFFER_USAGE0 flags described in {@link hardware_buffer.h}.
+ *            Passing {@link AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN} is equivalent to calling
+ *            {@link AImageReader_new} with the same parameters. Note that consumers that do not
+ *            require CPU access to the buffer should omit {@link
+ *            AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN} to improve performance.
+ * @param usage1 specifies how the consumer will access the AImage, using combination of the
+ *            AHARDWAREBUFFER_USAGE1 flags described in {@link hardware_buffer.h}.
+ *
+ * @see AImage
+ * @see AImageReader_new
+ * @see AHardwareBuffer
+ */
+media_status_t AImageReader_newWithUsage(
+        int32_t width, int32_t height, int32_t format, uint64_t usage0,
+        uint64_t usage1, int32_t maxImages, /*out*/ AImageReader** reader);
+
+/*
+ * Acquire the next {@link AImage} from the image reader's queue asynchronously.
+ *
+ * <p>AImageReader acquire method similar to {@link AImageReader_acquireNextImage} that takes an
+ * additional parameter for the sync fence. All other parameters and the return values are
+ * identical to those passed to {@link AImageReader_acquireNextImage}.</p>
+ *
+ * @param acquireFenceFd A sync fence fd defined in {@link sync.h}, which is used to signal when the
+ *         buffer is ready to consume. When synchronization fence is not needed, fence will be set
+ *         to -1 and the {@link AImage} returned is ready for use immediately. Otherwise, user shall
+ *         use syscalls such as {@code poll()}, {@code epoll()}, {@code select()} to wait for the
+ *         fence fd to change status before attempting to access the {@link AImage} returned.
+ *
+ * @see sync.h
+ * @see sync_get_fence_info
+ */
+media_status_t AImageReader_acquireNextImageAsync(
+        AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd);
+
+/*
+ * Acquire the latest {@link AImage} from the image reader's queue asynchronously, dropping older
+ * images.
+ *
+ * <p>AImageReader acquire method similar to {@link AImageReader_acquireLatestImage} that takes an
+ * additional parameter for the sync fence. All other parameters and the return values are
+ * identical to those passed to {@link AImageReader_acquireLatestImage}.</p>
+ *
+ * @param acquireFenceFd A sync fence fd defined in {@link sync.h}, which is used to signal when the
+ *         buffer is ready to consume. When synchronization fence is not needed, fence will be set
+ *         to -1 and the {@link AImage} returned is ready for use immediately. Otherwise, user shall
+ *         use syscalls such as {@code poll()}, {@code epoll()}, {@code select()} to wait for the
+ *         fence fd to change status before attempting to access the {@link AImage} returned.
+ *
+ * @see sync.h
+ * @see sync_get_fence_info
+ */
+media_status_t AImageReader_acquireLatestImageAsync(
+        AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd);
+/**
+ * The definition of {@link AImageReader} buffer removed callback.
+ *
+ * @param context The optional application context provided by user in
+ *                {@link AImageReader_setBufferRemovedListener}.
+ * @param reader The {@link AImageReader} of interest.
+ * @param buffer The {@link AHardwareBuffer} that is being removed from this image reader.
+ */
+typedef void (*AImageReader_BufferRemovedCallback)(void* context,
+        AImageReader* reader,
+        AHardwareBuffer* buffer);
+
+typedef struct AImageReader_BufferRemovedListener {
+    /// optional application context.
+    void*                      context;
+
+    /**
+     * This callback is called when an old {@link AHardwareBuffer} is about to be removed from the
+     * image reader.
+     *
+     * <p>Note that registering this callback is optional unless the user holds on extra reference
+     * to {@link AHardwareBuffer} returned from {@link AImage_getHardwareBuffer} by calling {@link
+     * AHardwareBuffer_acquire} or creating external graphic objects, such as EglImage, from it.</p>
+     *
+     * <p>If the callback is registered, the {@link AImageReader} will hold on the last of its
+     * references to the {@link AHardwareBuffer} until this callback returns. User can use the
+     * callback to get notified that it becomes the last owner of the buffer. It is up to the user
+     * to decide to either 1) immediately release all of its references to the buffer; or 2) keep
+     * using the buffer and release it in future. Note that, if option 2 if used, user of this API
+     * is responsible to deallocate the buffer properly by calling {@link AHardwareBuffer_release}.
+     * </p>
+     *
+     * @see AHardwareBuffer_release
+     * @see AImage_getHardwareBuffer
+     */
+    AImageReader_BufferRemovedCallback onBufferRemoved;
+} AImageReader_BufferRemovedListener;
+
+/**
+ * Set the onBufferRemoved listener of this image reader.
+ *
+ * <p>Note that calling this method will replace previously registered listeners.</p>
+ *
+ * @param reader The image reader of interest.
+ * @param listener the {@link AImageReader_BufferRemovedListener} to be registered. Set this to
+ * NULL if application no longer needs to listen to buffer removed events.
+ *
+ * @return <ul>
+ *         <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ *         <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL.</li></ul>
+ *
+ * @see AImage_getHardwareBuffer
+ */
+media_status_t AImageReader_setBufferRemovedListener(
+        AImageReader* reader, AImageReader_BufferRemovedListener* listener);
+
+#endif /* __ANDROID_API__ >= 26 */
+
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/DevicesFactoryHalHidl.cpp
index 758a5805..fc2645e 100644
--- a/media/libaudiohal/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/DevicesFactoryHalHidl.cpp
@@ -45,7 +45,8 @@
         // and thus have the same lifespan.
         mDevicesFactory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
     } else {
-        LOG_ALWAYS_FATAL("Failed to obtain IDevicesFactory service");
+        ALOGE("Failed to obtain IDevicesFactory service, terminating process.");
+        exit(1);
     }
 }
 
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.cpp b/media/libaudiohal/EffectsFactoryHalHidl.cpp
index f7dbb9c..605c059 100644
--- a/media/libaudiohal/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/EffectsFactoryHalHidl.cpp
@@ -44,7 +44,10 @@
 
 EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") {
     mEffectsFactory = IEffectsFactory::getService();
-    LOG_ALWAYS_FATAL_IF(mEffectsFactory == 0, "Failed to obtain IEffectsFactory service");
+    if (mEffectsFactory == 0) {
+        ALOGE("Failed to obtain IEffectsFactory service, terminating process.");
+        exit(1);
+    }
 }
 
 EffectsFactoryHalHidl::~EffectsFactoryHalHidl() {
diff --git a/media/libmedia/include/Crypto.h b/media/libmedia/include/Crypto.h
index b68413d..ce08f98 100644
--- a/media/libmedia/include/Crypto.h
+++ b/media/libmedia/include/Crypto.h
@@ -55,9 +55,6 @@
             const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
             const DestinationBuffer &destination, AString *errorDetailMsg);
 
-    virtual void setHeap(const sp<IMemoryHeap>&) {}
-    virtual void unsetHeap(const sp<IMemoryHeap>&) {}
-
 private:
     mutable Mutex mLock;
 
diff --git a/media/libmedia/include/CryptoHal.h b/media/libmedia/include/CryptoHal.h
index d6214c2..28ade20 100644
--- a/media/libmedia/include/CryptoHal.h
+++ b/media/libmedia/include/CryptoHal.h
@@ -60,9 +60,6 @@
             const ICrypto::DestinationBuffer &destination,
             AString *errorDetailMsg);
 
-    virtual void setHeap(const sp<IMemoryHeap>& heap) { setHeapBase(heap); }
-    virtual void unsetHeap(const sp<IMemoryHeap>& heap) { clearHeapBase(heap); }
-
 private:
     mutable Mutex mLock;
 
@@ -85,7 +82,6 @@
             const uint8_t uuid[16], const void *initData, size_t size);
 
     void setHeapBase(const sp<IMemoryHeap>& heap);
-    void clearHeapBase(const sp<IMemoryHeap>& heap);
 
     status_t toSharedBuffer(const sp<IMemory>& memory, ::SharedBuffer* buffer);
 
diff --git a/media/libmedia/include/ICrypto.h b/media/libmedia/include/ICrypto.h
index f83c846..8990f4b 100644
--- a/media/libmedia/include/ICrypto.h
+++ b/media/libmedia/include/ICrypto.h
@@ -27,7 +27,6 @@
 
 struct AString;
 class IMemory;
-class IMemoryHeap;
 
 struct ICrypto : public IInterface {
     DECLARE_META_INTERFACE(Crypto);
@@ -65,13 +64,6 @@
             const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
             const DestinationBuffer &destination, AString *errorDetailMsg) = 0;
 
-    /**
-     * Declare the heap that the shared memory source buffers passed
-     * to decrypt will be allocated from.
-     */
-    virtual void setHeap(const sp<IMemoryHeap>& heap) = 0;
-    virtual void unsetHeap(const sp<IMemoryHeap>& heap) = 0;
-
 private:
     DISALLOW_EVIL_CONSTRUCTORS(ICrypto);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 621347d..66b64f8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -93,7 +93,7 @@
     mLooper->stop();
 
     // finalize any pending metrics, usually a no-op.
-    finalizeMetrics("destructor");
+    updateMetrics("destructor");
     logMetrics("destructor");
 
     if (mAnalyticsItem != NULL) {
@@ -511,11 +511,11 @@
     return OK;
 }
 
-void NuPlayerDriver::finalizeMetrics(const char *where) {
+void NuPlayerDriver::updateMetrics(const char *where) {
     if (where == NULL) {
         where = "unknown";
     }
-    ALOGV("finalizeMetrics(%p) from %s at state %d", this, where, mState);
+    ALOGV("updateMetrics(%p) from %s at state %d", this, where, mState);
 
     // gather the final stats for this record
     Vector<sp<AMessage>> trackStats;
@@ -560,18 +560,16 @@
                 }
             }
         }
-
-        // getDuration() uses mLock for mutex -- careful where we use it.
-        int duration_ms = -1;
-        getDuration(&duration_ms);
-        if (duration_ms != -1) {
-            mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
-        }
-
-        if (mPlayingTimeUs > 0) {
-            mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
-        }
     }
+
+    // always provide duration and playing time, even if they have 0/unknown values.
+
+    // getDuration() uses mLock for mutex -- careful where we use it.
+    int duration_ms = -1;
+    getDuration(&duration_ms);
+    mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
+
+    mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
 }
 
 
@@ -605,7 +603,7 @@
 status_t NuPlayerDriver::reset() {
     ALOGD("reset(%p) at state %d", this, mState);
 
-    finalizeMetrics("reset");
+    updateMetrics("reset");
     logMetrics("reset");
 
     Mutex::Autolock autoLock(mLock);
@@ -731,7 +729,7 @@
     if (key == FOURCC('m','t','r','X')) {
         // mtrX -- a play on 'metrics' (not matrix)
         // gather current info all together, parcel it, and send it back
-        finalizeMetrics("api");
+        updateMetrics("api");
         mAnalyticsItem->writeToParcel(reply);
         return OK;
     }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 082f71a..c5ddcb0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -137,7 +137,7 @@
     bool mLooping;
     bool mAutoLoop;
 
-    void finalizeMetrics(const char *where);
+    void updateMetrics(const char *where);
     void logMetrics(const char *where);
 
     status_t prepare_l();
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 9cb0cdc..259e134 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -39,12 +39,6 @@
 using BufferInfo = ACodecBufferChannel::BufferInfo;
 using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;
 
-ACodecBufferChannel::~ACodecBufferChannel() {
-    if (mCrypto != nullptr && mDealer != nullptr) {
-        mCrypto->unsetHeap(mDealer->getMemoryHeap());
-    }
-}
-
 static BufferInfoIterator findClientBuffer(
         const std::shared_ptr<const std::vector<const BufferInfo>> &array,
         const sp<MediaCodecBuffer> &buffer) {
@@ -257,18 +251,6 @@
     }
 }
 
-sp<MemoryDealer> ACodecBufferChannel::makeMemoryDealer(size_t heapSize) {
-    sp<MemoryDealer> dealer;
-    if (mDealer != nullptr && mCrypto != nullptr) {
-        mCrypto->unsetHeap(mDealer->getMemoryHeap());
-    }
-    dealer = new MemoryDealer(heapSize, "ACodecBufferChannel");
-    if (mCrypto != nullptr) {
-        mCrypto->setHeap(dealer->getMemoryHeap());
-    }
-    return dealer;
-}
-
 void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
     if (hasCryptoOrDescrambler()) {
         size_t totalSize = std::accumulate(
@@ -283,10 +265,8 @@
                 (size_t max, const BufferAndId& elem) {
                     return std::max(max, align(elem.mBuffer->capacity(), alignment));
                 });
-        size_t destinationBufferSize = maxSize;
-        size_t heapSize = totalSize + destinationBufferSize;
-        mDealer = makeMemoryDealer(heapSize);
-        mDecryptDestination = mDealer->allocate(destinationBufferSize);
+        mDealer = new MemoryDealer(totalSize + maxSize, "ACodecBufferChannel");
+        mDecryptDestination = mDealer->allocate(maxSize);
     }
     std::vector<const BufferInfo> inputBuffers;
     for (const BufferAndId &elem : array) {
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index b731666..02468c1 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -60,7 +60,7 @@
 
     ACodecBufferChannel(
             const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained);
-    virtual ~ACodecBufferChannel();
+    virtual ~ACodecBufferChannel() = default;
 
     // BufferChannelBase interface
     virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
@@ -127,8 +127,6 @@
     std::shared_ptr<const std::vector<const BufferInfo>> mInputBuffers;
     std::shared_ptr<const std::vector<const BufferInfo>> mOutputBuffers;
 
-    sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
-
     bool hasCryptoOrDescrambler() {
         return mCrypto != NULL || mDescrambler != NULL;
     }
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 793ecb8..dae1ee9 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -379,7 +379,7 @@
     // the action are overridden by the last action. For the other cases, traverse
     // the Queue to find the newest action that with timestamp smaller or equal to
     // the buffer's timestamp. For example, an action queue like
-    // [pause, 1s], [resume 2us], [pause 3us], [resume 4us], [pause 5us].... Upon
+    // [pause 1us], [resume 2us], [pause 3us], [resume 4us], [pause 5us].... Upon
     // receiving a buffer with timestamp 3.5us, only the action [pause, 3us] needs
     // to be handled and [pause, 1us], [resume 2us] will be discarded.
     bool dropped = false;
@@ -398,15 +398,14 @@
         }
 
         if (!done) {
+            // Find the newest action that with timestamp smaller than itemTimeUs. Then
+            // remove all the actions before and include the newest action.
             List<ActionItem>::iterator it = mActionQueue.begin();
-            while(it != mActionQueue.end()) {
+            while (it != mActionQueue.end() && it->mActionTimeUs <= itemTimeUs) {
                 nextAction = *it;
-                mActionQueue.erase(it);
-                if (nextAction.mActionTimeUs > itemTimeUs) {
-                    break;
-                }
                 ++it;
             }
+            mActionQueue.erase(mActionQueue.begin(), it);
 
             CHECK(itemTimeUs >= nextAction.mActionTimeUs);
             switch (nextAction.mAction) {
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index 67b0ab1..bd4a968 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -56,6 +56,7 @@
     liblog \
     libutils \
     libcutils \
+    libandroid \
     libandroid_runtime \
     libbinder \
     libgui \
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 6c9a644..95fdf36 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -31,10 +31,10 @@
 
 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
-AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage,
+AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage0, uint64_t usage1,
         BufferItem* buffer, int64_t timestamp,
         int32_t width, int32_t height, int32_t numPlanes) :
-        mReader(reader), mFormat(format), mUsage(usage),
+        mReader(reader), mFormat(format), mUsage0(usage0), mUsage1(usage1),
         mBuffer(buffer), mLockedBuffer(nullptr), mTimestamp(timestamp),
         mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
 }
@@ -54,7 +54,7 @@
 }
 
 void
-AImage::close() {
+AImage::close(int releaseFenceFd) {
     Mutex::Autolock _l(mLock);
     if (mIsClosed) {
         return;
@@ -64,7 +64,7 @@
         LOG_ALWAYS_FATAL("Error: AImage not closed before AImageReader close!");
         return;
     }
-    reader->releaseImageLocked(this);
+    reader->releaseImageLocked(this, releaseFenceFd);
     // Should have been set to nullptr in releaseImageLocked
     // Set to nullptr here for extra safety only
     mBuffer = nullptr;
@@ -178,9 +178,9 @@
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
 
-    if ((mUsage & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN) == 0) {
+    if ((mUsage0 & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN) == 0) {
         ALOGE("%s: AImage %p does not have any software read usage bits set, usage=%" PRIu64 "",
-              __FUNCTION__, this, mUsage);
+              __FUNCTION__, this, mUsage0);
         return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
     }
 
@@ -194,7 +194,7 @@
     uint64_t producerUsage;
     uint64_t consumerUsage;
     android_hardware_HardwareBuffer_convertToGrallocUsageBits(
-            &producerUsage, &consumerUsage, mUsage, 0);
+            &producerUsage, &consumerUsage, mUsage0, mUsage1);
 
     status_t ret =
             lockImageFromBuffer(mBuffer, consumerUsage, mBuffer->mFence->dup(), lockedBuffer.get());
@@ -600,12 +600,31 @@
     return AMEDIA_OK;
 }
 
+media_status_t
+AImage::getHardwareBuffer(/*out*/AHardwareBuffer** buffer) const {
+    if (mBuffer == nullptr || mBuffer->mGraphicBuffer == nullptr) {
+        ALOGE("%s: AImage %p has no buffer.", __FUNCTION__, this);
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+
+    // TODO(jwcai) Someone from Android graphics team stating this should just be a static_cast.
+    *buffer = reinterpret_cast<AHardwareBuffer*>(mBuffer->mGraphicBuffer.get());
+    return AMEDIA_OK;
+}
+
 EXPORT
 void AImage_delete(AImage* image) {
     ALOGV("%s", __FUNCTION__);
+    AImage_deleteAsync(image, -1);
+    return;
+}
+
+EXPORT
+void AImage_deleteAsync(AImage* image, int releaseFenceFd) {
+    ALOGV("%s", __FUNCTION__);
     if (image != nullptr) {
         image->lockReader();
-        image->close();
+        image->close(releaseFenceFd);
         image->unlockReader();
         if (!image->isClosed()) {
             LOG_ALWAYS_FATAL("Image close failed!");
@@ -750,3 +769,15 @@
     }
     return image->getPlaneData(planeIdx, data, dataLength);
 }
+
+EXPORT
+media_status_t AImage_getHardwareBuffer(
+    const AImage* image, /*out*/AHardwareBuffer** buffer) {
+    ALOGV("%s", __FUNCTION__);
+
+    if (image == nullptr || buffer == nullptr) {
+        ALOGE("%s: bad argument. image %p buffer %p", __FUNCTION__, image, buffer);
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+    return image->getHardwareBuffer(buffer);
+}
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
index e01dcc7..1fcb495 100644
--- a/media/ndk/NdkImagePriv.h
+++ b/media/ndk/NdkImagePriv.h
@@ -32,12 +32,13 @@
 
 // TODO: this only supports ImageReader
 struct AImage {
-    AImage(AImageReader* reader, int32_t format, uint64_t usage,
+    AImage(AImageReader* reader, int32_t format, uint64_t usage0, uint64_t usage1,
            BufferItem* buffer, int64_t timestamp,
            int32_t width, int32_t height, int32_t numPlanes);
 
     // free all resources while keeping object alive. Caller must obtain reader lock
-    void close();
+    void close() { close(-1); }
+    void close(int releaseFenceFd);
 
     // Remove from object memory. Must be called after close
     void free();
@@ -61,6 +62,7 @@
     media_status_t getPlanePixelStride(int planeIdx, /*out*/int32_t* pixelStride) const;
     media_status_t getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const;
     media_status_t getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const;
+    media_status_t getHardwareBuffer(/*out*/AHardwareBuffer** buffer) const;
 
   private:
     // AImage should be deleted through free() API.
@@ -73,7 +75,8 @@
     // When reader is close, AImage will only accept close API call
     wp<AImageReader>           mReader;
     const int32_t              mFormat;
-    const uint64_t             mUsage;  // AHARDWAREBUFFER_USAGE0* flags.
+    const uint64_t             mUsage0;  // AHARDWAREBUFFER_USAGE0* flags.
+    const uint64_t             mUsage1;  // AHARDWAREBUFFER_USAGE1* flags.
     BufferItem*                mBuffer;
     std::unique_ptr<CpuConsumer::LockedBuffer> mLockedBuffer;
     const int64_t              mTimestamp;
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index e580dae..c0aee90 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -38,9 +38,9 @@
     }
 }
 
-const int32_t AImageReader::kDefaultUsage = AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN;
 const char* AImageReader::kCallbackFpKey = "Callback";
 const char* AImageReader::kContextKey    = "Context";
+const char* AImageReader::kGraphicBufferKey = "GraphicBuffer";
 
 bool
 AImageReader::isSupportedFormat(int32_t format) {
@@ -117,6 +117,45 @@
     return AMEDIA_OK;
 }
 
+void
+AImageReader::BufferRemovedListener::onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) {
+    Mutex::Autolock _l(mLock);
+    sp<AImageReader> reader = mReader.promote();
+    if (reader == nullptr) {
+        ALOGW("A frame is available after AImageReader closed!");
+        return; // reader has been closed
+    }
+    if (mListener.onBufferRemoved == nullptr) {
+        return; // No callback registered
+    }
+
+    sp<GraphicBuffer> gBuffer = graphicBuffer.promote();
+    if (gBuffer == nullptr) {
+        ALOGW("A buffer being freed has gone away!");
+        return; // buffer is already destroyed
+    }
+
+    sp<AMessage> msg = new AMessage(AImageReader::kWhatBufferRemoved, reader->mHandler);
+    msg->setPointer(
+        AImageReader::kCallbackFpKey, (void*) mListener.onBufferRemoved);
+    msg->setPointer(AImageReader::kContextKey, mListener.context);
+    msg->setObject(AImageReader::kGraphicBufferKey, gBuffer);
+    msg->post();
+}
+
+media_status_t
+AImageReader::BufferRemovedListener::setBufferRemovedListener(
+    AImageReader_BufferRemovedListener* listener) {
+    Mutex::Autolock _l(mLock);
+    if (listener == nullptr) {
+        mListener.context = nullptr;
+        mListener.onBufferRemoved = nullptr;
+    } else {
+        mListener = *listener;
+    }
+    return AMEDIA_OK;
+}
+
 media_status_t
 AImageReader::setImageListenerLocked(AImageReader_ImageListener* listener) {
     return mFrameListener->setImageListener(listener);
@@ -128,9 +167,50 @@
     return setImageListenerLocked(listener);
 }
 
+media_status_t
+AImageReader::setBufferRemovedListenerLocked(AImageReader_BufferRemovedListener* listener) {
+    return mBufferRemovedListener->setBufferRemovedListener(listener);
+}
+
+media_status_t
+AImageReader::setBufferRemovedListener(AImageReader_BufferRemovedListener* listener) {
+    Mutex::Autolock _l(mLock);
+    return setBufferRemovedListenerLocked(listener);
+}
+
 void AImageReader::CallbackHandler::onMessageReceived(
         const sp<AMessage> &msg) {
     switch (msg->what()) {
+        case kWhatBufferRemoved:
+        {
+            AImageReader_BufferRemovedCallback onBufferRemoved;
+            void* context;
+            bool found = msg->findPointer(kCallbackFpKey, (void**) &onBufferRemoved);
+            if (!found || onBufferRemoved == nullptr) {
+                ALOGE("%s: Cannot find onBufferRemoved callback fp!", __FUNCTION__);
+                return;
+            }
+            found = msg->findPointer(kContextKey, &context);
+            if (!found) {
+                ALOGE("%s: Cannot find callback context!", __FUNCTION__);
+                return;
+            }
+            sp<RefBase> bufferToFree;
+            found = msg->findObject(kGraphicBufferKey, &bufferToFree);
+            if (!found || bufferToFree == nullptr) {
+                ALOGE("%s: Cannot find the buffer to free!", __FUNCTION__);
+                return;
+            }
+
+            // TODO(jwcai) Someone from Android graphics team stating this should just be a
+            // static_cast.
+            AHardwareBuffer* outBuffer = reinterpret_cast<AHardwareBuffer*>(bufferToFree.get());
+
+            // At this point, bufferToFree holds the last reference to the GraphicBuffer owned by
+            // this AImageReader, and the reference will be gone once this function returns.
+            (*onBufferRemoved)(context, mReader, outBuffer);
+            break;
+        }
         case kWhatImageAvailable:
         {
             AImageReader_ImageCallback onImageAvailable;
@@ -157,15 +237,18 @@
 AImageReader::AImageReader(int32_t width,
                            int32_t height,
                            int32_t format,
-                           uint64_t usage,
+                           uint64_t usage0,
+                           uint64_t usage1,
                            int32_t maxImages)
     : mWidth(width),
       mHeight(height),
       mFormat(format),
-      mUsage(usage),
+      mUsage0(usage0),
+      mUsage1(usage1),
       mMaxImages(maxImages),
       mNumPlanes(getNumPlanesForFormat(format)),
-      mFrameListener(new FrameListener(this)) {}
+      mFrameListener(new FrameListener(this)),
+      mBufferRemovedListener(new BufferRemovedListener(this)) {}
 
 media_status_t
 AImageReader::init() {
@@ -176,18 +259,19 @@
     uint64_t producerUsage;
     uint64_t consumerUsage;
     android_hardware_HardwareBuffer_convertToGrallocUsageBits(
-            &producerUsage, &consumerUsage, mUsage, 0);
+            &producerUsage, &consumerUsage, mUsage0, mUsage1);
+    mHalUsage = consumerUsage;
 
     sp<IGraphicBufferProducer> gbProducer;
     sp<IGraphicBufferConsumer> gbConsumer;
     BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
 
-    String8 consumerName = String8::format(
-            "ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d", mWidth, mHeight, mFormat, mUsage,
-            mMaxImages, getpid(), createProcessUniqueId());
+    String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "u%" PRIu64 "m%d-%d-%d",
+            mWidth, mHeight, mFormat, mUsage0, mUsage1, mMaxImages, getpid(),
+            createProcessUniqueId());
 
     mBufferItemConsumer =
-            new BufferItemConsumer(gbConsumer, consumerUsage, mMaxImages, /*controlledByApp*/ true);
+            new BufferItemConsumer(gbConsumer, mHalUsage, mMaxImages, /*controlledByApp*/ true);
     if (mBufferItemConsumer == nullptr) {
         ALOGE("Failed to allocate BufferItemConsumer");
         return AMEDIA_ERROR_UNKNOWN;
@@ -196,6 +280,7 @@
     mProducer = gbProducer;
     mBufferItemConsumer->setName(consumerName);
     mBufferItemConsumer->setFrameAvailableListener(mFrameListener);
+    mBufferItemConsumer->setBufferFreedListener(mBufferRemovedListener);
 
     status_t res;
     res = mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
@@ -247,6 +332,9 @@
     AImageReader_ImageListener nullListener = {nullptr, nullptr};
     setImageListenerLocked(&nullListener);
 
+    AImageReader_BufferRemovedListener nullBufferRemovedListener = {nullptr, nullptr};
+    setBufferRemovedListenerLocked(&nullBufferRemovedListener);
+
     if (mCbLooper != nullptr) {
         mCbLooper->unregisterHandler(mHandler->id());
         mCbLooper->stop();
@@ -274,7 +362,7 @@
 }
 
 media_status_t
-AImageReader::acquireImageLocked(/*out*/AImage** image) {
+AImageReader::acquireImageLocked(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
     *image = nullptr;
     BufferItem* buffer = getBufferItemLocked();
     if (buffer == nullptr) {
@@ -283,7 +371,10 @@
         return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
     }
 
-    status_t res = mBufferItemConsumer->acquireBuffer(buffer, 0);
+    // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
+    bool waitForFence = acquireFenceFd == nullptr;
+    status_t res = mBufferItemConsumer->acquireBuffer(buffer, 0, waitForFence);
+
     if (res != NO_ERROR) {
         returnBufferItemLocked(buffer);
         if (res != BufferQueue::NO_BUFFER_AVAILABLE) {
@@ -301,10 +392,12 @@
     const int bufferWidth = getBufferWidth(buffer);
     const int bufferHeight = getBufferHeight(buffer);
     const int bufferFmt = buffer->mGraphicBuffer->getPixelFormat();
+    const int bufferUsage = buffer->mGraphicBuffer->getUsage();
 
     const int readerWidth = mWidth;
     const int readerHeight = mHeight;
     const int readerFmt = mHalFormat;
+    const int readerUsage = mHalUsage;
 
     // Check if the producer buffer configurations match what AImageReader configured. Add some
     // extra checks for non-opaque formats.
@@ -324,6 +417,13 @@
                     __FUNCTION__, bufferWidth, bufferHeight, readerWidth, readerHeight);
         }
 
+        // Check if the buffer usage is a super set of reader's usage bits, aka all usage bits that
+        // ImageReader requested has been supported from the producer side.
+        ALOGD_IF((readerUsage | bufferUsage) != bufferUsage,
+                "%s: Producer buffer usage: %x, doesn't cover all usage bits AImageReader "
+                "configured: %x",
+                __FUNCTION__, bufferUsage, readerUsage);
+
         if (readerFmt != bufferFmt) {
             if (readerFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && isPossiblyYUV(bufferFmt)) {
                 // Special casing for when producer switches to a format compatible with flexible
@@ -345,13 +445,19 @@
     }
 
     if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
-        *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
-                            readerWidth, readerHeight, mNumPlanes);
+        *image = new AImage(this, mFormat, mUsage0, mUsage1, buffer, buffer->mTimestamp,
+                readerWidth, readerHeight, mNumPlanes);
     } else {
-        *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
-                            bufferWidth, bufferHeight, mNumPlanes);
+        *image = new AImage(this, mFormat, mUsage0, mUsage1, buffer, buffer->mTimestamp,
+                bufferWidth, bufferHeight, mNumPlanes);
     }
     mAcquiredImages.push_back(*image);
+
+    // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
+    if (acquireFenceFd != nullptr) {
+        *acquireFenceFd = buffer->mFence->dup();
+    }
+
     return AMEDIA_OK;
 }
 
@@ -373,7 +479,7 @@
 }
 
 void
-AImageReader::releaseImageLocked(AImage* image) {
+AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd) {
     BufferItem* buffer = image->mBuffer;
     if (buffer == nullptr) {
         // This should not happen, but is not fatal
@@ -381,15 +487,17 @@
         return;
     }
 
-    int fenceFd = -1;
-    media_status_t ret = image->unlockImageIfLocked(&fenceFd);
+    int unlockFenceFd = -1;
+    media_status_t ret = image->unlockImageIfLocked(&unlockFenceFd);
     if (ret < 0) {
         ALOGW("%s: AImage %p is cannot be unlocked.", __FUNCTION__, image);
         return;
     }
 
-    sp<Fence> releaseFence = fenceFd > 0 ? new Fence(fenceFd) : Fence::NO_FENCE;
-    mBufferItemConsumer->releaseBuffer(*buffer, releaseFence);
+    sp<Fence> unlockFence = unlockFenceFd > 0 ? new Fence(unlockFenceFd) : Fence::NO_FENCE;
+    sp<Fence> releaseFence = releaseFenceFd > 0 ? new Fence(releaseFenceFd) : Fence::NO_FENCE;
+    sp<Fence> bufferFence = Fence::merge("AImageReader", unlockFence, releaseFence);
+    mBufferItemConsumer->releaseBuffer(*buffer, bufferFence);
     returnBufferItemLocked(buffer);
     image->mBuffer = nullptr;
 
@@ -433,13 +541,13 @@
 }
 
 media_status_t
-AImageReader::acquireNextImage(/*out*/AImage** image) {
+AImageReader::acquireNextImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
     Mutex::Autolock _l(mLock);
-    return acquireImageLocked(image);
+    return acquireImageLocked(image, acquireFenceFd);
 }
 
 media_status_t
-AImageReader::acquireLatestImage(/*out*/AImage** image) {
+AImageReader::acquireLatestImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
     if (image == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
@@ -447,17 +555,26 @@
     *image = nullptr;
     AImage* prevImage = nullptr;
     AImage* nextImage = nullptr;
-    media_status_t ret = acquireImageLocked(&prevImage);
+    media_status_t ret = acquireImageLocked(&prevImage, acquireFenceFd);
     if (prevImage == nullptr) {
         return ret;
     }
     for (;;) {
-        ret = acquireImageLocked(&nextImage);
+        ret = acquireImageLocked(&nextImage, acquireFenceFd);
         if (nextImage == nullptr) {
             *image = prevImage;
             return AMEDIA_OK;
         }
-        prevImage->close();
+
+        if (acquireFenceFd == nullptr) {
+            // No need for release fence here since the prevImage is unused and acquireImageLocked
+            // has already waited for acquired fence to be signaled.
+            prevImage->close();
+        } else {
+            // Use the acquire fence as release fence, so that producer can wait before trying to
+            // refill the buffer.
+            prevImage->close(*acquireFenceFd);
+        }
         prevImage->free();
         prevImage = nextImage;
         nextImage = nullptr;
@@ -469,6 +586,15 @@
         int32_t width, int32_t height, int32_t format, int32_t maxImages,
         /*out*/AImageReader** reader) {
     ALOGV("%s", __FUNCTION__);
+    return AImageReader_newWithUsage(
+            width, height, format, AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, 0, maxImages, reader);
+}
+
+EXPORT
+media_status_t AImageReader_newWithUsage(
+        int32_t width, int32_t height, int32_t format, uint64_t usage0, uint64_t usage1,
+        int32_t maxImages, /*out*/ AImageReader** reader) {
+    ALOGV("%s", __FUNCTION__);
 
     if (width < 1 || height < 1) {
         ALOGE("%s: image dimension must be positive: w:%d h:%d",
@@ -499,10 +625,8 @@
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
-    // Set consumer usage to AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN by default so that
-    // AImageReader_new behaves as if it's backed by CpuConsumer.
     AImageReader* tmpReader = new AImageReader(
-        width, height, format, AImageReader::kDefaultUsage, maxImages);
+        width, height, format, usage0, usage1, maxImages);
     if (tmpReader == nullptr) {
         ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
         return AMEDIA_ERROR_UNKNOWN;
@@ -590,23 +714,37 @@
 EXPORT
 media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image) {
     ALOGV("%s", __FUNCTION__);
-    if (reader == nullptr || image == nullptr) {
-        ALOGE("%s: invalid argument. reader %p, image %p",
-                __FUNCTION__, reader, image);
-        return AMEDIA_ERROR_INVALID_PARAMETER;
-    }
-    return reader->acquireNextImage(image);
+    return AImageReader_acquireNextImageAsync(reader, image, nullptr);
 }
 
 EXPORT
 media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image) {
     ALOGV("%s", __FUNCTION__);
+    return AImageReader_acquireLatestImageAsync(reader, image, nullptr);
+}
+
+EXPORT
+media_status_t AImageReader_acquireNextImageAsync(
+    AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
+    ALOGV("%s", __FUNCTION__);
     if (reader == nullptr || image == nullptr) {
         ALOGE("%s: invalid argument. reader %p, image %p",
                 __FUNCTION__, reader, image);
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
-    return reader->acquireLatestImage(image);
+    return reader->acquireNextImage(image, acquireFenceFd);
+}
+
+EXPORT
+media_status_t AImageReader_acquireLatestImageAsync(
+    AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
+    ALOGV("%s", __FUNCTION__);
+    if (reader == nullptr || image == nullptr) {
+        ALOGE("%s: invalid argument. reader %p, image %p",
+                __FUNCTION__, reader, image);
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+    return reader->acquireLatestImage(image, acquireFenceFd);
 }
 
 EXPORT
@@ -621,3 +759,16 @@
     reader->setImageListener(listener);
     return AMEDIA_OK;
 }
+
+EXPORT
+media_status_t AImageReader_setBufferRemovedListener(
+    AImageReader* reader, AImageReader_BufferRemovedListener* listener) {
+    ALOGV("%s", __FUNCTION__);
+    if (reader == nullptr) {
+        ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+
+    reader->setBufferRemovedListener(listener);
+    return AMEDIA_OK;
+}
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 8becb1d..a233ec8 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -49,15 +49,14 @@
 
 struct AImageReader : public RefBase {
   public:
-    static const int32_t kDefaultUsage;
-
     static bool isSupportedFormat(int32_t format);
     static int getNumPlanesForFormat(int32_t format);
 
     AImageReader(int32_t width,
                  int32_t height,
                  int32_t format,
-                 uint64_t usage,
+                 uint64_t usage0,
+                 uint64_t usage1,
                  int32_t maxImages);
     ~AImageReader();
 
@@ -66,15 +65,15 @@
     media_status_t init();
 
     media_status_t setImageListener(AImageReader_ImageListener* listener);
+    media_status_t setBufferRemovedListener(AImageReader_BufferRemovedListener* listener);
 
-    media_status_t acquireNextImage(/*out*/AImage** image);
-    media_status_t acquireLatestImage(/*out*/AImage** image);
+    media_status_t acquireNextImage(/*out*/AImage** image, /*out*/int* fenceFd);
+    media_status_t acquireLatestImage(/*out*/AImage** image, /*out*/int* fenceFd);
 
     ANativeWindow* getWindow()    const { return mWindow.get(); };
     int32_t        getWidth()     const { return mWidth; };
     int32_t        getHeight()    const { return mHeight; };
     int32_t        getFormat()    const { return mFormat; };
-    uint64_t       getUsage()     const { return mUsage; };
     int32_t        getMaxImages() const { return mMaxImages; };
 
   private:
@@ -85,22 +84,25 @@
     void returnBufferItemLocked(BufferItem* buffer);
 
     // Called by AImageReader_acquireXXX to acquire a Buffer and setup AImage.
-    media_status_t acquireImageLocked(/*out*/AImage** image);
+    media_status_t acquireImageLocked(/*out*/AImage** image, /*out*/int* fenceFd);
 
     // Called by AImage to close image
-    void releaseImageLocked(AImage* image);
+    void releaseImageLocked(AImage* image, int releaseFenceFd);
 
     static int getBufferWidth(BufferItem* buffer);
     static int getBufferHeight(BufferItem* buffer);
 
     media_status_t setImageListenerLocked(AImageReader_ImageListener* listener);
+    media_status_t setBufferRemovedListenerLocked(AImageReader_BufferRemovedListener* listener);
 
     // definition of handler and message
     enum {
-        kWhatImageAvailable
+        kWhatBufferRemoved,
+        kWhatImageAvailable,
     };
     static const char* kCallbackFpKey;
     static const char* kContextKey;
+    static const char* kGraphicBufferKey;
     class CallbackHandler : public AHandler {
       public:
         CallbackHandler(AImageReader* reader) : mReader(reader) {}
@@ -115,7 +117,8 @@
     const int32_t mWidth;
     const int32_t mHeight;
     const int32_t mFormat;
-    const uint64_t mUsage;
+    const uint64_t mUsage0;  // AHARDWAREBUFFER_USAGE0* flags.
+    const uint64_t mUsage1;  // AHARDWAREBUFFER_USAGE1* flags.
     const int32_t mMaxImages;
 
     // TODO(jwcai) Seems completely unused in AImageReader class.
@@ -136,8 +139,24 @@
     };
     sp<FrameListener> mFrameListener;
 
+    struct BufferRemovedListener : public BufferItemConsumer::BufferFreedListener {
+      public:
+        explicit BufferRemovedListener(AImageReader* parent) : mReader(parent) {}
+
+        void onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) override;
+
+        media_status_t setBufferRemovedListener(AImageReader_BufferRemovedListener* listener);
+
+       private:
+        AImageReader_BufferRemovedListener mListener = {nullptr, nullptr};
+        wp<AImageReader>           mReader;
+        Mutex                      mLock;
+    };
+    sp<BufferRemovedListener> mBufferRemovedListener;
+
     int mHalFormat;
     android_dataspace mHalDataSpace;
+    uint64_t mHalUsage;
 
     sp<IGraphicBufferProducer> mProducer;
     sp<Surface>                mSurface;
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 7db4d06..74c23d3 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -1,7 +1,9 @@
 LIBMEDIANDK {
   global:
     AImageReader_acquireLatestImage; # introduced=24
+    AImageReader_acquireLatestImageAsync; # introduced=26
     AImageReader_acquireNextImage; # introduced=24
+    AImageReader_acquireNextImageAsync; # introduced=26
     AImageReader_delete; # introduced=24
     AImageReader_getFormat; # introduced=24
     AImageReader_getHeight; # introduced=24
@@ -9,10 +11,14 @@
     AImageReader_getWidth; # introduced=24
     AImageReader_getWindow; # introduced=24
     AImageReader_new; # introduced=24
+    AImageReader_newWithUsage; # introduced=26
+    AImageReader_setBufferRemovedListener; # introduced=26
     AImageReader_setImageListener; # introduced=24
     AImage_delete; # introduced=24
+    AImage_deleteAsync; # introduced=26
     AImage_getCropRect; # introduced=24
     AImage_getFormat; # introduced=24
+    AImage_getHardwareBuffer; # introduced=26
     AImage_getHeight; # introduced=24
     AImage_getNumberOfPlanes; # introduced=24
     AImage_getPlaneData; # introduced=24
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index f8b2908..562e6c9 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -22,6 +22,7 @@
 
 #include <chrono>
 #include <inttypes.h>
+#include <set>
 #include <hidl/ServiceManagement.h>
 
 namespace android {
@@ -75,7 +76,7 @@
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
     int count = 0;
     for (auto& provider : mProviders) {
-        count += provider->mDevices.size();
+        count += provider->mUniqueDeviceCount;
     }
     return count;
 }
@@ -85,7 +86,7 @@
     int count = 0;
     for (auto& provider : mProviders) {
         if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
-            count += provider->mDevices.size();
+            count += provider->mUniqueDeviceCount;
         }
     }
     return count;
@@ -471,6 +472,12 @@
         }
     }
 
+    std::set<std::string> uniqueCameraIds;
+    for (auto& device : mDevices) {
+        uniqueCameraIds.insert(device->mId);
+    }
+    mUniqueDeviceCount = uniqueCameraIds.size();
+
     ALOGI("Camera provider %s ready with %zu camera devices",
             mProviderName.c_str(), mDevices.size());
 
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index b1da831..04b9597 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -292,6 +292,7 @@
             static status_t setTorchMode(InterfaceT& interface, bool enabled);
         };
         std::vector<std::unique_ptr<DeviceInfo>> mDevices;
+        int mUniqueDeviceCount;
 
         // HALv1-specific camera fields, including the actual device interface
         struct DeviceInfo1 : public DeviceInfo {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 71e52af..eccaddb 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -933,8 +933,18 @@
     return res;
 }
 
+
 hardware::Return<void> Camera3Device::processCaptureResult(
-        const device::V3_2::CaptureResult& result) {
+        const hardware::hidl_vec<
+                hardware::camera::device::V3_2::CaptureResult>& results) {
+    for (const auto& result : results) {
+        processOneCaptureResult(result);
+    }
+    return hardware::Void();
+}
+
+void Camera3Device::processOneCaptureResult(
+        const hardware::camera::device::V3_2::CaptureResult& result) {
     camera3_capture_result r;
     status_t res;
     r.frame_number = result.frameNumber;
@@ -944,7 +954,7 @@
         if ((res = validate_camera_metadata_structure(r.result, &expected_metadata_size)) != OK) {
             ALOGE("%s: Frame %d: Invalid camera metadata received by camera service from HAL: %s (%d)",
                     __FUNCTION__, result.frameNumber, strerror(-res), res);
-            return hardware::Void();
+            return;
         }
     } else {
         r.result = nullptr;
@@ -960,7 +970,7 @@
         if (idx == -1) {
             ALOGE("%s: Frame %d: Buffer %zu: Invalid output stream id %d",
                     __FUNCTION__, result.frameNumber, i, bSrc.streamId);
-            return hardware::Void();
+            return;
         }
         bDst.stream = mOutputStreams.valueAt(idx)->asHalStream();
 
@@ -969,7 +979,7 @@
         if (res != OK) {
             ALOGE("%s: Frame %d: Buffer %zu: No in-flight buffer for stream %d",
                     __FUNCTION__, result.frameNumber, i, bSrc.streamId);
-            return hardware::Void();
+            return;
         }
         bDst.buffer = buffer;
         bDst.status = mapHidlBufferStatus(bSrc.status);
@@ -981,7 +991,7 @@
         } else {
             ALOGE("%s: Frame %d: Invalid release fence for buffer %zu, fd count is %d, not 1",
                     __FUNCTION__, result.frameNumber, i, bSrc.releaseFence->numFds);
-            return hardware::Void();
+            return;
         }
     }
     r.num_output_buffers = outputBuffers.size();
@@ -994,7 +1004,7 @@
         if (mInputStream->getId() != result.inputBuffer.streamId) {
             ALOGE("%s: Frame %d: Invalid input stream id %d", __FUNCTION__,
                     result.frameNumber, result.inputBuffer.streamId);
-            return hardware::Void();
+            return;
         }
         inputBuffer.stream = mInputStream->asHalStream();
         buffer_handle_t *buffer;
@@ -1003,7 +1013,7 @@
         if (res != OK) {
             ALOGE("%s: Frame %d: Input buffer: No in-flight buffer for stream %d",
                     __FUNCTION__, result.frameNumber, result.inputBuffer.streamId);
-            return hardware::Void();
+            return;
         }
         inputBuffer.buffer = buffer;
         inputBuffer.status = mapHidlBufferStatus(result.inputBuffer.status);
@@ -1015,7 +1025,7 @@
         } else {
             ALOGE("%s: Frame %d: Invalid release fence for input buffer, fd count is %d, not 1",
                     __FUNCTION__, result.frameNumber, result.inputBuffer.releaseFence->numFds);
-            return hardware::Void();
+            return;
         }
         r.input_buffer = &inputBuffer;
     }
@@ -1023,12 +1033,19 @@
     r.partial_result = result.partialResult;
 
     processCaptureResult(&r);
-
-    return hardware::Void();
 }
 
 hardware::Return<void> Camera3Device::notify(
-        const NotifyMsg& msg) {
+        const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
+    for (const auto& msg : msgs) {
+        notify(msg);
+    }
+    return hardware::Void();
+}
+
+void Camera3Device::notify(
+        const hardware::camera::device::V3_2::NotifyMsg& msg) {
+
     camera3_notify_msg m;
     switch (msg.type) {
         case MsgType::ERROR:
@@ -1039,7 +1056,7 @@
                 if (idx == -1) {
                     ALOGE("%s: Frame %d: Invalid error stream id %d",
                             __FUNCTION__, m.message.error.frame_number, msg.msg.error.errorStreamId);
-                    return hardware::Void();
+                    return;
                 }
                 m.message.error.error_stream = mOutputStreams.valueAt(idx)->asHalStream();
             } else {
@@ -1067,8 +1084,6 @@
             break;
     }
     notify(&m);
-
-    return hardware::Void();
 }
 
 status_t Camera3Device::captureList(const List<const CameraMetadata> &requests,
@@ -3036,7 +3051,8 @@
         mHal3Device(nullptr) {}
 
 Camera3Device::HalInterface::HalInterface(const HalInterface& other) :
-        mHal3Device(other.mHal3Device), mHidlSession(other.mHidlSession) {}
+        mHal3Device(other.mHal3Device),
+        mHidlSession(other.mHidlSession) {}
 
 bool Camera3Device::HalInterface::valid() {
     return (mHal3Device != nullptr) || (mHidlSession != nullptr);
@@ -3047,6 +3063,10 @@
     mHidlSession.clear();
 }
 
+bool Camera3Device::HalInterface::supportBatchRequest() {
+    return mHidlSession != nullptr;
+}
+
 status_t Camera3Device::HalInterface::constructDefaultRequestSettings(
         camera3_request_template_t templateId,
         /*out*/ camera_metadata_t **requestTemplate) {
@@ -3251,6 +3271,114 @@
     return res;
 }
 
+void Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
+        /*out*/device::V3_2::CaptureRequest* captureRequest,
+        /*out*/std::vector<native_handle_t*>* handlesCreated) {
+
+    if (captureRequest == nullptr || handlesCreated == nullptr) {
+        ALOGE("%s: captureRequest (%p) and handlesCreated (%p) must not be null",
+                __FUNCTION__, captureRequest, handlesCreated);
+        return;
+    }
+
+    captureRequest->frameNumber = request->frame_number;
+    // A null request settings maps to a size-0 CameraMetadata
+    if (request->settings != nullptr) {
+        captureRequest->settings.setToExternal(
+                reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(request->settings)),
+                get_camera_metadata_size(request->settings));
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mInflightLock);
+        if (request->input_buffer != nullptr) {
+            int32_t streamId = Camera3Stream::cast(request->input_buffer->stream)->getId();
+            buffer_handle_t buf = *(request->input_buffer->buffer);
+            auto pair = getBufferId(buf, streamId);
+            bool isNewBuffer = pair.first;
+            uint64_t bufferId = pair.second;
+            captureRequest->inputBuffer.streamId = streamId;
+            captureRequest->inputBuffer.bufferId = bufferId;
+            captureRequest->inputBuffer.buffer = (isNewBuffer) ? buf : nullptr;
+            captureRequest->inputBuffer.status = BufferStatus::OK;
+            native_handle_t *acquireFence = nullptr;
+            if (request->input_buffer->acquire_fence != -1) {
+                acquireFence = native_handle_create(1,0);
+                acquireFence->data[0] = request->input_buffer->acquire_fence;
+                handlesCreated->push_back(acquireFence);
+            }
+            captureRequest->inputBuffer.acquireFence = acquireFence;
+            captureRequest->inputBuffer.releaseFence = nullptr;
+
+            pushInflightBufferLocked(captureRequest->frameNumber, streamId,
+                    request->input_buffer->buffer,
+                    request->input_buffer->acquire_fence);
+        } else {
+            captureRequest->inputBuffer.streamId = -1;
+            captureRequest->inputBuffer.bufferId = BUFFER_ID_NO_BUFFER;
+        }
+
+        captureRequest->outputBuffers.resize(request->num_output_buffers);
+        for (size_t i = 0; i < request->num_output_buffers; i++) {
+            const camera3_stream_buffer_t *src = request->output_buffers + i;
+            StreamBuffer &dst = captureRequest->outputBuffers[i];
+            int32_t streamId = Camera3Stream::cast(src->stream)->getId();
+            buffer_handle_t buf = *(src->buffer);
+            auto pair = getBufferId(buf, streamId);
+            bool isNewBuffer = pair.first;
+            dst.streamId = streamId;
+            dst.bufferId = pair.second;
+            dst.buffer = isNewBuffer ? buf : nullptr;
+            dst.status = BufferStatus::OK;
+            native_handle_t *acquireFence = nullptr;
+            if (src->acquire_fence != -1) {
+                acquireFence = native_handle_create(1,0);
+                acquireFence->data[0] = src->acquire_fence;
+                handlesCreated->push_back(acquireFence);
+            }
+            dst.acquireFence = acquireFence;
+            dst.releaseFence = nullptr;
+
+            pushInflightBufferLocked(captureRequest->frameNumber, streamId,
+                    src->buffer, src->acquire_fence);
+        }
+    }
+}
+
+status_t Camera3Device::HalInterface::processBatchCaptureRequests(
+        std::vector<camera3_capture_request_t*>& requests,/*out*/uint32_t* numRequestProcessed) {
+    ATRACE_NAME("CameraHal::processBatchCaptureRequests");
+    if (!valid()) return INVALID_OPERATION;
+
+    hardware::hidl_vec<device::V3_2::CaptureRequest> captureRequests;
+    size_t batchSize = requests.size();
+    captureRequests.resize(batchSize);
+    std::vector<native_handle_t*> handlesCreated;
+
+    for (size_t i = 0; i < batchSize; i++) {
+        wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i], /*out*/&handlesCreated);
+    }
+
+    common::V1_0::Status status = common::V1_0::Status::INTERNAL_ERROR;
+    *numRequestProcessed = 0;
+    mHidlSession->processCaptureRequest(captureRequests,
+            [&status, &numRequestProcessed] (auto s, uint32_t n) {
+                status = s;
+                *numRequestProcessed = n;
+            });
+
+    if (status == common::V1_0::Status::OK && *numRequestProcessed != batchSize) {
+        ALOGE("%s: processCaptureRequest returns OK but processed %d/%zu requests",
+                __FUNCTION__, *numRequestProcessed, batchSize);
+        status = common::V1_0::Status::INTERNAL_ERROR;
+    }
+
+    for (auto& handle : handlesCreated) {
+        native_handle_delete(handle);
+    }
+    return CameraProviderManager::mapToStatusT(status);
+}
+
 status_t Camera3Device::HalInterface::processCaptureRequest(
         camera3_capture_request_t *request) {
     ATRACE_NAME("CameraHal::processCaptureRequest");
@@ -3260,76 +3388,10 @@
     if (mHal3Device != nullptr) {
         res = mHal3Device->ops->process_capture_request(mHal3Device, request);
     } else {
-        device::V3_2::CaptureRequest captureRequest;
-        captureRequest.frameNumber = request->frame_number;
-        std::vector<native_handle_t*> handlesCreated;
-        // A null request settings maps to a size-0 CameraMetadata
-        if (request->settings != nullptr) {
-            captureRequest.settings.setToExternal(
-                    reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(request->settings)),
-                    get_camera_metadata_size(request->settings));
-        }
-
-        {
-            std::lock_guard<std::mutex> lock(mInflightLock);
-            if (request->input_buffer != nullptr) {
-                int32_t streamId = Camera3Stream::cast(request->input_buffer->stream)->getId();
-                buffer_handle_t buf = *(request->input_buffer->buffer);
-                auto pair = getBufferId(buf, streamId);
-                bool isNewBuffer = pair.first;
-                uint64_t bufferId = pair.second;
-                captureRequest.inputBuffer.streamId = streamId;
-                captureRequest.inputBuffer.bufferId = bufferId;
-                captureRequest.inputBuffer.buffer = (isNewBuffer) ? buf : nullptr;
-                captureRequest.inputBuffer.status = BufferStatus::OK;
-                native_handle_t *acquireFence = nullptr;
-                if (request->input_buffer->acquire_fence != -1) {
-                    acquireFence = native_handle_create(1,0);
-                    acquireFence->data[0] = request->input_buffer->acquire_fence;
-                    handlesCreated.push_back(acquireFence);
-                }
-                captureRequest.inputBuffer.acquireFence = acquireFence;
-                captureRequest.inputBuffer.releaseFence = nullptr;
-
-                pushInflightBufferLocked(captureRequest.frameNumber, streamId,
-                        request->input_buffer->buffer,
-                        request->input_buffer->acquire_fence);
-            } else {
-                captureRequest.inputBuffer.streamId = -1;
-                captureRequest.inputBuffer.bufferId = BUFFER_ID_NO_BUFFER;
-            }
-
-            captureRequest.outputBuffers.resize(request->num_output_buffers);
-            for (size_t i = 0; i < request->num_output_buffers; i++) {
-                const camera3_stream_buffer_t *src = request->output_buffers + i;
-                StreamBuffer &dst = captureRequest.outputBuffers[i];
-                int32_t streamId = Camera3Stream::cast(src->stream)->getId();
-                buffer_handle_t buf = *(src->buffer);
-                auto pair = getBufferId(buf, streamId);
-                bool isNewBuffer = pair.first;
-                dst.streamId = streamId;
-                dst.bufferId = pair.second;
-                dst.buffer = isNewBuffer ? buf : nullptr;
-                dst.status = BufferStatus::OK;
-                native_handle_t *acquireFence = nullptr;
-                if (src->acquire_fence != -1) {
-                    acquireFence = native_handle_create(1,0);
-                    acquireFence->data[0] = src->acquire_fence;
-                    handlesCreated.push_back(acquireFence);
-                }
-                dst.acquireFence = acquireFence;
-                dst.releaseFence = nullptr;
-
-                pushInflightBufferLocked(captureRequest.frameNumber, streamId,
-                        src->buffer, src->acquire_fence);
-            }
-        }
-        common::V1_0::Status status = mHidlSession->processCaptureRequest(captureRequest);
-
-        for (auto& handle : handlesCreated) {
-            native_handle_delete(handle);
-        }
-        res = CameraProviderManager::mapToStatusT(status);
+        uint32_t numRequestProcessed = 0;
+        std::vector<camera3_capture_request_t*> requests(1);
+        requests[0] = request;
+        res = processBatchCaptureRequests(requests, &numRequestProcessed);
     }
     return res;
 }
@@ -3757,6 +3819,128 @@
     }
 }
 
+bool Camera3Device::RequestThread::sendRequestsBatch() {
+    status_t res;
+    size_t batchSize = mNextRequests.size();
+    std::vector<camera3_capture_request_t*> requests(batchSize);
+    uint32_t numRequestProcessed = 0;
+    for (size_t i = 0; i < batchSize; i++) {
+        requests[i] = &mNextRequests.editItemAt(i).halRequest;
+    }
+
+    ATRACE_ASYNC_BEGIN("batch frame capture", mNextRequests[0].halRequest.frame_number);
+    res = mInterface->processBatchCaptureRequests(requests, &numRequestProcessed);
+
+    bool triggerRemoveFailed = false;
+    NextRequest& triggerFailedRequest = mNextRequests.editItemAt(0);
+    for (size_t i = 0; i < numRequestProcessed; i++) {
+        NextRequest& nextRequest = mNextRequests.editItemAt(i);
+        nextRequest.submitted = true;
+
+
+        // Update the latest request sent to HAL
+        if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
+            Mutex::Autolock al(mLatestRequestMutex);
+
+            camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
+            mLatestRequest.acquire(cloned);
+
+            sp<Camera3Device> parent = mParent.promote();
+            if (parent != NULL) {
+                parent->monitorMetadata(TagMonitor::REQUEST,
+                        nextRequest.halRequest.frame_number,
+                        0, mLatestRequest);
+            }
+        }
+
+        if (nextRequest.halRequest.settings != NULL) {
+            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
+        }
+
+        if (!triggerRemoveFailed) {
+            // Remove any previously queued triggers (after unlock)
+            status_t removeTriggerRes = removeTriggers(mPrevRequest);
+            if (removeTriggerRes != OK) {
+                triggerRemoveFailed = true;
+                triggerFailedRequest = nextRequest;
+            }
+        }
+    }
+
+    if (triggerRemoveFailed) {
+        SET_ERR("RequestThread: Unable to remove triggers "
+              "(capture request %d, HAL device: %s (%d)",
+              triggerFailedRequest.halRequest.frame_number, strerror(-res), res);
+        cleanUpFailedRequests(/*sendRequestError*/ false);
+        return false;
+    }
+
+    if (res != OK) {
+        // Should only get a failure here for malformed requests or device-level
+        // errors, so consider all errors fatal.  Bad metadata failures should
+        // come through notify.
+        SET_ERR("RequestThread: Unable to submit capture request %d to HAL device: %s (%d)",
+                mNextRequests[numRequestProcessed].halRequest.frame_number,
+                strerror(-res), res);
+        cleanUpFailedRequests(/*sendRequestError*/ false);
+        return false;
+    }
+    return true;
+}
+
+bool Camera3Device::RequestThread::sendRequestsOneByOne() {
+    status_t res;
+
+    for (auto& nextRequest : mNextRequests) {
+        // Submit request and block until ready for next one
+        ATRACE_ASYNC_BEGIN("frame capture", nextRequest.halRequest.frame_number);
+        res = mInterface->processCaptureRequest(&nextRequest.halRequest);
+
+        if (res != OK) {
+            // Should only get a failure here for malformed requests or device-level
+            // errors, so consider all errors fatal.  Bad metadata failures should
+            // come through notify.
+            SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
+                    " device: %s (%d)", nextRequest.halRequest.frame_number, strerror(-res),
+                    res);
+            cleanUpFailedRequests(/*sendRequestError*/ false);
+            return false;
+        }
+
+        // Mark that the request has be submitted successfully.
+        nextRequest.submitted = true;
+
+        // Update the latest request sent to HAL
+        if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
+            Mutex::Autolock al(mLatestRequestMutex);
+
+            camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
+            mLatestRequest.acquire(cloned);
+
+            sp<Camera3Device> parent = mParent.promote();
+            if (parent != NULL) {
+                parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number,
+                        0, mLatestRequest);
+            }
+        }
+
+        if (nextRequest.halRequest.settings != NULL) {
+            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
+        }
+
+        // Remove any previously queued triggers (after unlock)
+        res = removeTriggers(mPrevRequest);
+        if (res != OK) {
+            SET_ERR("RequestThread: Unable to remove triggers "
+                  "(capture request %d, HAL device: %s (%d)",
+                  nextRequest.halRequest.frame_number, strerror(-res), res);
+            cleanUpFailedRequests(/*sendRequestError*/ false);
+            return false;
+        }
+    }
+    return true;
+}
+
 bool Camera3Device::RequestThread::threadLoop() {
     ATRACE_CALL();
     status_t res;
@@ -3819,58 +4003,12 @@
 
     ALOGVV("%s: %d: submitting %zu requests in a batch.", __FUNCTION__, __LINE__,
             mNextRequests.size());
-    for (auto& nextRequest : mNextRequests) {
-        // Submit request and block until ready for next one
-        ATRACE_ASYNC_BEGIN("frame capture", nextRequest.halRequest.frame_number);
-        res = mInterface->processCaptureRequest(&nextRequest.halRequest);
 
-        if (res != OK) {
-            // Should only get a failure here for malformed requests or device-level
-            // errors, so consider all errors fatal.  Bad metadata failures should
-            // come through notify.
-            SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
-                    " device: %s (%d)", nextRequest.halRequest.frame_number, strerror(-res),
-                    res);
-            cleanUpFailedRequests(/*sendRequestError*/ false);
-            if (useFlushLock) {
-                mFlushLock.unlock();
-            }
-            return false;
-        }
-
-        // Mark that the request has be submitted successfully.
-        nextRequest.submitted = true;
-
-        // Update the latest request sent to HAL
-        if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
-            Mutex::Autolock al(mLatestRequestMutex);
-
-            camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
-            mLatestRequest.acquire(cloned);
-
-            sp<Camera3Device> parent = mParent.promote();
-            if (parent != NULL) {
-                parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number,
-                        0, mLatestRequest);
-            }
-        }
-
-        if (nextRequest.halRequest.settings != NULL) {
-            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
-        }
-
-        // Remove any previously queued triggers (after unlock)
-        res = removeTriggers(mPrevRequest);
-        if (res != OK) {
-            SET_ERR("RequestThread: Unable to remove triggers "
-                  "(capture request %d, HAL device: %s (%d)",
-                  nextRequest.halRequest.frame_number, strerror(-res), res);
-            cleanUpFailedRequests(/*sendRequestError*/ false);
-            if (useFlushLock) {
-                mFlushLock.unlock();
-            }
-            return false;
-        }
+    bool submitRequestSuccess = false;
+    if (mInterface->supportBatchRequest()) {
+        submitRequestSuccess = sendRequestsBatch();
+    } else {
+        submitRequestSuccess = sendRequestsOneByOne();
     }
 
     if (useFlushLock) {
@@ -3883,7 +4021,7 @@
         mNextRequests.clear();
     }
 
-    return true;
+    return submitRequestSuccess;
 }
 
 status_t Camera3Device::RequestThread::prepareHalRequests() {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index c10b1b4..d873b27 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -241,6 +241,9 @@
         // Reset this HalInterface object (does not call close())
         void clear();
 
+        // Check if HalInterface support sending requests in batch
+        bool supportBatchRequest();
+
         // Calls into the HAL interface
 
         // Caller takes ownership of requestTemplate
@@ -248,6 +251,9 @@
                 /*out*/ camera_metadata_t **requestTemplate);
         status_t configureStreams(/*inout*/ camera3_stream_configuration *config);
         status_t processCaptureRequest(camera3_capture_request_t *request);
+        status_t processBatchCaptureRequests(
+                std::vector<camera3_capture_request_t*>& requests,
+                /*out*/uint32_t* numRequestProcessed);
         status_t flush();
         status_t dump(int fd);
         status_t close();
@@ -262,6 +268,12 @@
 
         std::mutex mInflightLock;
 
+        // The output HIDL request still depends on input camera3_capture_request_t
+        // Do not free input camera3_capture_request_t before output HIDL request
+        void wrapAsHidlRequest(camera3_capture_request_t* in,
+                /*out*/hardware::camera::device::V3_2::CaptureRequest* out,
+                /*out*/std::vector<native_handle_t*>* handlesCreated);
+
         status_t pushInflightBufferLocked(int32_t frameNumber, int32_t streamId,
                 buffer_handle_t *buffer, int acquireFence);
         // Cache of buffer handles keyed off (frameNumber << 32 | streamId)
@@ -438,9 +450,17 @@
      */
 
     hardware::Return<void> processCaptureResult(
-            const hardware::camera::device::V3_2::CaptureResult& result) override;
+            const hardware::hidl_vec<
+                    hardware::camera::device::V3_2::CaptureResult>& results) override;
     hardware::Return<void> notify(
-            const hardware::camera::device::V3_2::NotifyMsg& msg) override;
+            const hardware::hidl_vec<
+                    hardware::camera::device::V3_2::NotifyMsg>& msgs) override;
+
+    // Handle one capture result
+    void processOneCaptureResult(
+            const hardware::camera::device::V3_2::CaptureResult& results);
+    // Handle one notify message
+    void notify(const hardware::camera::device::V3_2::NotifyMsg& msg);
 
     /**
      * Common initialization code shared by both HAL paths
@@ -759,6 +779,12 @@
         // Clear repeating requests. Must be called with mRequestLock held.
         status_t clearRepeatingRequestsLocked(/*out*/ int64_t *lastFrameNumber = NULL);
 
+        // send request in mNextRequests to HAL one by one. Return true = sucssess
+        bool sendRequestsOneByOne();
+
+        // send request in mNextRequests to HAL in a batch. Return true = sucssess
+        bool sendRequestsBatch();
+
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
         HalInterface*      mInterface;
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 19016cd..2ce6cf8 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -51,3 +51,7 @@
 getdents64: 1
 pipe2: 1
 ppoll: 1
+
+# Required by AddressSanitizer
+gettid: 1
+sched_yield: 1