Merge "AMediaDataSourceWrapper: keep DataSource sp alive"
diff --git a/camera/ndk/Android.mk b/camera/ndk/Android.mk
index a5aab07..508f930 100644
--- a/camera/ndk/Android.mk
+++ b/camera/ndk/Android.mk
@@ -41,6 +41,8 @@
 LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
+LOCAL_LDFLAGS += -Wl,--version-script=$(LOCAL_PATH)/libcamera2ndk.map.txt
+
 LOCAL_SHARED_LIBRARIES := \
     libbinder \
     liblog \
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
index eaa3390..cb69f91 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
@@ -89,7 +89,7 @@
     // asset_id change. If it sends an EcmContainer with 2 Ecms with different
     // asset_ids (old and new) then it might be best to prefetch the Emm.
     if ((asset_.id() != 0) && (*asset_id != asset_.id())) {
-        ALOGW("Asset_id change from %llu to %" PRIu64, asset_.id(), *asset_id);
+        ALOGW("Asset_id change from %" PRIu64 " to %" PRIu64, asset_.id(), *asset_id);
         asset_.Clear();
     }
 
diff --git a/drm/mediacas/plugins/clearkey/ecm.cpp b/drm/mediacas/plugins/clearkey/ecm.cpp
index 9fde13a..b3b5218 100644
--- a/drm/mediacas/plugins/clearkey/ecm.cpp
+++ b/drm/mediacas/plugins/clearkey/ecm.cpp
@@ -17,6 +17,8 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "ecm"
 
+#include <inttypes.h>
+
 #include "ecm.h"
 #include "ecm_generator.h"
 #include "protos/license_protos.pb.h"
@@ -76,7 +78,7 @@
         return status;
     }
     if (asset.id() != asset_from_emm.id()) {
-        ALOGE("Asset_id from Emm (%llu) does not match asset_id from Ecm (%llu).",
+        ALOGE("Asset_id from Emm (%" PRIu64 ") does not match asset_id from Ecm (%" PRIu64 ").",
                 asset_from_emm.id(), asset.id());
         return CLEARKEY_STATUS_INVALID_PARAMETER;
     }
diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
index 3eaea7c..f264501 100644
--- a/media/bufferpool/2.0/Accessor.cpp
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -117,19 +117,18 @@
 Return<void> Accessor::connect(
         const sp<::android::hardware::media::bufferpool::V2_0::IObserver>& observer,
         connect_cb _hidl_cb) {
-    (void)observer;
     sp<Connection> connection;
     ConnectionId connectionId;
+    uint32_t msgId;
     const StatusDescriptor* fmqDesc;
+    const InvalidationDescriptor* invDesc;
 
-    ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
+    ResultStatus status = connect(
+            observer, false, &connection, &connectionId, &msgId, &fmqDesc, &invDesc);
     if (status == ResultStatus::OK) {
-        _hidl_cb(status, connection, connectionId, *fmqDesc,
-                 android::hardware::MQDescriptorUnsync<BufferInvalidationMessage>(
-                         std::vector<android::hardware::GrantorDescriptor>(),
-                         nullptr /* nhandle */, 0 /* size */));
+        _hidl_cb(status, connection, connectionId, msgId, *fmqDesc, *invDesc);
     } else {
-        _hidl_cb(status, nullptr, -1LL,
+        _hidl_cb(status, nullptr, -1LL, 0,
                  android::hardware::MQDescriptorSync<BufferStatusMessage>(
                          std::vector<android::hardware::GrantorDescriptor>(),
                          nullptr /* nhandle */, 0 /* size */),
@@ -147,7 +146,15 @@
 }
 
 bool Accessor::isValid() {
-    return (bool)mImpl;
+    return (bool)mImpl && mImpl->isValid();
+}
+
+ResultStatus Accessor::flush() {
+    if (mImpl) {
+        mImpl->flush();
+        return ResultStatus::OK;
+    }
+    return ResultStatus::CRITICAL_ERROR;
 }
 
 ResultStatus Accessor::allocate(
@@ -170,10 +177,15 @@
 }
 
 ResultStatus Accessor::connect(
+        const sp<IObserver> &observer, bool local,
         sp<Connection> *connection, ConnectionId *pConnectionId,
-        const StatusDescriptor** fmqDescPtr, bool local) {
+        uint32_t *pMsgId,
+        const StatusDescriptor** statusDescPtr,
+        const InvalidationDescriptor** invDescPtr) {
     if (mImpl) {
-        ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+        ResultStatus status = mImpl->connect(
+                this, observer, connection, pConnectionId, pMsgId,
+                statusDescPtr, invDescPtr);
         if (!local && status == ResultStatus::OK) {
             sp<Accessor> accessor(this);
             sConnectionDeathRecipient->add(*pConnectionId, accessor);
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
index a718da1..4b5b17a 100644
--- a/media/bufferpool/2.0/Accessor.h
+++ b/media/bufferpool/2.0/Accessor.h
@@ -95,6 +95,9 @@
     /** Returns whether the accessor is valid. */
     bool isValid();
 
+    /** Invalidates all buffers which are owned by bufferpool */
+    ResultStatus flush();
+
     /** Allocates a buffer from a buffer pool.
      *
      * @param connectionId  the connection id of the client.
@@ -135,20 +138,28 @@
      * created connection in order to communicate with the buffer pool. An
      * FMQ for buffer status message is also created for the client.
      *
-     * @param connection    created connection
-     * @param pConnectionId the id of the created connection
-     * @param fmqDescPtr    FMQ descriptor for shared buffer status message
-     *                      queue between a buffer pool and the client.
+     * @param observer      client observer for buffer invalidation
      * @param local         true when a connection request comes from local process,
      *                      false otherwise.
+     * @param connection    created connection
+     * @param pConnectionId the id of the created connection
+     * @param pMsgId        the id of the recent buffer pool message
+     * @param statusDescPtr FMQ descriptor for shared buffer status message
+     *                      queue between a buffer pool and the client.
+     * @param invDescPtr    FMQ descriptor for buffer invalidation message
+     *                      queue from a buffer pool to the client.
      *
      * @return OK when a connection is successfully made.
      *         NO_MEMORY when there is no memory.
      *         CRITICAL_ERROR otherwise.
      */
     ResultStatus connect(
+            const sp<IObserver>& observer,
+            bool local,
             sp<Connection> *connection, ConnectionId *pConnectionId,
-            const StatusDescriptor** fmqDescPtr, bool local);
+            uint32_t *pMsgId,
+            const StatusDescriptor** statusDescPtr,
+            const InvalidationDescriptor** invDescPtr);
 
     /**
      * Closes the specified connection to the client.
@@ -176,7 +187,7 @@
 
 private:
     class Impl;
-    std::unique_ptr<Impl> mImpl;
+    std::shared_ptr<Impl> mImpl;
 };
 
 }  // namespace implementation
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 0ba6600..4cc8abc 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -21,6 +21,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <utils/Log.h>
+#include <thread>
 #include "AccessorImpl.h"
 #include "Connection.h"
 
@@ -47,6 +48,7 @@
     const std::shared_ptr<BufferPoolAllocation> mAllocation;
     const size_t mAllocSize;
     const std::vector<uint8_t> mConfig;
+    bool mInvalidated;
 
     InternalBuffer(
             BufferId id,
@@ -54,11 +56,16 @@
             const size_t allocSize,
             const std::vector<uint8_t> &allocConfig)
             : mId(id), mOwnerCount(0), mTransactionCount(0),
-            mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
+            mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig),
+            mInvalidated(false) {}
 
     const native_handle_t *handle() {
         return mAllocation->handle();
     }
+
+    void invalidate() {
+        mInvalidated = true;
+    }
 };
 
 struct TransactionStatus {
@@ -138,21 +145,29 @@
 }
 
 ResultStatus Accessor::Impl::connect(
-        const sp<Accessor> &accessor, sp<Connection> *connection,
-        ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr) {
+        const sp<Accessor> &accessor, const sp<IObserver> &observer,
+        sp<Connection> *connection,
+        ConnectionId *pConnectionId,
+        uint32_t *pMsgId,
+        const StatusDescriptor** statusDescPtr,
+        const InvalidationDescriptor** invDescPtr) {
     sp<Connection> newConnection = new Connection();
     ResultStatus status = ResultStatus::CRITICAL_ERROR;
     {
         std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
         if (newConnection) {
             ConnectionId id = (int64_t)sPid << 32 | sSeqId;
-            status = mBufferPool.mObserver.open(id, fmqDescPtr);
+            status = mBufferPool.mObserver.open(id, statusDescPtr);
             if (status == ResultStatus::OK) {
                 newConnection->initialize(accessor, id);
                 *connection = newConnection;
                 *pConnectionId = id;
+                *pMsgId = mBufferPool.mInvalidation.mInvalidationId;
+                mBufferPool.mInvalidationChannel.getDesc(invDescPtr);
+                mBufferPool.mInvalidation.onConnect(id, observer);
                 ++sSeqId;
             }
+
         }
         mBufferPool.processStatusMessages();
         mBufferPool.cleanUp();
@@ -165,6 +180,7 @@
     mBufferPool.processStatusMessages();
     mBufferPool.handleClose(connectionId);
     mBufferPool.mObserver.close(connectionId);
+    mBufferPool.mInvalidation.onClose(connectionId);
     // Since close# will be called after all works are finished, it is OK to
     // evict unused buffers.
     mBufferPool.cleanUp(true);
@@ -229,11 +245,30 @@
     mBufferPool.cleanUp(clearCache);
 }
 
+void Accessor::Impl::flush() {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.flush(shared_from_this());
+}
+
+void Accessor::Impl::handleInvalidateAck() {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.mInvalidation.onHandleAck();
+}
+
+bool Accessor::Impl::isValid() {
+    return mBufferPool.isValid();
+}
+
 Accessor::Impl::Impl::BufferPool::BufferPool()
     : mTimestampUs(getTimestampNow()),
       mLastCleanUpUs(mTimestampUs),
       mLastLogUs(mTimestampUs),
-      mSeq(0) {}
+      mSeq(0),
+      mStartSeq(0) {
+    mValid = mInvalidationChannel.isValid();
+}
 
 
 // Statistics helper
@@ -242,6 +277,8 @@
     return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
 }
 
+std::atomic<std::uint32_t> Accessor::Impl::BufferPool::Invalidation::sSeqId(0);
+
 Accessor::Impl::Impl::BufferPool::~BufferPool() {
     std::lock_guard<std::mutex> lock(mMutex);
     ALOGD("Destruction - bufferpool %p "
@@ -255,6 +292,96 @@
           percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
 }
 
+void Accessor::Impl::BufferPool::Invalidation::onConnect(
+        ConnectionId conId, const sp<IObserver>& observer) {
+    mAcks[conId] = mInvalidationId; // starts from current invalidationId
+    mObservers.insert(std::make_pair(conId, observer));
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onClose(ConnectionId conId) {
+    mAcks.erase(conId);
+    mObservers.erase(conId);
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onAck(
+        ConnectionId conId,
+        uint32_t msgId) {
+    auto it = mAcks.find(conId);
+    if (it == mAcks.end() || isMessageLater(msgId, it->second)) {
+        mAcks[conId] = msgId;
+    }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onBufferInvalidated(
+        BufferId bufferId,
+        BufferInvalidationChannel &channel) {
+    for (auto it = mPendings.begin(); it != mPendings.end();) {
+        if (it->invalidate(bufferId)) {
+            it = mPendings.erase(it);
+            uint32_t msgId = 0;
+            if (it->mNeedsAck) {
+                msgId = ++mInvalidationId;
+                if (msgId == 0) {
+                    // wrap happens
+                    msgId = ++mInvalidationId;
+                }
+            }
+            channel.postInvalidation(msgId, it->mFrom, it->mTo);
+            sInvalidator.addAccessor(mId, it->mImpl);
+            continue;
+        }
+        ++it;
+    }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onInvalidationRequest(
+        bool needsAck,
+        uint32_t from,
+        uint32_t to,
+        size_t left,
+        BufferInvalidationChannel &channel,
+        const std::shared_ptr<Accessor::Impl> &impl) {
+    if (left == 0) {
+        uint32_t msgId = 0;
+        if (needsAck) {
+            msgId = ++mInvalidationId;
+            if (msgId == 0) {
+                // wrap happens
+                msgId = ++mInvalidationId;
+            }
+        }
+        channel.postInvalidation(msgId, from, to);
+        sInvalidator.addAccessor(mId, impl);
+    } else {
+        // TODO: sending hint message?
+        Pending pending(needsAck, from, to, left, impl);
+        mPendings.push_back(pending);
+    }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onHandleAck() {
+    if (mInvalidationId != 0) {
+        std::set<int> deads;
+        for (auto it = mAcks.begin(); it != mAcks.end(); ++it) {
+            if (it->second != mInvalidationId) {
+                const sp<IObserver> observer = mObservers[it->first].promote();
+                if (observer) {
+                    observer->onMessage(it->first, mInvalidationId);
+                } else {
+                    deads.insert(it->first);
+                }
+            }
+        }
+        if (deads.size() > 0) {
+            for (auto it = deads.begin(); it != deads.end(); ++it) {
+                onClose(*it);
+            }
+        }
+    }
+    // All invalidation Ids are synced.
+    sInvalidator.delAccessor(mId);
+}
+
 bool Accessor::Impl::BufferPool::handleOwnBuffer(
         ConnectionId connectionId, BufferId bufferId) {
 
@@ -275,8 +402,15 @@
         iter->second->mOwnerCount--;
         if (iter->second->mOwnerCount == 0 &&
                 iter->second->mTransactionCount == 0) {
-            mStats.onBufferUnused(iter->second->mAllocSize);
-            mFreeBuffers.insert(bufferId);
+            if (!iter->second->mInvalidated) {
+                mStats.onBufferUnused(iter->second->mAllocSize);
+                mFreeBuffers.insert(bufferId);
+            } else {
+                mStats.onBufferUnused(iter->second->mAllocSize);
+                mStats.onBufferEvicted(iter->second->mAllocSize);
+                mBuffers.erase(iter);
+                mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+            }
         }
     }
     erase(&mUsingConnections, bufferId, connectionId);
@@ -352,8 +486,15 @@
             bufferIter->second->mTransactionCount--;
             if (bufferIter->second->mOwnerCount == 0
                 && bufferIter->second->mTransactionCount == 0) {
-                mStats.onBufferUnused(bufferIter->second->mAllocSize);
-                mFreeBuffers.insert(message.bufferId);
+                if (!bufferIter->second->mInvalidated) {
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mFreeBuffers.insert(message.bufferId);
+                } else {
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+                    mBuffers.erase(bufferIter);
+                    mInvalidation.onBufferInvalidated(message.bufferId, mInvalidationChannel);
+                }
             }
             mTransactions.erase(found);
         }
@@ -400,7 +541,7 @@
                 ret = handleTransferResult(message);
                 break;
             case BufferStatus::INVALIDATION_ACK:
-                // TODO
+                mInvalidation.onAck(message.connectionId, message.bufferId);
                 break;
         }
         if (ret == false) {
@@ -423,8 +564,15 @@
                 if (bufferIter->second->mOwnerCount == 0 &&
                         bufferIter->second->mTransactionCount == 0) {
                     // TODO: handle freebuffer insert fail
-                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
-                    mFreeBuffers.insert(bufferId);
+                    if (!bufferIter->second->mInvalidated) {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mFreeBuffers.insert(bufferId);
+                    } else {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+                        mBuffers.erase(bufferIter);
+                        mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+                    }
                 }
             }
         }
@@ -446,8 +594,15 @@
                 if (bufferIter->second->mOwnerCount == 0 &&
                     bufferIter->second->mTransactionCount == 0) {
                     // TODO: handle freebuffer insert fail
-                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
-                    mFreeBuffers.insert(bufferId);
+                    if (!bufferIter->second->mInvalidated) {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mFreeBuffers.insert(bufferId);
+                    } else {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+                        mBuffers.erase(bufferIter);
+                        mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+                    }
                 }
                 mTransactions.erase(iter);
             }
@@ -538,6 +693,121 @@
     }
 }
 
+void Accessor::Impl::BufferPool::invalidate(
+        bool needsAck, BufferId from, BufferId to,
+        const std::shared_ptr<Accessor::Impl> &impl) {
+    for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+        if (isBufferInRange(from, to, *freeIt)) {
+            auto it = mBuffers.find(*freeIt);
+            if (it != mBuffers.end() &&
+                it->second->mOwnerCount == 0 && it->second->mTransactionCount == 0) {
+                mStats.onBufferEvicted(it->second->mAllocSize);
+                mBuffers.erase(it);
+                freeIt = mFreeBuffers.erase(freeIt);
+                continue;
+            } else {
+                ALOGW("bufferpool inconsistent!");
+            }
+        }
+        ++freeIt;
+    }
+
+    size_t left = 0;
+    for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+        if (isBufferInRange(from, to, it->first)) {
+            it->second->invalidate();
+            ++left;
+        }
+    }
+    mInvalidation.onInvalidationRequest(needsAck, from, to, left, mInvalidationChannel, impl);
+}
+
+void Accessor::Impl::BufferPool::flush(const std::shared_ptr<Accessor::Impl> &impl) {
+    BufferId from = mStartSeq;
+    BufferId to = mSeq;
+    mStartSeq = mSeq;
+    // TODO: needsAck params 
+    if (from != to) {
+        invalidate(true, from, to, impl);
+    }
+}
+
+void Accessor::Impl::invalidatorThread(
+            std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
+            std::mutex &mutex,
+            std::condition_variable &cv,
+            bool &ready) {
+    while(true) {
+        std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> copied;
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            if (!ready) {
+                cv.wait(lock);
+            }
+            copied.insert(accessors.begin(), accessors.end());
+        }
+        std::list<ConnectionId> erased;
+        for (auto it = copied.begin(); it != copied.end(); ++it) {
+            const std::shared_ptr<Accessor::Impl> impl = it->second.lock();
+            if (!impl) {
+                erased.push_back(it->first);
+            } else {
+                impl->handleInvalidateAck();
+            }
+        }
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            for (auto it = erased.begin(); it != erased.end(); ++it) {
+                accessors.erase(*it);
+            }
+            if (accessors.size() == 0) {
+                ready = false;
+            } else {
+                // prevent draining cpu.
+                lock.unlock();
+                std::this_thread::yield();
+            }
+        }
+    }
+}
+
+Accessor::Impl::AccessorInvalidator::AccessorInvalidator() : mReady(false) {
+    std::thread invalidator(
+            invalidatorThread,
+            std::ref(mAccessors),
+            std::ref(mMutex),
+            std::ref(mCv),
+            std::ref(mReady));
+    invalidator.detach();
+}
+
+void Accessor::Impl::AccessorInvalidator::addAccessor(
+        uint32_t accessorId, const std::weak_ptr<Accessor::Impl> &impl) {
+    bool notify = false;
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mAccessors.find(accessorId) == mAccessors.end()) {
+        if (!mReady) {
+            mReady = true;
+            notify = true;
+        }
+        mAccessors.insert(std::make_pair(accessorId, impl));
+    }
+    lock.unlock();
+    if (notify) {
+        mCv.notify_one();
+    }
+}
+
+void Accessor::Impl::AccessorInvalidator::delAccessor(uint32_t accessorId) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mAccessors.erase(accessorId);
+    if (mAccessors.size() == 0) {
+        mReady = false;
+    }
+}
+
+Accessor::Impl::AccessorInvalidator Accessor::Impl::sInvalidator;
+
 }  // namespace implementation
 }  // namespace V2_0
 }  // namespace bufferpool
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index 1d33880..6b03494 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -19,6 +19,7 @@
 
 #include <map>
 #include <set>
+#include <condition_variable>
 #include "Accessor.h"
 
 namespace android {
@@ -33,15 +34,20 @@
 
 /**
  * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
-class Accessor::Impl {
+class Accessor::Impl 
+    : public std::enable_shared_from_this<Accessor::Impl> {
 public:
     Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
 
     ~Impl();
 
     ResultStatus connect(
-            const sp<Accessor> &accessor, sp<Connection> *connection,
-            ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr);
+            const sp<Accessor> &accessor, const sp<IObserver> &observer,
+            sp<Connection> *connection,
+            ConnectionId *pConnectionId,
+            uint32_t *pMsgId,
+            const StatusDescriptor** statusDescPtr,
+            const InvalidationDescriptor** invDescPtr);
 
     ResultStatus close(ConnectionId connectionId);
 
@@ -55,8 +61,14 @@
                        BufferId bufferId,
                        const native_handle_t** handle);
 
+    void flush();
+
     void cleanUp(bool clearCache);
 
+    bool isValid();
+
+    void handleInvalidateAck();
+
 private:
     // ConnectionId = pid : (timestamp_created + seqId)
     // in order to guarantee uniqueness for each connection
@@ -78,7 +90,10 @@
         int64_t mLastCleanUpUs;
         int64_t mLastLogUs;
         BufferId mSeq;
+        BufferId mStartSeq;
+        bool mValid;
         BufferStatusObserver mObserver;
+        BufferInvalidationChannel mInvalidationChannel;
 
         std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
         std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
@@ -95,6 +110,54 @@
         std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
         std::set<BufferId> mFreeBuffers;
 
+        struct Invalidation {
+            static std::atomic<std::uint32_t> sSeqId;
+
+            struct Pending {
+                bool mNeedsAck;
+                uint32_t mFrom;
+                uint32_t mTo;
+                size_t mLeft;
+                const std::weak_ptr<Accessor::Impl> mImpl;
+                Pending(bool needsAck, uint32_t from, uint32_t to, size_t left,
+                        const std::shared_ptr<Accessor::Impl> &impl)
+                        : mNeedsAck(needsAck),
+                          mFrom(from),
+                          mTo(to),
+                          mLeft(left),
+                          mImpl(impl)
+                {}
+
+                bool invalidate(uint32_t bufferId) {
+                    return isBufferInRange(mFrom, mTo, bufferId) && --mLeft == 0;
+                }
+            };
+
+            std::list<Pending> mPendings;
+            std::map<ConnectionId, uint32_t> mAcks;
+            std::map<ConnectionId, const wp<IObserver>> mObservers;
+            uint32_t mInvalidationId;
+            uint32_t mId;
+
+            Invalidation() : mInvalidationId(0), mId(sSeqId.fetch_add(1)) {}
+
+            void onConnect(ConnectionId conId, const sp<IObserver> &observer);
+
+            void onClose(ConnectionId conId);
+
+            void onAck(ConnectionId conId, uint32_t msgId);
+
+            void onBufferInvalidated(
+                    BufferId bufferId,
+                    BufferInvalidationChannel &channel);
+
+            void onInvalidationRequest(
+                    bool needsAck, uint32_t from, uint32_t to, size_t left,
+                    BufferInvalidationChannel &channel,
+                    const std::shared_ptr<Accessor::Impl> &impl);
+
+            void onHandleAck();
+        } mInvalidation;
         /// Buffer pool statistics which tracks allocation and transfer statistics.
         struct Stats {
             /// Total size of allocations which are used or available to use.
@@ -164,6 +227,13 @@
             }
         } mStats;
 
+        bool isValid() {
+            return mValid;
+        }
+
+        void invalidate(bool needsAck, BufferId from, BufferId to,
+                        const std::shared_ptr<Accessor::Impl> &impl);
+
     public:
         /** Creates a buffer pool. */
         BufferPool();
@@ -286,8 +356,33 @@
          */
         void cleanUp(bool clearCache = false);
 
+        /**
+         * Processes pending buffer status messages and invalidate all current
+         * free buffers. Active buffers are invalidated after being inactive.
+         */
+        void flush(const std::shared_ptr<Accessor::Impl> &impl);
+
         friend class Accessor::Impl;
     } mBufferPool;
+
+    struct  AccessorInvalidator {
+        std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> mAccessors;
+        std::mutex mMutex;
+        std::condition_variable mCv;
+        bool mReady;
+
+        AccessorInvalidator();
+        void addAccessor(uint32_t accessorId, const std::weak_ptr<Accessor::Impl> &impl);
+        void delAccessor(uint32_t accessorId);
+    };
+
+    static AccessorInvalidator sInvalidator;
+
+    static void invalidatorThread(
+        std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
+        std::mutex &mutex,
+        std::condition_variable &cv,
+        bool &ready);
 };
 
 }  // namespace implementation
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
index 413125a..cd4e06e 100644
--- a/media/bufferpool/2.0/Android.bp
+++ b/media/bufferpool/2.0/Android.bp
@@ -8,6 +8,7 @@
         "BufferStatus.cpp",
         "ClientManager.cpp",
         "Connection.cpp",
+        "Observer.cpp",
     ],
     export_include_dirs: [
         "include",
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
index 0f763f7..c80beff 100644
--- a/media/bufferpool/2.0/BufferPoolClient.cpp
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -36,9 +36,9 @@
 class BufferPoolClient::Impl
         : public std::enable_shared_from_this<BufferPoolClient::Impl> {
 public:
-    explicit Impl(const sp<Accessor> &accessor);
+    explicit Impl(const sp<Accessor> &accessor, const sp<IObserver> &observer);
 
-    explicit Impl(const sp<IAccessor> &accessor);
+    explicit Impl(const sp<IAccessor> &accessor, const sp<IObserver> &observer);
 
     bool isValid() {
         return mValid;
@@ -58,6 +58,10 @@
 
     bool isActive(int64_t *lastTransactionUs, bool clearCache);
 
+    void receiveInvalidation(uint32_t msgID);
+
+    ResultStatus flush();
+
     ResultStatus allocate(const std::vector<uint8_t> &params,
                           native_handle_t **handle,
                           std::shared_ptr<BufferPoolData> *buffer);
@@ -83,10 +87,14 @@
 
     void trySyncFromRemote();
 
-    bool syncReleased();
+    bool syncReleased(uint32_t msgId = 0);
 
     void evictCaches(bool clearCache = false);
 
+    void invalidateBuffer(BufferId id);
+
+    void invalidateRange(BufferId from, BufferId to);
+
     ResultStatus allocateBufferHandle(
             const std::vector<uint8_t>& params, BufferId *bufferId,
             native_handle_t **handle);
@@ -106,6 +114,7 @@
     uint32_t mSeqId;
     ConnectionId mConnectionId;
     int64_t mLastEvictCacheUs;
+    std::unique_ptr<BufferInvalidationListener> mInvalidationListener;
 
     // CachedBuffers
     struct BufferCache {
@@ -130,12 +139,16 @@
     } mCache;
 
     // FMQ - release notifier
-    struct {
+    struct ReleaseCache {
         std::mutex mLock;
         // TODO: use only one list?(using one list may dealy sending messages?)
         std::list<BufferId> mReleasingIds;
         std::list<BufferId> mReleasedIds;
+        uint32_t mInvalidateId; // TODO: invalidation ACK to bufferpool
+        bool mInvalidateAck;
         std::unique_ptr<BufferStatusChannel> mStatusChannel;
+
+        ReleaseCache() : mInvalidateId(0), mInvalidateAck(true) {}
     } mReleasing;
 
     // This lock is held during synchronization from remote side.
@@ -162,7 +175,6 @@
 
 struct BufferPoolClient::Impl::ClientBuffer {
 private:
-    bool mInvalidated; // TODO: implement
     int64_t mExpireUs;
     bool mHasCache;
     ConnectionId mConnectionId;
@@ -177,9 +189,8 @@
 public:
     ClientBuffer(
             ConnectionId connectionId, BufferId id, native_handle_t *handle)
-            : mInvalidated(false), mHasCache(false),
-              mConnectionId(connectionId), mId(id), mHandle(handle) {
-        (void)mInvalidated;
+            : mHasCache(false), mConnectionId(connectionId),
+              mId(id), mHandle(handle) {
         mExpireUs = getTimestampNow() + kCacheTtlUs;
     }
 
@@ -190,6 +201,10 @@
         }
     }
 
+    BufferId id() const {
+        return mId;
+    }
+
     bool expire() const {
         int64_t now = getTimestampNow();
         return now >= mExpireUs;
@@ -244,41 +259,53 @@
     }
 };
 
-BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor, const sp<IObserver> &observer)
     : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
       mLastEvictCacheUs(getTimestampNow()) {
-    const StatusDescriptor *fmqDesc;
+    const StatusDescriptor *statusDesc;
+    const InvalidationDescriptor *invDesc;
     ResultStatus status = accessor->connect(
-            &mLocalConnection, &mConnectionId, &fmqDesc, true);
+            observer, true,
+            &mLocalConnection, &mConnectionId, &mReleasing.mInvalidateId,
+            &statusDesc, &invDesc);
     if (status == ResultStatus::OK) {
         mReleasing.mStatusChannel =
-                std::make_unique<BufferStatusChannel>(*fmqDesc);
+                std::make_unique<BufferStatusChannel>(*statusDesc);
+        mInvalidationListener =
+                std::make_unique<BufferInvalidationListener>(*invDesc);
         mValid = mReleasing.mStatusChannel &&
-                mReleasing.mStatusChannel->isValid();
+                mReleasing.mStatusChannel->isValid() &&
+                mInvalidationListener &&
+                mInvalidationListener->isValid();
     }
 }
 
-BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor, const sp<IObserver> &observer)
     : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
       mLastEvictCacheUs(getTimestampNow()) {
     bool valid = false;
-    sp<IObserver> observer; // TODO
     sp<IConnection>& outConnection = mRemoteConnection;
     ConnectionId& id = mConnectionId;
+    uint32_t& outMsgId = mReleasing.mInvalidateId;
     std::unique_ptr<BufferStatusChannel>& outChannel =
             mReleasing.mStatusChannel;
+    std::unique_ptr<BufferInvalidationListener>& outObserver =
+            mInvalidationListener;
     Return<void> transResult = accessor->connect(
             observer,
-            [&valid, &outConnection, &id, &outChannel]
+            [&valid, &outConnection, &id, &outMsgId, &outChannel, &outObserver]
             (ResultStatus status, sp<IConnection> connection,
-             ConnectionId connectionId, const StatusDescriptor& desc,
+             ConnectionId connectionId, uint32_t msgId,
+             const StatusDescriptor& statusDesc,
              const InvalidationDescriptor& invDesc) {
-                (void) invDesc;
                 if (status == ResultStatus::OK) {
                     outConnection = connection;
                     id = connectionId;
-                    outChannel = std::make_unique<BufferStatusChannel>(desc);
-                    if (outChannel && outChannel->isValid()) {
+                    outMsgId = msgId;
+                    outChannel = std::make_unique<BufferStatusChannel>(statusDesc);
+                    outObserver = std::make_unique<BufferInvalidationListener>(invDesc);
+                    if (outChannel && outChannel->isValid() &&
+                        outObserver && outObserver->isValid()) {
                         valid = true;
                     }
                 }
@@ -302,6 +329,24 @@
     return active;
 }
 
+void BufferPoolClient::Impl::receiveInvalidation(uint32_t messageId) {
+    std::lock_guard<std::mutex> lock(mCache.mLock);
+    syncReleased(messageId);
+    // TODO: evict cache required?
+}
+
+ResultStatus BufferPoolClient::Impl::flush() {
+    if (!mLocal || !mLocalConnection || !mValid) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    {
+        std::unique_lock<std::mutex> lock(mCache.mLock);
+        syncReleased();
+        evictCaches();
+        return mLocalConnection->flush();
+    }
+}
+
 ResultStatus BufferPoolClient::Impl::allocate(
         const std::vector<uint8_t> &params,
         native_handle_t **pHandle,
@@ -455,6 +500,11 @@
 bool BufferPoolClient::Impl::postSend(
         BufferId bufferId, ConnectionId receiver,
         TransactionId *transactionId, int64_t *timestampUs) {
+    {
+        // TODO: don't need to call syncReleased every time
+        std::lock_guard<std::mutex> lock(mCache.mLock);
+        syncReleased();
+    }
     bool ret = false;
     bool needsSync = false;
     {
@@ -538,34 +588,74 @@
 }
 
 // should have mCache.mLock
-bool BufferPoolClient::Impl::syncReleased() {
-    std::lock_guard<std::mutex> lock(mReleasing.mLock);
-    if (mReleasing.mReleasingIds.size() > 0) {
-        mReleasing.mStatusChannel->postBufferRelease(
-                mConnectionId, mReleasing.mReleasingIds,
-                mReleasing.mReleasedIds);
-    }
-    if (mReleasing.mReleasedIds.size() > 0) {
-        for (BufferId& id: mReleasing.mReleasedIds) {
-            ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
-            auto found = mCache.mBuffers.find(id);
-            if (found != mCache.mBuffers.end()) {
-                if (found->second->onCacheRelease()) {
-                    mCache.decActive_l();
+bool BufferPoolClient::Impl::syncReleased(uint32_t messageId) {
+    bool cleared = false;
+    {
+        std::lock_guard<std::mutex> lock(mReleasing.mLock);
+        if (mReleasing.mReleasingIds.size() > 0) {
+            mReleasing.mStatusChannel->postBufferRelease(
+                    mConnectionId, mReleasing.mReleasingIds,
+                    mReleasing.mReleasedIds);
+        }
+        if (mReleasing.mReleasedIds.size() > 0) {
+            for (BufferId& id: mReleasing.mReleasedIds) {
+                ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
+                auto found = mCache.mBuffers.find(id);
+                if (found != mCache.mBuffers.end()) {
+                    if (found->second->onCacheRelease()) {
+                        mCache.decActive_l();
+                    } else {
+                        // should not happen!
+                        ALOGW("client %lld cache release status inconsitent!",
+                            (long long)mConnectionId);
+                    }
                 } else {
                     // should not happen!
-                    ALOGW("client %lld cache release status inconsitent!",
-                          (long long)mConnectionId);
+                    ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
                 }
+            }
+            mReleasing.mReleasedIds.clear();
+            cleared = true;
+        }
+    }
+    std::vector<BufferInvalidationMessage> invalidations;
+    mInvalidationListener->getInvalidations(invalidations);
+    uint32_t lastMsgId = 0;
+    if (invalidations.size() > 0) {
+        for (auto it = invalidations.begin(); it != invalidations.end(); ++it) {
+            if (it->messageId != 0) {
+                lastMsgId = it->messageId;
+            }
+            if (it->fromBufferId == it->toBufferId) {
+                // TODO: handle fromBufferId = UINT32_MAX
+                invalidateBuffer(it->fromBufferId);
             } else {
-                // should not happen!
-                ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
+                invalidateRange(it->fromBufferId, it->toBufferId);
             }
         }
-        mReleasing.mReleasedIds.clear();
-        return true;
     }
-    return false;
+    {
+        std::lock_guard<std::mutex> lock(mReleasing.mLock);
+        if (lastMsgId != 0) {
+            if (isMessageLater(lastMsgId, mReleasing.mInvalidateId)) {
+                mReleasing.mInvalidateId = lastMsgId;
+                mReleasing.mInvalidateAck = false;
+            }
+        } else if (messageId != 0) {
+            // messages are drained.
+            if (isMessageLater(messageId, mReleasing.mInvalidateId)) {
+                mReleasing.mInvalidateId = lastMsgId;
+                mReleasing.mInvalidateAck = true;
+            }
+        }
+        if (!mReleasing.mInvalidateAck) {
+            // post ACK
+            mReleasing.mStatusChannel->postBufferInvalidateAck(
+                    mConnectionId,
+                    mReleasing.mInvalidateId, &mReleasing.mInvalidateAck);
+        }
+    }
+    return cleared;
 }
 
 // should have mCache.mLock
@@ -587,6 +677,49 @@
     }
 }
 
+// should have mCache.mLock
+void BufferPoolClient::Impl::invalidateBuffer(BufferId id) {
+    for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end(); ++it) {
+        if (id == it->second->id()) {
+            if (!it->second->hasCache()) {
+                mCache.mBuffers.erase(it);
+                ALOGV("cache invalidated %lld : buffer %u",
+                      (long long)mConnectionId, id);
+            } else {
+                ALOGW("Inconsitent invalidation %lld : activer buffer!! %u",
+                      (long long)mConnectionId, (unsigned int)id);
+            }
+            break;
+        }
+    }
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::invalidateRange(BufferId from, BufferId to) {
+    size_t invalidated = 0;
+    for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+        if (!it->second->hasCache()) {
+            BufferId bid = it->second->id();
+            if (from < to) {
+                if (from <= bid && bid < to) {
+                    ++invalidated;
+                    it = mCache.mBuffers.erase(it);
+                    continue;
+                }
+            } else {
+                if (from <= bid || bid < to) {
+                    ++invalidated;
+                    it = mCache.mBuffers.erase(it);
+                    continue;
+                }
+            }
+        }
+        ++it;
+    }
+    ALOGV("cache invalidated %lld : # of invalidated %zu",
+          (long long)mConnectionId, invalidated);
+}
+
 ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
         const std::vector<uint8_t>& params, BufferId *bufferId,
         native_handle_t** handle) {
@@ -629,12 +762,14 @@
 }
 
 
-BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
-    mImpl = std::make_shared<Impl>(accessor);
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor,
+                                   const sp<IObserver> &observer) {
+    mImpl = std::make_shared<Impl>(accessor, observer);
 }
 
-BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
-    mImpl = std::make_shared<Impl>(accessor);
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor,
+                                   const sp<IObserver> &observer) {
+    mImpl = std::make_shared<Impl>(accessor, observer);
 }
 
 BufferPoolClient::~BufferPoolClient() {
@@ -672,6 +807,19 @@
     return ResultStatus::CRITICAL_ERROR;
 }
 
+void BufferPoolClient::receiveInvalidation(uint32_t msgId) {
+    if (isValid()) {
+        mImpl->receiveInvalidation(msgId);
+    }
+}
+
+ResultStatus BufferPoolClient::flush() {
+    if (isValid()) {
+        return mImpl->flush();
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
 ResultStatus BufferPoolClient::allocate(
         const std::vector<uint8_t> &params,
         native_handle_t **handle,
diff --git a/media/bufferpool/2.0/BufferPoolClient.h b/media/bufferpool/2.0/BufferPoolClient.h
index 1889ea3..e8d9ae6 100644
--- a/media/bufferpool/2.0/BufferPoolClient.h
+++ b/media/bufferpool/2.0/BufferPoolClient.h
@@ -49,7 +49,8 @@
      * Creates a buffer pool client from a local buffer pool
      * (via ClientManager#create).
      */
-    explicit BufferPoolClient(const sp<Accessor> &accessor);
+    explicit BufferPoolClient(const sp<Accessor> &accessor,
+                              const sp<IObserver> &observer);
 
     /**
      * Creates a buffer pool client from a remote buffer pool
@@ -57,7 +58,8 @@
      * Note: A buffer pool client created with remote buffer pool cannot
      * allocate a buffer.
      */
-    explicit BufferPoolClient(const sp<IAccessor> &accessor);
+    explicit BufferPoolClient(const sp<IAccessor> &accessor,
+                              const sp<IObserver> &observer);
 
     /** Destructs a buffer pool client. */
     ~BufferPoolClient();
@@ -73,6 +75,10 @@
 
     ResultStatus getAccessor(sp<IAccessor> *accessor);
 
+    void receiveInvalidation(uint32_t msgId);
+
+    ResultStatus flush();
+
     ResultStatus allocate(const std::vector<uint8_t> &params,
                           native_handle_t **handle,
                           std::shared_ptr<BufferPoolData> *buffer);
@@ -92,6 +98,7 @@
     std::shared_ptr<Impl> mImpl;
 
     friend struct ClientManager;
+    friend struct Observer;
 };
 
 }  // namespace implementation
diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp
index 0d3f5a3..6937260 100644
--- a/media/bufferpool/2.0/BufferStatus.cpp
+++ b/media/bufferpool/2.0/BufferStatus.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "BufferPoolStatus"
 //#define LOG_NDEBUG 0
 
+#include <thread>
 #include <time.h>
 #include "BufferStatus.h"
 
@@ -37,6 +38,18 @@
     return stamp;
 }
 
+bool isMessageLater(uint32_t curMsgId, uint32_t prevMsgId) {
+    return curMsgId != prevMsgId && curMsgId - prevMsgId < prevMsgId - curMsgId;
+}
+
+bool isBufferInRange(BufferId from, BufferId to, BufferId bufferId) {
+    if (from < to) {
+        return from <= bufferId && bufferId < to;
+    } else { // wrap happens
+        return from <= bufferId || bufferId < to;
+    }
+}
+
 static constexpr int kNumElementsInQueue = 1024*16;
 static constexpr int kMinElementsToSyncInQueue = 128;
 
@@ -139,6 +152,29 @@
     }
 }
 
+void BufferStatusChannel::postBufferInvalidateAck(
+        ConnectionId connectionId,
+        uint32_t invalidateId,
+        bool *invalidated) {
+    if (mValid && !*invalidated) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        if (avail > 0) {
+            BufferStatusMessage message;
+            message.newStatus = BufferStatus::INVALIDATION_ACK;
+            message.bufferId = invalidateId;
+            message.connectionId = connectionId;
+            if (!mBufferStatusQueue->write(&message, 1)) {
+                // Since avaliable # of writes are already confirmed,
+                // this should not happen.
+                // TODO: error handing?
+                ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+                return;
+            }
+            *invalidated = true;
+        }
+    }
+}
+
 bool BufferStatusChannel::postBufferStatusMessage(
         TransactionId transactionId, BufferId bufferId,
         BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
@@ -182,6 +218,83 @@
     return false;
 }
 
+BufferInvalidationListener::BufferInvalidationListener(
+        const InvalidationDescriptor &fmqDesc) {
+    std::unique_ptr<BufferInvalidationQueue> queue =
+            std::make_unique<BufferInvalidationQueue>(fmqDesc);
+    if (!queue || queue->isValid() == false) {
+        mValid = false;
+        return;
+    }
+    mValid  = true;
+    mBufferInvalidationQueue = std::move(queue);
+    // drain previous messages
+    size_t avail = std::min(
+            mBufferInvalidationQueue->availableToRead(), (size_t) kNumElementsInQueue);
+    std::vector<BufferInvalidationMessage> temp(avail);
+    if (avail > 0) {
+        mBufferInvalidationQueue->read(temp.data(), avail);
+    }
+}
+
+void BufferInvalidationListener::getInvalidations(
+        std::vector<BufferInvalidationMessage> &messages) {
+    // Try twice in case of overflow.
+    // TODO: handling overflow though it may not happen.
+    for (int i = 0; i < 2; ++i) {
+        size_t avail = std::min(
+                mBufferInvalidationQueue->availableToRead(), (size_t) kNumElementsInQueue);
+        if (avail > 0) {
+            std::vector<BufferInvalidationMessage> temp(avail);
+            if (mBufferInvalidationQueue->read(temp.data(), avail)) {
+                messages.reserve(messages.size() + avail);
+                for (auto it = temp.begin(); it != temp.end(); ++it) {
+                    messages.push_back(*it);
+                }
+                break;
+            }
+        } else {
+            return;
+        }
+    }
+}
+
+bool BufferInvalidationListener::isValid() {
+    return mValid;
+}
+
+BufferInvalidationChannel::BufferInvalidationChannel()
+    : mValid(true),
+      mBufferInvalidationQueue(
+              std::make_unique<BufferInvalidationQueue>(kNumElementsInQueue, true)) {
+    if (!mBufferInvalidationQueue || mBufferInvalidationQueue->isValid() == false) {
+        mValid = false;
+    }
+}
+
+bool BufferInvalidationChannel::isValid() {
+    return mValid;
+}
+
+void BufferInvalidationChannel::getDesc(const InvalidationDescriptor **fmqDescPtr) {
+    if (mValid) {
+        *fmqDescPtr = mBufferInvalidationQueue->getDesc();
+    } else {
+        *fmqDescPtr = nullptr;
+    }
+}
+
+void BufferInvalidationChannel::postInvalidation(
+        uint32_t msgId, BufferId fromId, BufferId toId) {
+    BufferInvalidationMessage message;
+
+    message.messageId = msgId;
+    message.fromBufferId = fromId;
+    message.toBufferId = toId;
+    // TODO: handle failure (it does not happen normally.)
+    mBufferInvalidationQueue->write(&message);
+}
+
 }  // namespace implementation
 }  // namespace V2_0
 }  // namespace bufferpool
diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h
index 777a320..fa65838 100644
--- a/media/bufferpool/2.0/BufferStatus.h
+++ b/media/bufferpool/2.0/BufferStatus.h
@@ -37,9 +37,13 @@
 /** Returns monotonic timestamp in Us since fixed point in time. */
 int64_t getTimestampNow();
 
+bool isMessageLater(uint32_t curMsgId, uint32_t prevMsgId);
+
+bool isBufferInRange(BufferId from, BufferId to, BufferId bufferId);
+
 /**
- * A collection of FMQ for a buffer pool. buffer ownership/status change
- * messages are sent via the FMQs from the clients.
+ * A collection of buffer status message FMQ for a buffer pool. buffer
+ * ownership/status change messages are sent via the FMQs from the clients.
  */
 class BufferStatusObserver {
 private:
@@ -47,7 +51,8 @@
             mBufferStatusQueues;
 
 public:
-    /** Creates an FMQ for the specified connection(client).
+    /** Creates a buffer status message FMQ for the specified
+     * connection(client).
      *
      * @param connectionId  connection Id of the specified client.
      * @param fmqDescPtr    double ptr of created FMQ's descriptor.
@@ -58,7 +63,8 @@
      */
     ResultStatus open(ConnectionId id, const StatusDescriptor** fmqDescPtr);
 
-    /** Closes an FMQ for the specified connection(client).
+    /** Closes a buffer status message FMQ for the specified
+     * connection(client).
      *
      * @param connectionId  connection Id of the specified client.
      *
@@ -75,8 +81,8 @@
 };
 
 /**
- * An FMQ for a buffer pool client. Buffer ownership/status change messages
- * are sent via the fmq to the buffer pool.
+ * A buffer status message FMQ for a buffer pool client. Buffer ownership/status
+ * change messages are sent via the fmq to the buffer pool.
  */
 class BufferStatusChannel {
 private:
@@ -85,7 +91,8 @@
 
 public:
     /**
-     * Connects to an FMQ from a descriptor of the created FMQ.
+     * Connects to a buffer status message FMQ from a descriptor of
+     * the created FMQ.
      *
      * @param fmqDesc   Descriptor of the created FMQ.
      */
@@ -131,6 +138,86 @@
             ConnectionId connectionId,
             ConnectionId targetId,
             std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+    /**
+     * Posts a buffer invaliadation messge to the buffer pool.
+     *
+     * @param connectionId  connection Id of the client.
+     * @param invalidateId  invalidation ack to the buffer pool.
+     *                      if invalidation id is zero, the ack will not be
+     *                      posted.
+     * @param invalidated   sets {@code true} only when the invalidation ack is
+     *                      posted.
+     */
+    void postBufferInvalidateAck(
+            ConnectionId connectionId,
+            uint32_t invalidateId,
+            bool *invalidated);
+};
+
+/**
+ * A buffer invalidation FMQ for a buffer pool client. Buffer invalidation
+ * messages are received via the fmq from the buffer pool. Buffer invalidation
+ * messages are handled as soon as possible.
+ */
+class BufferInvalidationListener {
+private:
+    bool mValid;
+    std::unique_ptr<BufferInvalidationQueue> mBufferInvalidationQueue;
+
+public:
+    /**
+     * Connects to a buffer invalidation FMQ from a descriptor of the created FMQ.
+     *
+     * @param fmqDesc   Descriptor of the created FMQ.
+     */
+    BufferInvalidationListener(const InvalidationDescriptor &fmqDesc);
+
+    /** Retrieves all pending buffer invalidation messages from the buffer pool.
+     *
+     * @param messages  retrieved pending messages.
+     */
+    void getInvalidations(std::vector<BufferInvalidationMessage> &messages);
+
+    /** Returns whether the FMQ is connected succesfully. */
+    bool isValid();
+};
+
+/**
+ * A buffer invalidation FMQ for a buffer pool. A buffer pool will send buffer
+ * invalidation messages to the clients via the FMQ. The FMQ is shared among
+ * buffer pool clients.
+ */
+class BufferInvalidationChannel {
+private:
+    bool mValid;
+    std::unique_ptr<BufferInvalidationQueue> mBufferInvalidationQueue;
+
+public:
+    /**
+     * Creates a buffer invalidation FMQ for a buffer pool.
+     */
+    BufferInvalidationChannel();
+
+    /** Returns whether the FMQ is connected succesfully. */
+    bool isValid();
+
+    /**
+     * Retrieves the descriptor of a buffer invalidation FMQ. the descriptor may
+     * be passed to the client for buffer invalidation handling.
+     *
+     * @param fmqDescPtr    double ptr of created FMQ's descriptor.
+     */
+    void getDesc(const InvalidationDescriptor **fmqDescPtr);
+
+    /** Posts a buffer invalidation for invalidated buffers.
+     *
+     * @param msgId     Invalidation message id which is used when clients send
+     *                  acks back via BufferStatusMessage
+     * @param fromId    The start bufferid of the invalidated buffers(inclusive)
+     * @param toId      The end bufferId of the invalidated buffers(inclusive)
+     */
+    void postInvalidation(uint32_t msgId, BufferId fromId, BufferId toId);
 };
 
 }  // namespace implementation
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
index eeaf093..f8531d8 100644
--- a/media/bufferpool/2.0/ClientManager.cpp
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <utils/Log.h>
 #include "BufferPoolClient.h"
+#include "Observer.h"
 
 namespace android {
 namespace hardware {
@@ -106,6 +107,8 @@
 
     ResultStatus close(ConnectionId connectionId);
 
+    ResultStatus flush(ConnectionId connectionId);
+
     ResultStatus allocate(ConnectionId connectionId,
                           const std::vector<uint8_t> &params,
                           native_handle_t **handle,
@@ -153,10 +156,13 @@
                 mClients;
     } mActive;
 
+    sp<Observer> mObserver;
+
     ClientManagerCookieHolder mRemoteClientCookies;
 };
 
-ClientManager::Impl::Impl() {}
+ClientManager::Impl::Impl()
+    : mObserver(new Observer()) {}
 
 ResultStatus ClientManager::Impl::registerSender(
         const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
@@ -185,7 +191,7 @@
             lock.unlock();
             ResultStatus result = ResultStatus::OK;
             const std::shared_ptr<BufferPoolClient> client =
-                    std::make_shared<BufferPoolClient>(accessor);
+                    std::make_shared<BufferPoolClient>(accessor, mObserver);
             lock.lock();
             if (!client) {
                 result = ResultStatus::NO_MEMORY;
@@ -197,6 +203,7 @@
                 const std::weak_ptr<BufferPoolClient> wclient = client;
                 mCache.mClients.push_back(std::make_pair(accessor, wclient));
                 ConnectionId conId = client->getConnectionId();
+                mObserver->addClient(conId, wclient);
                 {
                     std::lock_guard<std::mutex> lock(mActive.mMutex);
                     mActive.mClients.insert(std::make_pair(conId, client));
@@ -266,8 +273,9 @@
     if (!accessor || !accessor->isValid()) {
         return ResultStatus::CRITICAL_ERROR;
     }
+    // TODO: observer is local. use direct call instead of hidl call.
     std::shared_ptr<BufferPoolClient> client =
-            std::make_shared<BufferPoolClient>(accessor);
+            std::make_shared<BufferPoolClient>(accessor, mObserver);
     if (!client || !client->isValid()) {
         return ResultStatus::CRITICAL_ERROR;
     }
@@ -280,6 +288,7 @@
         const std::weak_ptr<BufferPoolClient> wclient = client;
         mCache.mClients.push_back(std::make_pair(accessor, wclient));
         ConnectionId conId = client->getConnectionId();
+        mObserver->addClient(conId, wclient);
         {
             std::lock_guard<std::mutex> lock(mActive.mMutex);
             mActive.mClients.insert(std::make_pair(conId, client));
@@ -291,12 +300,13 @@
 }
 
 ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
-    std::lock_guard<std::mutex> lock1(mCache.mMutex);
-    std::lock_guard<std::mutex> lock2(mActive.mMutex);
+    std::unique_lock<std::mutex> lock1(mCache.mMutex);
+    std::unique_lock<std::mutex> lock2(mActive.mMutex);
     auto it = mActive.mClients.find(connectionId);
     if (it != mActive.mClients.end()) {
         sp<IAccessor> accessor;
         it->second->getAccessor(&accessor);
+        std::shared_ptr<BufferPoolClient> closing = it->second;
         mActive.mClients.erase(connectionId);
         for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
             // clean up dead client caches
@@ -307,11 +317,27 @@
                 cit++;
             }
         }
+        lock2.unlock();
+        lock1.unlock();
+        closing->flush();
         return ResultStatus::OK;
     }
     return ResultStatus::NOT_FOUND;
 }
 
+ResultStatus ClientManager::Impl::flush(ConnectionId connectionId) {
+    std::shared_ptr<BufferPoolClient> client;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(connectionId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        client = it->second;
+    }
+    return client->flush();
+}
+
 ResultStatus ClientManager::Impl::allocate(
         ConnectionId connectionId, const std::vector<uint8_t> &params,
         native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
@@ -461,6 +487,13 @@
     return ResultStatus::CRITICAL_ERROR;
 }
 
+ResultStatus ClientManager::flush(ConnectionId connectionId) {
+    if (mImpl) {
+        return mImpl->flush(connectionId);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
 ResultStatus ClientManager::allocate(
         ConnectionId connectionId, const std::vector<uint8_t> &params,
         native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp
index cd837a1..6bd0e79 100644
--- a/media/bufferpool/2.0/Connection.cpp
+++ b/media/bufferpool/2.0/Connection.cpp
@@ -60,6 +60,13 @@
     }
 }
 
+ResultStatus Connection::flush() {
+    if (mInitialized && mAccessor) {
+        return mAccessor->flush();
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
 ResultStatus Connection::allocate(
         const std::vector<uint8_t> &params, BufferId *bufferId,
         const native_handle_t **handle) {
diff --git a/media/bufferpool/2.0/Connection.h b/media/bufferpool/2.0/Connection.h
index e2b47f1..8507749 100644
--- a/media/bufferpool/2.0/Connection.h
+++ b/media/bufferpool/2.0/Connection.h
@@ -44,6 +44,11 @@
     Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
 
     /**
+     * Invalidates all buffers which are active and/or are ready to be recycled.
+     */
+    ResultStatus flush();
+
+    /**
      * Allocates a buffer using the specified parameters. Recycles a buffer if
      * it is possible. The returned buffer can be transferred to other remote
      * clients(Connection).
diff --git a/media/bufferpool/2.0/Observer.cpp b/media/bufferpool/2.0/Observer.cpp
new file mode 100644
index 0000000..5b23160
--- /dev/null
+++ b/media/bufferpool/2.0/Observer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Observer.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+Observer::Observer() {
+}
+
+Observer::~Observer() {
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IObserver follow.
+Return<void> Observer::onMessage(int64_t connectionId, uint32_t msgId) {
+    std::unique_lock<std::mutex> lock(mLock);
+    auto it = mClients.find(connectionId);
+    if (it != mClients.end()) {
+        const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+        if (!client) {
+            mClients.erase(it);
+        } else {
+            lock.unlock();
+            client->receiveInvalidation(msgId);
+        }
+    }
+    return Void();
+}
+
+void Observer::addClient(ConnectionId connectionId,
+                         const std::weak_ptr<BufferPoolClient> &wclient) {
+    std::lock_guard<std::mutex> lock(mLock);
+    for (auto it = mClients.begin(); it != mClients.end();) {
+        if (!it->second.lock() || it->first == connectionId) {
+            it = mClients.erase(it);
+        } else {
+            ++it;
+        }
+    }
+    mClients.insert(std::make_pair(connectionId, wclient));
+
+}
+
+void Observer::delClient(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mClients.erase(connectionId);
+}
+
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Observer.h b/media/bufferpool/2.0/Observer.h
new file mode 100644
index 0000000..42bd7c1
--- /dev/null
+++ b/media/bufferpool/2.0/Observer.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
+
+#include <android/hardware/media/bufferpool/2.0/IObserver.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Observer : public IObserver {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IObserver follow.
+    Return<void> onMessage(int64_t connectionId, uint32_t msgId) override;
+
+    ~Observer();
+
+    void addClient(ConnectionId connectionId,
+                   const std::weak_ptr<BufferPoolClient> &wclient);
+
+    void delClient(ConnectionId connectionId);
+
+private:
+    Observer();
+
+    friend struct ClientManager;
+
+    std::mutex mLock;
+    std::map<ConnectionId, const std::weak_ptr<BufferPoolClient>> mClients;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
diff --git a/media/bufferpool/2.0/include/bufferpool/ClientManager.h b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
index cfc3bc3..953c304 100644
--- a/media/bufferpool/2.0/include/bufferpool/ClientManager.h
+++ b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
@@ -92,6 +92,18 @@
     ResultStatus close(ConnectionId connectionId);
 
     /**
+     * Evicts cached allocations. If it's local connection, release the
+     * previous allocations and do not recycle current active allocations.
+     *
+     * @param connectionId The id of the connection.
+     *
+     * @return OK when the connection is resetted.
+     *         NOT_FOUND when the specified connection was not found.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus flush(ConnectionId connectionId);
+
+    /**
      * Allocates a buffer from the specified connection.
      *
      * @param connectionId  The id of the connection.
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index fcfdaff..be6d9fd 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -622,6 +622,8 @@
             AMediaFormat_setInt32(mTrackMetadata,
                     AMEDIAFORMAT_KEY_SAMPLE_RATE, getSampleRate());
             AMediaFormat_setInt32(mTrackMetadata,
+                    AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, getBitsPerSample());
+            AMediaFormat_setInt32(mTrackMetadata,
                     AMEDIAFORMAT_KEY_PCM_ENCODING, kAudioEncodingPcm16bit);
             // sample rate is non-zero, so division by zero not possible
             AMediaFormat_setInt64(mTrackMetadata,
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 9a329f1..ddc7031 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -311,6 +311,7 @@
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, mNumChannels);
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, mChannelMask);
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, mSampleRate);
+                AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, mBitsPerSample);
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_PCM_ENCODING,
                         kAudioEncodingPcm16bit);
 
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index ef9a753..64deb60 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -163,8 +163,7 @@
                             const float *needle, int needleSize,
                             LatencyReport *report) {
     const double threshold = 0.1;
-    printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n",
-           haystackSize, needleSize);
+    // printf("%s: haystackSize = %d, needleSize = %d\n", __func__, haystackSize, needleSize);
 
     // Find first peak
     int first = (int) (findFirstMatch(haystack,
@@ -181,8 +180,6 @@
                                       needle,
                                       needleSize,
                                       threshold) + 0.5);
-
-    printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again);
     first = again;
 
     // Allocate results array
@@ -199,7 +196,7 @@
 
     // Add higher harmonics mapped onto lower harmonics.
     // This reinforces the "fundamental" echo.
-    const int numEchoes = 10;
+    const int numEchoes = 8;
     for (int partial = 1; partial < numEchoes; partial++) {
         for (int i = 0; i < numCorrelations; i++) {
             harmonicSums[i / partial] += correlations[i] / partial;
@@ -216,7 +213,7 @@
             maxCorrelation = harmonicSums[i];
             sumOfPeaks += maxCorrelation;
             peakIndex = i;
-            printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
+            // printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
         }
     }
 
@@ -476,9 +473,13 @@
     void report() override {
 
         printf("EchoAnalyzer ---------------\n");
-        printf(LOOPBACK_RESULT_TAG "measured.gain          = %f\n", mMeasuredLoopGain);
-        printf(LOOPBACK_RESULT_TAG "echo.gain              = %f\n", mEchoGain);
-        printf(LOOPBACK_RESULT_TAG "test.state             = %d\n", mState);
+        printf(LOOPBACK_RESULT_TAG "measured.gain          = %8f\n", mMeasuredLoopGain);
+        printf(LOOPBACK_RESULT_TAG "echo.gain              = %8f\n", mEchoGain);
+        printf(LOOPBACK_RESULT_TAG "test.state             = %8d\n", mState);
+        printf(LOOPBACK_RESULT_TAG "test.state.name        = %8s\n", convertStateToText(mState));
+        if (mState == STATE_WAITING_FOR_SILENCE) {
+            printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
+        }
         if (mMeasuredLoopGain >= 0.9999) {
             printf("   ERROR - clipping, turn down volume slightly\n");
         } else {
@@ -491,9 +492,12 @@
                 printf("   ERROR - confidence too low = %f\n", mLatencyReport.confidence);
             } else {
                 double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
-                printf(LOOPBACK_RESULT_TAG "latency.frames        = %8.2f\n", mLatencyReport.latencyInFrames);
-                printf(LOOPBACK_RESULT_TAG "latency.msec          = %8.2f\n", latencyMillis);
-                printf(LOOPBACK_RESULT_TAG "latency.confidence    = %8.6f\n", mLatencyReport.confidence);
+                printf(LOOPBACK_RESULT_TAG "latency.frames         = %8.2f\n",
+                       mLatencyReport.latencyInFrames);
+                printf(LOOPBACK_RESULT_TAG "latency.msec           = %8.2f\n",
+                       latencyMillis);
+                printf(LOOPBACK_RESULT_TAG "latency.confidence     = %8.6f\n",
+                       mLatencyReport.confidence);
             }
         }
     }
@@ -527,7 +531,7 @@
         int numWritten;
         int numSamples;
 
-        echo_state_t nextState = mState;
+        echo_state nextState = mState;
 
         switch (mState) {
             case STATE_INITIAL_SILENCE:
@@ -553,6 +557,7 @@
                         //       mLoopCounter, peak);
                         mDownCounter = 8;
                         mMeasuredLoopGain = peak;  // assumes original pulse amplitude is one
+                        mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
                         // Calculate gain that will give us a nice decaying echo.
                         mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
                         if (mEchoGain > MAX_ECHO_GAIN) {
@@ -638,7 +643,7 @@
 
 private:
 
-    enum echo_state_t {
+    enum echo_state {
         STATE_INITIAL_SILENCE,
         STATE_MEASURING_GAIN,
         STATE_WAITING_FOR_SILENCE,
@@ -648,6 +653,35 @@
         STATE_FAILED
     };
 
+    const char *convertStateToText(echo_state state) {
+        const char *result = "Unknown";
+        switch(state) {
+            case STATE_INITIAL_SILENCE:
+                result = "INIT";
+                break;
+            case STATE_MEASURING_GAIN:
+                result = "GAIN";
+                break;
+            case STATE_WAITING_FOR_SILENCE:
+                result = "SILENCE";
+                break;
+            case STATE_SENDING_PULSE:
+                result = "PULSE";
+                break;
+            case STATE_GATHERING_ECHOS:
+                result = "ECHOS";
+                break;
+            case STATE_DONE:
+                result = "DONE";
+                break;
+            case STATE_FAILED:
+                result = "FAILED";
+                break;
+        }
+        return result;
+    }
+
+
     int32_t         mDownCounter = 500;
     int32_t         mLoopCounter = 0;
     int32_t         mSampleIndex = 0;
@@ -656,7 +690,7 @@
     float           mMeasuredLoopGain = 0.0f;
     float           mDesiredEchoGain = 0.95f;
     float           mEchoGain = 1.0f;
-    echo_state_t    mState = STATE_INITIAL_SILENCE;
+    echo_state      mState = STATE_INITIAL_SILENCE;
 
     AudioRecording  mAudioRecording; // contains only the input after the gain detection burst
     LatencyReport   mLatencyReport;
@@ -680,21 +714,32 @@
 
     void report() override {
         printf("SineAnalyzer ------------------\n");
-        printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %7.5f\n", mPeakAmplitude);
-        printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %7.5f\n", mMagnitude);
-        printf(LOOPBACK_RESULT_TAG "phase.offset       = %7.5f\n", mPhaseOffset);
-        printf(LOOPBACK_RESULT_TAG "ref.phase          = %7.5f\n", mPhase);
-        printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
-        printf(LOOPBACK_RESULT_TAG "sine.period        = %6d\n", mSinePeriod);
-        printf(LOOPBACK_RESULT_TAG "test.state         = %6d\n", mState);
-        printf(LOOPBACK_RESULT_TAG "frame.count        = %6d\n", mFrameCounter);
+        printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %8f\n", mPeakAmplitude);
+        printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %8f\n", mMagnitude);
+        printf(LOOPBACK_RESULT_TAG "peak.noise         = %8f\n", mPeakNoise);
+        printf(LOOPBACK_RESULT_TAG "rms.noise          = %8f\n", mRootMeanSquareNoise);
+        float amplitudeRatio = mMagnitude / mPeakNoise;
+        float signalToNoise = amplitudeRatio * amplitudeRatio;
+        printf(LOOPBACK_RESULT_TAG "signal.to.noise    = %8.2f\n", signalToNoise);
+        float signalToNoiseDB = 10.0 * log(signalToNoise);
+        printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
+        if (signalToNoiseDB < MIN_SNRATIO_DB) {
+            printf("WARNING - signal to noise ratio is too low! < %d dB\n", MIN_SNRATIO_DB);
+        }
+        printf(LOOPBACK_RESULT_TAG "phase.offset       = %8.5f\n", mPhaseOffset);
+        printf(LOOPBACK_RESULT_TAG "ref.phase          = %8.5f\n", mPhase);
+        printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
+        printf(LOOPBACK_RESULT_TAG "sine.period        = %8d\n", mSinePeriod);
+        printf(LOOPBACK_RESULT_TAG "test.state         = %8d\n", mState);
+        printf(LOOPBACK_RESULT_TAG "frame.count        = %8d\n", mFrameCounter);
         // Did we ever get a lock?
         bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
         if (!gotLock) {
             printf("ERROR - failed to lock on reference sine tone\n");
         } else {
             // Only print if meaningful.
-            printf(LOOPBACK_RESULT_TAG "glitch.count       = %6d\n", mGlitchCount);
+            printf(LOOPBACK_RESULT_TAG "glitch.count       = %8d\n", mGlitchCount);
+            printf(LOOPBACK_RESULT_TAG "max.glitch         = %8f\n", mMaxGlitchDelta);
         }
     }
 
@@ -732,15 +777,48 @@
         }
 
         for (int i = 0; i < numFrames; i++) {
+            bool sineEnabled = true;
             float sample = inputData[i * inputChannelCount];
 
             float sinOut = sinf(mPhase);
 
             switch (mState) {
                 case STATE_IDLE:
-                case STATE_IMMUNE:
-                case STATE_WAITING_FOR_SIGNAL:
+                    sineEnabled = false;
+                    mDownCounter--;
+                    if (mDownCounter <= 0) {
+                        mState = STATE_MEASURE_NOISE;
+                        mDownCounter = NOISE_FRAME_COUNT;
+                    }
                     break;
+                case STATE_MEASURE_NOISE:
+                    sineEnabled = false;
+                    mPeakNoise = std::max(abs(sample), mPeakNoise);
+                    mNoiseSumSquared += sample * sample;
+                    mDownCounter--;
+                    if (mDownCounter <= 0) {
+                        mState = STATE_WAITING_FOR_SIGNAL;
+                        mRootMeanSquareNoise = sqrt(mNoiseSumSquared / NOISE_FRAME_COUNT);
+                        mTolerance = std::max(MIN_TOLERANCE, mPeakNoise * 2.0f);
+                        mPhase = 0.0; // prevent spike at start
+                    }
+                    break;
+
+                case STATE_IMMUNE:
+                    mDownCounter--;
+                    if (mDownCounter <= 0) {
+                        mState = STATE_WAITING_FOR_SIGNAL;
+                    }
+                    break;
+
+                case STATE_WAITING_FOR_SIGNAL:
+                    if (peak > mThreshold) {
+                        mState = STATE_WAITING_FOR_LOCK;
+                        //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
+                        resetAccumulator();
+                    }
+                    break;
+
                 case STATE_WAITING_FOR_LOCK:
                     mSinAccumulator += sample * sinOut;
                     mCosAccumulator += sample * cosf(mPhase);
@@ -766,13 +844,14 @@
                     // printf("    predicted = %f, actual = %f\n", predicted, sample);
 
                     float diff = predicted - sample;
-                    if (fabs(diff) > mTolerance) {
+                    float absDiff = fabs(diff);
+                    mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
+                    if (absDiff > mTolerance) {
                         mGlitchCount++;
                         //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
                         //       mFrameCounter, mGlitchCount, predicted, sample);
                         mState = STATE_IMMUNE;
-                        //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
-                        mDownCounter = mSinePeriod;  // Set duration of IMMUNE state.
+                        mDownCounter = mSinePeriod * PERIODS_IMMUNE;
                     }
 
                     // Track incoming signal and slowly adjust magnitude to account
@@ -792,44 +871,23 @@
                 } break;
             }
 
+            float output = 0.0f;
             // Output sine wave so we can measure it.
-            outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
-                    + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
-            // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut,  mPhaseIncrement);
-
-            // advance and wrap phase
-            mPhase += mPhaseIncrement;
-            if (mPhase > M_PI) {
-                mPhase -= (2.0 * M_PI);
+            if (sineEnabled) {
+                output = (sinOut * mOutputAmplitude)
+                         + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
+                // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut,  mPhaseIncrement);
+                // advance and wrap phase
+                mPhase += mPhaseIncrement;
+                if (mPhase > M_PI) {
+                    mPhase -= (2.0 * M_PI);
+                }
             }
+            outputData[i * outputChannelCount] = output;
+
 
             mFrameCounter++;
         }
-
-        // Do these once per buffer.
-        switch (mState) {
-            case STATE_IDLE:
-                mState = STATE_IMMUNE; // so we can tell when
-                break;
-            case STATE_IMMUNE:
-                mDownCounter -= numFrames;
-                if (mDownCounter <= 0) {
-                    mState = STATE_WAITING_FOR_SIGNAL;
-                    //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
-                }
-                break;
-            case STATE_WAITING_FOR_SIGNAL:
-                if (peak > mThreshold) {
-                    mState = STATE_WAITING_FOR_LOCK;
-                    //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
-                    resetAccumulator();
-                }
-                break;
-            case STATE_WAITING_FOR_LOCK:
-            case STATE_LOCKED:
-                break;
-        }
-
     }
 
     void resetAccumulator() {
@@ -840,18 +898,24 @@
 
     void reset() override {
         mGlitchCount = 0;
-        mState = STATE_IMMUNE;
-        mDownCounter = IMMUNE_FRAME_COUNT;
+        mState = STATE_IDLE;
+        mDownCounter = IDLE_FRAME_COUNT;
         mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
         printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
         resetAccumulator();
         mProcessCount = 0;
+        mPeakNoise = 0.0f;
+        mNoiseSumSquared = 0.0;
+        mRootMeanSquareNoise = 0.0;
+        mPhase = 0.0f;
+        mMaxGlitchDelta = 0.0;
     }
 
 private:
 
     enum sine_state_t {
         STATE_IDLE,
+        STATE_MEASURE_NOISE,
         STATE_IMMUNE,
         STATE_WAITING_FOR_SIGNAL,
         STATE_WAITING_FOR_LOCK,
@@ -859,10 +923,16 @@
     };
 
     enum constants {
-        IMMUNE_FRAME_COUNT = 48 * 500,
-        PERIODS_NEEDED_FOR_LOCK = 8
+        // Arbitrary durations, assuming 48000 Hz
+        IDLE_FRAME_COUNT = 48 * 100,
+        NOISE_FRAME_COUNT = 48 * 600,
+        PERIODS_NEEDED_FOR_LOCK = 8,
+        PERIODS_IMMUNE = 2,
+        MIN_SNRATIO_DB = 65
     };
 
+    static constexpr float MIN_TOLERANCE = 0.01;
+
     int     mSinePeriod = 79;
     double  mPhaseIncrement = 0.0;
     double  mPhase = 0.0;
@@ -870,17 +940,23 @@
     double  mPreviousPhaseOffset = 0.0;
     double  mMagnitude = 0.0;
     double  mThreshold = 0.005;
-    double  mTolerance = 0.01;
+    double  mTolerance = MIN_TOLERANCE;
     int32_t mFramesAccumulated = 0;
     int32_t mProcessCount = 0;
     double  mSinAccumulator = 0.0;
     double  mCosAccumulator = 0.0;
+    float   mMaxGlitchDelta = 0.0f;
     int32_t mGlitchCount = 0;
     double  mPeakAmplitude = 0.0;
-    int     mDownCounter = IMMUNE_FRAME_COUNT;
+    int     mDownCounter = IDLE_FRAME_COUNT;
     int32_t mFrameCounter = 0;
     float   mOutputAmplitude = 0.75;
 
+    // measure background noise
+    float   mPeakNoise = 0.0f;
+    double  mNoiseSumSquared = 0.0;
+    double  mRootMeanSquareNoise = 0.0;
+
     PseudoRandom  mWhiteNoise;
     float   mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
 
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 84f9c22..124efd8 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -37,13 +37,12 @@
 
 // Tag for machine readable results as property = value pairs
 #define RESULT_TAG              "RESULT: "
-#define NUM_SECONDS             5
-#define PERIOD_MILLIS           1000
-#define NUM_INPUT_CHANNELS      1
 #define FILENAME_ALL            "/data/loopback_all.wav"
 #define FILENAME_ECHOS          "/data/loopback_echos.wav"
-#define APP_VERSION             "0.2.04"
+#define APP_VERSION             "0.3.00"
 
+constexpr int kLogPeriodMillis       = 1000;
+constexpr int kNumInputChannels      = 1;
 constexpr int kNumCallbacksToDrain   = 20;
 constexpr int kNumCallbacksToDiscard = 20;
 
@@ -336,7 +335,7 @@
 
     aaudio_result_t       result = AAUDIO_OK;
     aaudio_sharing_mode_t requestedInputSharingMode  = AAUDIO_SHARING_MODE_SHARED;
-    int                   requestedInputChannelCount = NUM_INPUT_CHANNELS;
+    int                   requestedInputChannelCount = kNumInputChannels;
     aaudio_format_t       requestedInputFormat       = AAUDIO_FORMAT_UNSPECIFIED;
     int32_t               requestedInputCapacity     = AAUDIO_UNSPECIFIED;
     aaudio_performance_mode_t inputPerformanceLevel  = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
@@ -459,7 +458,9 @@
     argParser.setPerformanceMode(inputPerformanceLevel);
     argParser.setChannelCount(requestedInputChannelCount);
     argParser.setSharingMode(requestedInputSharingMode);
-    // Warning! If you change input capacity then you may not get a FAST track on Legacy path.
+    if (requestedInputCapacity != AAUDIO_UNSPECIFIED) {
+        printf("Warning! If you set input capacity then maybe no FAST track on Legacy path!\n");
+    }
     argParser.setBufferCapacity(requestedInputCapacity);
 
     result = recorder.open(argParser);
@@ -510,15 +511,11 @@
     // Start OUTPUT first so INPUT does not overflow.
     result = player.start();
     if (result != AAUDIO_OK) {
-        printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
-               result, AAudio_convertResultToText(result));
         goto finish;
     }
 
     result = recorder.start();
     if (result != AAUDIO_OK) {
-        printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
-               result, AAudio_convertResultToText(result));
         goto finish;
     }
 
@@ -561,7 +558,7 @@
                    AAudioStream_getXRunCount(outputStream)
             );
         }
-        int32_t periodMillis = (timeMillis < 2000) ? PERIOD_MILLIS / 4 : PERIOD_MILLIS;
+        int32_t periodMillis = (timeMillis < 2000) ? kLogPeriodMillis / 4 : kLogPeriodMillis;
         usleep(periodMillis * 1000);
         timeMillis += periodMillis;
     }
@@ -586,12 +583,12 @@
     if (loopbackData.inputError == AAUDIO_OK) {
         if (testMode == TEST_SINE_MAGNITUDE) {
             printAudioGraph(loopbackData.audioRecording, 200);
+            // Print again so we don't have to scroll past waveform.
+            printf("OUTPUT Stream ----------------------------------------\n");
+            argParser.compareWithStream(outputStream);
+            printf("INPUT  Stream ----------------------------------------\n");
+            argParser.compareWithStream(inputStream);
         }
-        // Print again so we don't have to scroll past waveform.
-        printf("OUTPUT Stream ----------------------------------------\n");
-        argParser.compareWithStream(outputStream);
-        printf("INPUT  Stream ----------------------------------------\n");
-        argParser.compareWithStream(inputStream);
 
         loopbackData.loopbackProcessor->report();
     }
@@ -600,21 +597,21 @@
         int32_t framesRead = AAudioStream_getFramesRead(inputStream);
         int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
         printf("Callback Results ---------------------------------------- INPUT\n");
-        printf("  input overruns   = %d\n", AAudioStream_getXRunCount(inputStream));
+        printf("  input overruns   = %8d\n", AAudioStream_getXRunCount(inputStream));
         printf("  framesWritten    = %8d\n", framesWritten);
         printf("  framesRead       = %8d\n", framesRead);
         printf("  myFramesRead     = %8d\n", (int) loopbackData.framesReadTotal);
         printf("  written - read   = %8d\n", (int) (framesWritten - framesRead));
         printf("  insufficient #   = %8d\n", (int) loopbackData.insufficientReadCount);
         if (loopbackData.insufficientReadCount > 0) {
-            printf("  insufficient frames = %8d\n", (int) loopbackData.insufficientReadFrames);
+            printf("  insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
         }
     }
     {
         int32_t framesRead = AAudioStream_getFramesRead(outputStream);
         int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
         printf("Callback Results ---------------------------------------- OUTPUT\n");
-        printf("  output underruns = %d\n", AAudioStream_getXRunCount(outputStream));
+        printf("  output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
         printf("  myFramesWritten  = %8d\n", (int) loopbackData.framesWrittenTotal);
         printf("  framesWritten    = %8d\n", framesWritten);
         printf("  framesRead       = %8d\n", framesRead);
@@ -659,4 +656,3 @@
         return EXIT_SUCCESS;
     }
 }
-
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index 54b77ba..1645986 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -193,7 +193,7 @@
      aaudio_result_t start() {
         aaudio_result_t result = AAudioStream_requestStart(mStream);
         if (result != AAUDIO_OK) {
-            printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
+            printf("ERROR - AAudioStream_requestStart(output) returned %d %s\n",
                     result, AAudio_convertResultToText(result));
         }
         return result;
@@ -203,7 +203,7 @@
     aaudio_result_t stop() {
         aaudio_result_t result = AAudioStream_requestStop(mStream);
         if (result != AAUDIO_OK) {
-            printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
+            printf("ERROR - AAudioStream_requestStop(output) returned %d %s\n",
                    result, AAudio_convertResultToText(result));
         }
         int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -215,7 +215,7 @@
     aaudio_result_t pause() {
         aaudio_result_t result = AAudioStream_requestPause(mStream);
         if (result != AAUDIO_OK) {
-            printf("ERROR - AAudioStream_requestPause() returned %d %s\n",
+            printf("ERROR - AAudioStream_requestPause(output) returned %d %s\n",
                    result, AAudio_convertResultToText(result));
         }
         int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -223,11 +223,27 @@
         return result;
     }
 
+    aaudio_result_t waitUntilPaused() {
+        aaudio_result_t result = AAUDIO_OK;
+        aaudio_stream_state_t currentState = AAudioStream_getState(mStream);
+        aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
+        while (result == AAUDIO_OK && currentState == AAUDIO_STREAM_STATE_PAUSING) {
+            result = AAudioStream_waitForStateChange(mStream, inputState,
+                                                     &currentState, NANOS_PER_SECOND);
+            inputState = currentState;
+        }
+        if (result != AAUDIO_OK) {
+            return result;
+        }
+        return (currentState == AAUDIO_STREAM_STATE_PAUSED)
+               ? AAUDIO_OK : AAUDIO_ERROR_INVALID_STATE;
+    }
+
     // Flush the stream. AAudio will stop calling your callback function.
     aaudio_result_t flush() {
         aaudio_result_t result = AAudioStream_requestFlush(mStream);
         if (result != AAUDIO_OK) {
-            printf("ERROR - AAudioStream_requestFlush() returned %d %s\n",
+            printf("ERROR - AAudioStream_requestFlush(output) returned %d %s\n",
                    result, AAudio_convertResultToText(result));
         }
         return result;
diff --git a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
index 869fad0..246e2d7 100644
--- a/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
+++ b/media/libaaudio/examples/utils/AAudioSimpleRecorder.h
@@ -201,8 +201,10 @@
      aaudio_result_t start() {
         aaudio_result_t result = AAudioStream_requestStart(mStream);
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
+            fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d %s\n",
                     result, AAudio_convertResultToText(result));
+            fprintf(stderr, "        Did you remember to enter:   adb root    ????\n");
+
         }
         return result;
     }
@@ -211,8 +213,9 @@
     aaudio_result_t stop() {
         aaudio_result_t result = AAudioStream_requestStop(mStream);
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
+            fprintf(stderr, "ERROR - AAudioStream_requestStop(input) returned %d %s\n",
                     result, AAudio_convertResultToText(result));
+
         }
         return result;
     }
@@ -221,7 +224,7 @@
     aaudio_result_t pause() {
         aaudio_result_t result = AAudioStream_requestPause(mStream);
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
+            fprintf(stderr, "ERROR - AAudioStream_requestPause(input) returned %d %s\n",
                     result, AAudio_convertResultToText(result));
         }
         return result;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index e33e9f8..7a48153 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -30,6 +30,8 @@
 #include "AAudioSimplePlayer.h"
 #include "AAudioArgsParser.h"
 
+#define APP_VERSION  "0.1.5"
+
 /**
  * Open stream, play some sine waves, then close the stream.
  *
@@ -109,13 +111,13 @@
         startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
         for (int second = 0; second < durationSeconds; second++) {
             // Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
-            long ret = myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
+            myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
             int64_t millis =
                     (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
             result = myData.waker.get();
-            printf("wait() returns %ld, aaudio_result = %d, at %6d millis"
+            printf(" waker result = %d, at %6d millis"
                            ", second = %3d, framesWritten = %8d, underruns = %d\n",
-                   ret, result, (int) millis,
+                   result, (int) millis,
                    second,
                    (int) AAudioStream_getFramesWritten(player.getStream()),
                    (int) AAudioStream_getXRunCount(player.getStream()));
@@ -138,6 +140,10 @@
             if (result != AAUDIO_OK) {
                 goto error;
             }
+            result = player.waitUntilPaused();
+            if (result != AAUDIO_OK) {
+                goto error;
+            }
             result = player.flush();
         }
         if (result != AAUDIO_OK) {
@@ -219,7 +225,7 @@
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Play a sine sweep using an AAudio callback V0.1.4\n", argv[0]);
+    printf("%s - Play a sine sweep using an AAudio callback V%s\n", argv[0], APP_VERSION);
 
     for (int i = 1; i < argc; i++) {
         const char *arg = argv[i];
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 8dc31d0..e272f2a 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -146,14 +146,14 @@
 AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
                                                     int32_t channelCount)
 {
-    AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
-    streamBuilder->setSamplesPerFrame(channelCount);
+    AAudioStreamBuilder_setSamplesPerFrame(builder, channelCount);
 }
 
 AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
-                                                       int32_t channelCount)
+                                                       int32_t samplesPerFrame)
 {
-    AAudioStreamBuilder_setChannelCount(builder, channelCount);
+    AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+    streamBuilder->setSamplesPerFrame(samplesPerFrame);
 }
 
 AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder,
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index 3534149..35e2f3d 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -9,6 +9,7 @@
 
 LOCAL_CFLAGS+= -O2 -fvisibility=hidden
 LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -DBUILD_FLOAT -DSUPPORT_MC
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index e2ccfb7..00bc371 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -32,8 +32,6 @@
 #include <audio_effects/effect_visualizer.h>
 #include <audio_utils/primitives.h>
 
-#define BUILD_FLOAT
-
 #ifdef BUILD_FLOAT
 
 static constexpr audio_format_t kProcessFormat = AUDIO_FORMAT_PCM_FLOAT;
@@ -157,7 +155,12 @@
     if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
     if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
     if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
-    if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
+    const uint32_t channelCount = audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
+#ifdef SUPPORT_MC
+    if (channelCount < 1 || channelCount > FCC_8) return -EINVAL;
+#else
+    if (channelCount != FCC_2) return -EINVAL;
+#endif
     if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
             pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
     if (pConfig->inputCfg.format != kProcessFormat) return -EINVAL;
@@ -356,7 +359,7 @@
         // store the measurement
         pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample;
         pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared =
-                rmsSqAcc / (inBuffer->frameCount * pContext->mChannelCount);
+                rmsSqAcc / sampleLen;
         pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mIsValid = true;
         if (++pContext->mMeasurementBufferIdx >= pContext->mMeasurementWindowSizeInBuffers) {
             pContext->mMeasurementBufferIdx = 0;
@@ -375,12 +378,17 @@
 
 #ifdef BUILD_FLOAT
         float maxSample = 0.f;
-        for (size_t inIdx = 0; inIdx < sampleLen; ++inIdx) {
-            maxSample = fmax(maxSample, fabs(inBuffer->f32[inIdx]));
+        for (size_t inIdx = 0; inIdx < sampleLen; ) {
+            // we reconstruct the actual summed value to ensure proper normalization
+            // for multichannel outputs (channels > 2 may often be 0).
+            float smp = 0.f;
+            for (int i = 0; i < pContext->mChannelCount; ++i) {
+                smp += inBuffer->f32[inIdx++];
+            }
+            maxSample = fmax(maxSample, fabs(smp));
         }
         if (maxSample > 0.f) {
-            constexpr float halfish = 127.f / 256.f;
-            fscale = halfish / maxSample;
+            fscale = 127.f / maxSample;
             int exp; // unused
             const float significand = frexp(fscale, &exp);
             if (significand == 0.5f) {
@@ -412,7 +420,8 @@
     } else {
         assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED);
 #ifdef BUILD_FLOAT
-        fscale = 0.5f;  // default divide by 2 to account for sum of L + R.
+        // Note: if channels are uncorrelated, 1/sqrt(N) could be used at the risk of clipping.
+        fscale = 1.f / pContext->mChannelCount;  // account for summing all the channels together.
 #else
         shift = 9;
 #endif // BUILD_FLOAT
@@ -422,17 +431,19 @@
     uint32_t inIdx;
     uint8_t *buf = pContext->mCaptureBuf;
     for (inIdx = 0, captIdx = pContext->mCaptureIdx;
-         inIdx < inBuffer->frameCount;
-         inIdx++, captIdx++) {
-        if (captIdx >= CAPTURE_BUF_SIZE) {
-            // wrap around
-            captIdx = 0;
-        }
+         inIdx < sampleLen;
+         captIdx++) {
+        if (captIdx >= CAPTURE_BUF_SIZE) captIdx = 0; // wrap
+
 #ifdef BUILD_FLOAT
-        const float smp = (inBuffer->f32[2 * inIdx] + inBuffer->f32[2 * inIdx + 1]) * fscale;
-        buf[captIdx] = clamp8_from_float(smp);
+        float smp = 0.f;
+        for (uint32_t i = 0; i < pContext->mChannelCount; ++i) {
+            smp += inBuffer->f32[inIdx++];
+        }
+        buf[captIdx] = clamp8_from_float(smp * fscale);
 #else
-        const int32_t smp = (inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]) >> shift;
+        const int32_t smp = (inBuffer->s16[inIdx] + inBuffer->s16[inIdx + 1]) >> shift;
+        inIdx += FCC_2;  // integer supports stereo only.
         buf[captIdx] = ((uint8_t)smp)^0x80;
 #endif // BUILD_FLOAT
     }
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index 1e5fa07..d29e97d 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -71,6 +71,8 @@
     METADATA_KEY_COLOR_STANDARD  = 35,
     METADATA_KEY_COLOR_TRANSFER  = 36,
     METADATA_KEY_COLOR_RANGE     = 37,
+    METADATA_KEY_SAMPLERATE      = 38,
+    METADATA_KEY_BITS_PER_SAMPLE = 39,
 
     // Add more here...
 };
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index a37973b..09b19d7 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -29,6 +29,7 @@
         "libmediametrics",
         "libmediautils",
         "libmemunreachable",
+        "libnetd_client",
         "libpowermanager",
         "libstagefright",
         "libstagefright_foundation",
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 0f24329..03e0d12 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -114,30 +114,4 @@
     mMaxBandwidthHistoryItems = numHistoryItems;
 }
 
-// static
-void HTTPBase::RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag) {
-    int res = qtaguid_tagSocket(sockfd, kTag, uid);
-    if (res != 0) {
-        ALOGE("Failed tagging socket %d for uid %d (My UID=%d)", sockfd, uid, geteuid());
-    }
-}
-
-// static
-void HTTPBase::UnRegisterSocketUserTag(int sockfd) {
-    int res = qtaguid_untagSocket(sockfd);
-    if (res != 0) {
-        ALOGE("Failed untagging socket %d (My UID=%d)", sockfd, geteuid());
-    }
-}
-
-// static
-void HTTPBase::RegisterSocketUserMark(int sockfd, uid_t uid) {
-    setNetworkForUser(uid, sockfd);
-}
-
-// static
-void HTTPBase::UnRegisterSocketUserMark(int sockfd) {
-    RegisterSocketUserMark(sockfd, geteuid());
-}
-
 }  // namespace android
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index cdef3a5..f34d54c 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -545,6 +545,19 @@
                 if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
                     audioBitrate = -1;
                 }
+
+                int32_t bitsPerSample = -1;
+                int32_t sampleRate = -1;
+                trackMeta->findInt32(kKeyBitsPerSample, &bitsPerSample);
+                trackMeta->findInt32(kKeySampleRate, &sampleRate);
+                if (bitsPerSample >= 0) {
+                    sprintf(tmp, "%d", bitsPerSample);
+                    mMetaData.add(METADATA_KEY_BITS_PER_SAMPLE, String8(tmp));
+                }
+                if (sampleRate >= 0) {
+                    sprintf(tmp, "%d", sampleRate);
+                    mMetaData.add(METADATA_KEY_SAMPLERATE, String8(tmp));
+                }
             } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
                 hasVideo = true;
 
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 53258ad..96993e9 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -787,6 +787,11 @@
         msg->setInt32("channel-count", numChannels);
         msg->setInt32("sample-rate", sampleRate);
 
+        int32_t bitsPerSample;
+        if (meta->findInt32(kKeyBitsPerSample, &bitsPerSample)) {
+            msg->setInt32("bits-per-sample", bitsPerSample);
+        }
+
         int32_t channelMask;
         if (meta->findInt32(kKeyChannelMask, &channelMask)) {
             msg->setInt32("channel-mask", channelMask);
@@ -1526,6 +1531,10 @@
         if (msg->findInt32("sample-rate", &sampleRate)) {
             meta->setInt32(kKeySampleRate, sampleRate);
         }
+        int32_t bitsPerSample;
+        if (msg->findInt32("bits-per-sample", &bitsPerSample)) {
+            meta->setInt32(kKeyBitsPerSample, bitsPerSample);
+        }
         int32_t channelMask;
         if (msg->findInt32("channel-mask", &channelMask)) {
             meta->setInt32(kKeyChannelMask, channelMask);
diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
index a924197..8b20187 100644
--- a/media/libstagefright/include/HTTPBase.h
+++ b/media/libstagefright/include/HTTPBase.h
@@ -51,12 +51,6 @@
 
     virtual void setBandwidthHistorySize(size_t numHistoryItems);
 
-    static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag);
-    static void UnRegisterSocketUserTag(int sockfd);
-
-    static void RegisterSocketUserMark(int sockfd, uid_t uid);
-    static void UnRegisterSocketUserMark(int sockfd);
-
     virtual String8 toString() {
         return mName;
     }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index fb498d4..3debe34 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -552,8 +552,10 @@
                 hasStreamCA = true;
                 streamCA.mSystemID = br->getBits(16);
                 streamCA.mPID = br->getBits(16) & 0x1fff;
-                ES_info_length -= 4;
-                streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length - 4);
+                ES_info_length -= descriptor_length;
+                descriptor_length -= 4;
+                streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length);
+                br->skipBits(descriptor_length * 8);
             } else if (info.mType == STREAMTYPE_PES_PRIVATE_DATA &&
                        descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) {
                 unsigned descTagExt = br->getBits(8);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 5620cf8..33c1c18 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "ARTSPConnection.h"
+#include "NetworkUtils.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -59,8 +60,8 @@
     if (mSocket >= 0) {
         ALOGE("Connection is still open, closing the socket.");
         if (mUIDValid) {
-            HTTPBase::UnRegisterSocketUserTag(mSocket);
-            HTTPBase::UnRegisterSocketUserMark(mSocket);
+            NetworkUtils::UnRegisterSocketUserTag(mSocket);
+            NetworkUtils::UnRegisterSocketUserMark(mSocket);
         }
         close(mSocket);
         mSocket = -1;
@@ -214,8 +215,8 @@
 
     if (mState != DISCONNECTED) {
         if (mUIDValid) {
-            HTTPBase::UnRegisterSocketUserTag(mSocket);
-            HTTPBase::UnRegisterSocketUserMark(mSocket);
+            NetworkUtils::UnRegisterSocketUserTag(mSocket);
+            NetworkUtils::UnRegisterSocketUserMark(mSocket);
         }
         close(mSocket);
         mSocket = -1;
@@ -266,9 +267,9 @@
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
 
     if (mUIDValid) {
-        HTTPBase::RegisterSocketUserTag(mSocket, mUID,
+        NetworkUtils::RegisterSocketUserTag(mSocket, mUID,
                                         (uint32_t)*(uint32_t*) "RTSP");
-        HTTPBase::RegisterSocketUserMark(mSocket, mUID);
+        NetworkUtils::RegisterSocketUserMark(mSocket, mUID);
     }
 
     MakeSocketBlocking(mSocket, false);
@@ -297,8 +298,8 @@
         mState = DISCONNECTED;
 
         if (mUIDValid) {
-            HTTPBase::UnRegisterSocketUserTag(mSocket);
-            HTTPBase::UnRegisterSocketUserMark(mSocket);
+            NetworkUtils::UnRegisterSocketUserTag(mSocket);
+            NetworkUtils::UnRegisterSocketUserMark(mSocket);
         }
         close(mSocket);
         mSocket = -1;
@@ -315,8 +316,8 @@
 
 void ARTSPConnection::performDisconnect() {
     if (mUIDValid) {
-        HTTPBase::UnRegisterSocketUserTag(mSocket);
-        HTTPBase::UnRegisterSocketUserMark(mSocket);
+        NetworkUtils::UnRegisterSocketUserTag(mSocket);
+        NetworkUtils::UnRegisterSocketUserMark(mSocket);
     }
     close(mSocket);
     mSocket = -1;
@@ -389,8 +390,8 @@
 
         mState = DISCONNECTED;
         if (mUIDValid) {
-            HTTPBase::UnRegisterSocketUserTag(mSocket);
-            HTTPBase::UnRegisterSocketUserMark(mSocket);
+            NetworkUtils::UnRegisterSocketUserTag(mSocket);
+            NetworkUtils::UnRegisterSocketUserMark(mSocket);
         }
         close(mSocket);
         mSocket = -1;
diff --git a/media/libstagefright/rtsp/Android.bp b/media/libstagefright/rtsp/Android.bp
index debd07e..d1767d3 100644
--- a/media/libstagefright/rtsp/Android.bp
+++ b/media/libstagefright/rtsp/Android.bp
@@ -1,5 +1,5 @@
-cc_library_static {
-    name: "libstagefright_rtsp",
+cc_defaults {
+    name: "libstagefright_rtsp_defaults",
 
     srcs: [
         "AAMRAssembler.cpp",
@@ -52,6 +52,21 @@
     },
 }
 
+cc_library_static {
+    name: "libstagefright_rtsp",
+
+    srcs: ["NetworkUtils.cpp"],
+    header_libs: ["libnetd_client_headers"],
+    defaults: ["libstagefright_rtsp_defaults"],
+}
+
+cc_library_static {
+    name: "libstagefright_rtsp_player2",
+
+    srcs: ["NetworkUtilsForAppProc.cpp"],
+    defaults: ["libstagefright_rtsp_defaults"],
+}
+
 //###############################################################################
 
 cc_test {
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index d183516..5d993db 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -31,6 +31,7 @@
 #include "ARTPConnection.h"
 #include "ARTSPConnection.h"
 #include "ASessionDescription.h"
+#include "NetworkUtils.h"
 
 #include <ctype.h>
 #include <cutils/properties.h>
@@ -757,10 +758,10 @@
                         if (!track->mUsingInterleavedTCP) {
                             // Clear the tag
                             if (mUIDValid) {
-                                HTTPBase::UnRegisterSocketUserTag(track->mRTPSocket);
-                                HTTPBase::UnRegisterSocketUserMark(track->mRTPSocket);
-                                HTTPBase::UnRegisterSocketUserTag(track->mRTCPSocket);
-                                HTTPBase::UnRegisterSocketUserMark(track->mRTCPSocket);
+                                NetworkUtils::UnRegisterSocketUserTag(track->mRTPSocket);
+                                NetworkUtils::UnRegisterSocketUserMark(track->mRTPSocket);
+                                NetworkUtils::UnRegisterSocketUserTag(track->mRTCPSocket);
+                                NetworkUtils::UnRegisterSocketUserMark(track->mRTCPSocket);
                             }
 
                             close(track->mRTPSocket);
@@ -886,10 +887,10 @@
 
                         // Clear the tag
                         if (mUIDValid) {
-                            HTTPBase::UnRegisterSocketUserTag(info->mRTPSocket);
-                            HTTPBase::UnRegisterSocketUserMark(info->mRTPSocket);
-                            HTTPBase::UnRegisterSocketUserTag(info->mRTCPSocket);
-                            HTTPBase::UnRegisterSocketUserMark(info->mRTCPSocket);
+                            NetworkUtils::UnRegisterSocketUserTag(info->mRTPSocket);
+                            NetworkUtils::UnRegisterSocketUserMark(info->mRTPSocket);
+                            NetworkUtils::UnRegisterSocketUserTag(info->mRTCPSocket);
+                            NetworkUtils::UnRegisterSocketUserMark(info->mRTCPSocket);
                         }
 
                         close(info->mRTPSocket);
@@ -1665,12 +1666,12 @@
                     &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
 
             if (mUIDValid) {
-                HTTPBase::RegisterSocketUserTag(info->mRTPSocket, mUID,
-                                                (uint32_t)*(uint32_t*) "RTP_");
-                HTTPBase::RegisterSocketUserTag(info->mRTCPSocket, mUID,
-                                                (uint32_t)*(uint32_t*) "RTP_");
-                HTTPBase::RegisterSocketUserMark(info->mRTPSocket, mUID);
-                HTTPBase::RegisterSocketUserMark(info->mRTCPSocket, mUID);
+                NetworkUtils::RegisterSocketUserTag(info->mRTPSocket, mUID,
+                        (uint32_t)*(uint32_t*) "RTP_");
+                NetworkUtils::RegisterSocketUserTag(info->mRTCPSocket, mUID,
+                        (uint32_t)*(uint32_t*) "RTP_");
+                NetworkUtils::RegisterSocketUserMark(info->mRTPSocket, mUID);
+                NetworkUtils::RegisterSocketUserMark(info->mRTCPSocket, mUID);
             }
 
             request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
diff --git a/media/libstagefright/rtsp/NetworkUtils.cpp b/media/libstagefright/rtsp/NetworkUtils.cpp
new file mode 100644
index 0000000..cc36b78
--- /dev/null
+++ b/media/libstagefright/rtsp/NetworkUtils.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NetworkUtils"
+#include <utils/Log.h>
+
+#include "NetworkUtils.h"
+#include <cutils/qtaguid.h>
+#include <NetdClient.h>
+
+namespace android {
+
+// static
+void NetworkUtils::RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag) {
+    int res = qtaguid_tagSocket(sockfd, kTag, uid);
+    if (res != 0) {
+        ALOGE("Failed tagging socket %d for uid %d (My UID=%d)", sockfd, uid, geteuid());
+    }
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserTag(int sockfd) {
+    int res = qtaguid_untagSocket(sockfd);
+    if (res != 0) {
+        ALOGE("Failed untagging socket %d (My UID=%d)", sockfd, geteuid());
+    }
+}
+
+// static
+void NetworkUtils::RegisterSocketUserMark(int sockfd, uid_t uid) {
+    setNetworkForUser(uid, sockfd);
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserMark(int sockfd) {
+    RegisterSocketUserMark(sockfd, geteuid());
+}
+
+}  // namespace android
diff --git a/media/libstagefright/rtsp/NetworkUtils.h b/media/libstagefright/rtsp/NetworkUtils.h
new file mode 100644
index 0000000..e25ee46
--- /dev/null
+++ b/media/libstagefright/rtsp/NetworkUtils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NETWORK_UTILS_H_
+#define NETWORK_UTILS_H_
+
+namespace android {
+
+struct NetworkUtils {
+    static void RegisterSocketUserTag(int sockfd, uid_t uid, uint32_t kTag);
+    static void UnRegisterSocketUserTag(int sockfd);
+
+    static void RegisterSocketUserMark(int sockfd, uid_t uid);
+    static void UnRegisterSocketUserMark(int sockfd);
+};
+
+}  // namespace android
+
+#endif  // NETWORK_UTILS_H_
diff --git a/media/libstagefright/rtsp/NetworkUtilsForAppProc.cpp b/media/libstagefright/rtsp/NetworkUtilsForAppProc.cpp
new file mode 100644
index 0000000..662159c
--- /dev/null
+++ b/media/libstagefright/rtsp/NetworkUtilsForAppProc.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NetworkUtils"
+#include <utils/Log.h>
+
+#include "NetworkUtils.h"
+
+// NetworkUtils implementation for application process.
+namespace android {
+
+// static
+void NetworkUtils::RegisterSocketUserTag(int, uid_t, uint32_t) {
+    // No op. Framework already handles the data usage billing for applications.
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserTag(int) {
+    // No op.
+}
+
+// static
+void NetworkUtils::RegisterSocketUserMark(int, uid_t) {
+    // No op. Framework already handles the data usage billing for applications.
+}
+
+// static
+void NetworkUtils::UnRegisterSocketUserMark(int) {
+    // No op.
+}
+
+}  // namespace android
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index b95dcd0..6537ad1 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -276,6 +276,7 @@
 EXPORT const char* AMEDIAFORMAT_KEY_AUTHOR = "author";
 EXPORT const char* AMEDIAFORMAT_KEY_BITRATE_MODE = "bitrate-mode";
 EXPORT const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate";
+EXPORT const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE = "bits-per-sample";
 EXPORT const char* AMEDIAFORMAT_KEY_CAPTURE_RATE = "capture-rate";
 EXPORT const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER = "cdtracknum";
 EXPORT const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count";
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index b8047d9..3a3268c 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -181,6 +181,7 @@
 extern const char* AMEDIAFORMAT_KEY_ALBUMARTIST __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_ARTIST __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_AUTHOR __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_BITS_PER_SAMPLE __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_CDTRACKNUMBER __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_COLOR_RANGE __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_COLOR_STANDARD __INTRODUCED_IN(29);