Merge "Audioflinger: Dump "master mute" state in threads"
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index fc00a2d..d73f744 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -32,6 +32,10 @@
     if (mType == ACM_CHARACTERISTICS) {
         filterUnsupportedFeatures();
         filterStreamConfigurations();
+        filterDurations(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+        filterDurations(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS);
+        filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
+        filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
     }
     // TODO: filter request/result keys
 }
@@ -82,6 +86,72 @@
 
 
 void
+ACameraMetadata::filterDurations(uint32_t tag) {
+    const int STREAM_CONFIGURATION_SIZE = 4;
+    const int STREAM_FORMAT_OFFSET = 0;
+    const int STREAM_WIDTH_OFFSET = 1;
+    const int STREAM_HEIGHT_OFFSET = 2;
+    const int STREAM_DURATION_OFFSET = 3;
+    camera_metadata_entry entry = mData.find(tag);
+    if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT64) {
+        ALOGE("%s: malformed duration key %d! count %zu, type %d",
+                __FUNCTION__, tag, entry.count, entry.type);
+        return;
+    }
+    Vector<int64_t> filteredDurations;
+    filteredDurations.setCapacity(entry.count * 2);
+
+    for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+        int64_t format = entry.data.i64[i + STREAM_FORMAT_OFFSET];
+        int64_t width = entry.data.i64[i + STREAM_WIDTH_OFFSET];
+        int64_t height = entry.data.i64[i + STREAM_HEIGHT_OFFSET];
+        int64_t duration = entry.data.i32[i + STREAM_DURATION_OFFSET];
+
+        // Leave the unfiltered format in so apps depending on previous wrong
+        // filter behavior continue to work
+        filteredDurations.push_back(format);
+        filteredDurations.push_back(width);
+        filteredDurations.push_back(height);
+        filteredDurations.push_back(duration);
+
+        // Translate HAL formats to NDK format
+        switch (tag) {
+            case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:
+            case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:
+                if (format == HAL_PIXEL_FORMAT_BLOB) {
+                    format = AIMAGE_FORMAT_JPEG;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                }
+                break;
+            case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:
+            case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:
+                if (format == HAL_PIXEL_FORMAT_BLOB) {
+                    format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                } else if (format == HAL_PIXEL_FORMAT_Y16) {
+                    format = AIMAGE_FORMAT_DEPTH16;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                }
+                break;
+            default:
+                // Should not reach here
+                ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag);
+        }
+    }
+
+    mData.update(tag, filteredDurations);
+}
+
+void
 ACameraMetadata::filterStreamConfigurations() {
     const int STREAM_CONFIGURATION_SIZE = 4;
     const int STREAM_FORMAT_OFFSET = 0;
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index 0fd7efa..e76b80c 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -58,13 +58,16 @@
     camera_status_t getTags(/*out*/int32_t* numTags,
                             /*out*/const uint32_t** tags) const;
 
+    const CameraMetadata& getInternalData() const;
+
+  private:
+
     bool isNdkSupportedCapability(const int32_t capability);
     static inline bool isVendorTag(const uint32_t tag);
     static bool isCaptureRequestTag(const uint32_t tag);
     void filterUnsupportedFeatures(); // Hide features not yet supported by NDK
     void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats
-
-    const CameraMetadata& getInternalData() const;
+    void filterDurations(uint32_t tag); // translate hal format to NDK formats
 
     template<typename INTERNAL_T, typename NDK_T>
     camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) {
@@ -96,7 +99,6 @@
         }
     }
 
-  private:
     // guard access of public APIs: get/update/getTags
     mutable Mutex    mLock;
     CameraMetadata   mData;
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index c559ff9..1b8e8d9 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -235,10 +235,10 @@
 
     // Set the region of the layer stack we're interested in, which in our
     // case is "all of it".
-    Rect layerStackRect(mainDpyInfo.w, mainDpyInfo.h);
+    Rect layerStackRect(mainDpyInfo.viewportW, mainDpyInfo.viewportH);
 
     // We need to preserve the aspect ratio of the display.
-    float displayAspect = (float) mainDpyInfo.h / (float) mainDpyInfo.w;
+    float displayAspect = (float) mainDpyInfo.viewportH / (float) mainDpyInfo.viewportW;
 
 
     // Set the way we map the output onto the display surface (which will
@@ -315,22 +315,6 @@
 }
 
 /*
- * Set the main display width and height to the actual width and height
- */
-static status_t getActualDisplaySize(const sp<IBinder>& mainDpy, DisplayInfo* mainDpyInfo) {
-    Rect viewport;
-    status_t err = SurfaceComposerClient::getDisplayViewport(mainDpy, &viewport);
-    if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to get display viewport\n");
-        return err;
-    }
-    mainDpyInfo->w = viewport.width();
-    mainDpyInfo->h = viewport.height();
-
-    return NO_ERROR;
-}
-
-/*
  * Runs the MediaCodec encoder, sending the output to the MediaMuxer.  The
  * input frames are coming from the virtual display as fast as SurfaceFlinger
  * wants to send them.
@@ -400,22 +384,14 @@
                     // useful stuff is hard to get at without a Dalvik VM.
                     err = SurfaceComposerClient::getDisplayInfo(mainDpy,
                             &mainDpyInfo);
-                    if (err == NO_ERROR) {
-                        err = getActualDisplaySize(mainDpy, &mainDpyInfo);
-                        if (err != NO_ERROR) {
-                            fprintf(stderr, "ERROR: unable to set actual display size\n");
-                            return err;
-                        }
-
-                        if (orientation != mainDpyInfo.orientation) {
-                            ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
-                            SurfaceComposerClient::Transaction t;
-                            setDisplayProjection(t, virtualDpy, mainDpyInfo);
-                            t.apply();
-                            orientation = mainDpyInfo.orientation;
-                        }
-                    } else {
+                    if (err != NO_ERROR) {
                         ALOGW("getDisplayInfo(main) failed: %d", err);
+                    } else if (orientation != mainDpyInfo.orientation) {
+                        ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
+                        SurfaceComposerClient::Transaction t;
+                        setDisplayProjection(t, virtualDpy, mainDpyInfo);
+                        t.apply();
+                        orientation = mainDpyInfo.orientation;
                     }
                 }
 
@@ -589,25 +565,19 @@
         return err;
     }
 
-    err = getActualDisplaySize(mainDpy, &mainDpyInfo);
-    if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to set actual display size\n");
-        return err;
-    }
-
     if (gVerbose) {
         printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
-                mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
+                mainDpyInfo.viewportW, mainDpyInfo.viewportH, mainDpyInfo.fps,
                 mainDpyInfo.orientation);
         fflush(stdout);
     }
 
     // Encoder can't take odd number as config
     if (gVideoWidth == 0) {
-        gVideoWidth = floorToEven(mainDpyInfo.w);
+        gVideoWidth = floorToEven(mainDpyInfo.viewportW);
     }
     if (gVideoHeight == 0) {
-        gVideoHeight = floorToEven(mainDpyInfo.h);
+        gVideoHeight = floorToEven(mainDpyInfo.viewportH);
     }
 
     // Configure and start the encoder.
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index cf08610..ce9dc38 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -318,7 +318,6 @@
                     for (const auto &instance : registered) {
                         auto factory = drm::V1_0::IDrmFactory::getService(instance);
                         if (factory != NULL) {
-                            ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str());
                             factories.push_back(factory);
                         }
                     }
@@ -329,7 +328,6 @@
                     for (const auto &instance : registered) {
                         auto factory = drm::V1_1::IDrmFactory::getService(instance);
                         if (factory != NULL) {
-                            ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str());
                             factories.push_back(factory);
                         }
                     }
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 73ed8c3..1558e8b 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -118,9 +118,9 @@
 
 status_t ClearKeyCasPlugin::closeSession(const CasSessionId &sessionId) {
     ALOGV("closeSession: sessionId=%s", sessionIdToString(sessionId).string());
-    sp<ClearKeyCasSession> session =
+    std::shared_ptr<ClearKeyCasSession> session =
             ClearKeySessionLibrary::get()->findSession(sessionId);
-    if (session == NULL) {
+    if (session.get() == nullptr) {
         return ERROR_CAS_SESSION_NOT_OPENED;
     }
 
@@ -132,9 +132,9 @@
         const CasSessionId &sessionId, const CasData & /*data*/) {
     ALOGV("setSessionPrivateData: sessionId=%s",
             sessionIdToString(sessionId).string());
-    sp<ClearKeyCasSession> session =
+    std::shared_ptr<ClearKeyCasSession> session =
             ClearKeySessionLibrary::get()->findSession(sessionId);
-    if (session == NULL) {
+    if (session.get() == nullptr) {
         return ERROR_CAS_SESSION_NOT_OPENED;
     }
     return OK;
@@ -143,9 +143,9 @@
 status_t ClearKeyCasPlugin::processEcm(
         const CasSessionId &sessionId, const CasEcm& ecm) {
     ALOGV("processEcm: sessionId=%s", sessionIdToString(sessionId).string());
-    sp<ClearKeyCasSession> session =
+    std::shared_ptr<ClearKeyCasSession> session =
             ClearKeySessionLibrary::get()->findSession(sessionId);
-    if (session == NULL) {
+    if (session.get() == nullptr) {
         return ERROR_CAS_SESSION_NOT_OPENED;
     }
 
@@ -418,15 +418,15 @@
         const CasSessionId &sessionId) {
     ALOGV("setMediaCasSession: sessionId=%s", sessionIdToString(sessionId).string());
 
-    sp<ClearKeyCasSession> session =
+    std::shared_ptr<ClearKeyCasSession> session =
             ClearKeySessionLibrary::get()->findSession(sessionId);
 
-    if (session == NULL) {
+    if (session.get() == nullptr) {
         ALOGE("ClearKeyDescramblerPlugin: session not found");
         return ERROR_CAS_SESSION_NOT_OPENED;
     }
 
-    mCASSession = session;
+    std::atomic_store(&mCASSession, session);
     return OK;
 }
 
@@ -447,12 +447,14 @@
           subSamplesToString(subSamples, numSubSamples).string(),
           srcPtr, dstPtr, srcOffset, dstOffset);
 
-    if (mCASSession == NULL) {
+    std::shared_ptr<ClearKeyCasSession> session = std::atomic_load(&mCASSession);
+
+    if (session.get() == nullptr) {
         ALOGE("Uninitialized CAS session!");
         return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
     }
 
-    return mCASSession->decrypt(
+    return session->decrypt(
             secure, scramblingControl,
             numSubSamples, subSamples,
             (uint8_t*)srcPtr + srcOffset,
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
index 42cfb8f..389e172 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
@@ -120,7 +120,7 @@
             AString *errorDetailMsg) override;
 
 private:
-    sp<ClearKeyCasSession> mCASSession;
+    std::shared_ptr<ClearKeyCasSession> mCASSession;
 
     String8 subSamplesToString(
             SubSample const *subSamples,
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
index 4b4051d..3bb1176 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
@@ -56,7 +56,7 @@
 
     Mutex::Autolock lock(mSessionsLock);
 
-    sp<ClearKeyCasSession> session = new ClearKeyCasSession(plugin);
+    std::shared_ptr<ClearKeyCasSession> session(new ClearKeyCasSession(plugin));
 
     uint8_t *byteArray = (uint8_t *) &mNextSessionId;
     sessionId->push_back(byteArray[3]);
@@ -69,7 +69,7 @@
     return OK;
 }
 
-sp<ClearKeyCasSession> ClearKeySessionLibrary::findSession(
+std::shared_ptr<ClearKeyCasSession> ClearKeySessionLibrary::findSession(
         const CasSessionId& sessionId) {
     Mutex::Autolock lock(mSessionsLock);
 
@@ -88,7 +88,7 @@
         return;
     }
 
-    sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
+    std::shared_ptr<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
     mIDToSessionMap.removeItemsAt(index);
 }
 
@@ -96,7 +96,7 @@
     Mutex::Autolock lock(mSessionsLock);
 
     for (ssize_t index = (ssize_t)mIDToSessionMap.size() - 1; index >= 0; index--) {
-        sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
+        std::shared_ptr<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
         if (session->getPlugin() == plugin) {
             mIDToSessionMap.removeItemsAt(index);
         }
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
index 01f5f47..a537e63 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
@@ -32,6 +32,10 @@
 
 class ClearKeyCasSession : public RefBase {
 public:
+    explicit ClearKeyCasSession(CasPlugin *plugin);
+
+    virtual ~ClearKeyCasSession();
+
     ssize_t decrypt(
             bool secure,
             DescramblerPlugin::ScramblingControl scramblingControl,
@@ -58,8 +62,6 @@
 
     friend class ClearKeySessionLibrary;
 
-    explicit ClearKeyCasSession(CasPlugin *plugin);
-    virtual ~ClearKeyCasSession();
     CasPlugin* getPlugin() const { return mPlugin; }
     status_t decryptPayload(
             const AES_KEY& key, size_t length, size_t offset, char* buffer) const;
@@ -73,7 +75,7 @@
 
     status_t addSession(CasPlugin *plugin, CasSessionId *sessionId);
 
-    sp<ClearKeyCasSession> findSession(const CasSessionId& sessionId);
+    std::shared_ptr<ClearKeyCasSession> findSession(const CasSessionId& sessionId);
 
     void destroySession(const CasSessionId& sessionId);
 
@@ -85,7 +87,7 @@
 
     Mutex mSessionsLock;
     uint32_t mNextSessionId;
-    KeyedVector<CasSessionId, sp<ClearKeyCasSession>> mIDToSessionMap;
+    KeyedVector<CasSessionId, std::shared_ptr<ClearKeyCasSession>> mIDToSessionMap;
 
     ClearKeySessionLibrary();
     DISALLOW_EVIL_CONSTRUCTORS(ClearKeySessionLibrary);
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d51e29d..3b61085 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -245,11 +245,29 @@
 
     setPlayPolicy();
     std::vector<uint8_t> keySetId;
+    keySetId.clear();
+
     Status status = session->provideKeyResponse(response);
     if (status == Status::OK) {
-        // This is for testing AMediaDrm_setOnEventListener only.
-        sendEvent(EventType::VENDOR_DEFINED, 0, scope);
-        keySetId.clear();
+        // Test calling AMediaDrm listeners.
+        sendEvent(EventType::VENDOR_DEFINED, toVector(scope), toVector(scope));
+
+        sendExpirationUpdate(toVector(scope), 100);
+
+        std::vector<KeyStatus> keysStatus;
+        KeyStatus keyStatus;
+
+        std::vector<uint8_t> keyId1 = { 0xA, 0xB, 0xC };
+        keyStatus.keyId = keyId1;
+        keyStatus.type = V1_0::KeyStatusType::USABLE;
+        keysStatus.push_back(keyStatus);
+
+        std::vector<uint8_t> keyId2 = { 0xD, 0xE, 0xF };
+        keyStatus.keyId = keyId2;
+        keyStatus.type = V1_0::KeyStatusType::EXPIRED;
+        keysStatus.push_back(keyStatus);
+
+        sendKeysChange(toVector(scope), keysStatus, true);
     }
 
     installSecureStop(scope);
diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
new file mode 100644
index 0000000..c1b62f8
--- /dev/null
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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_TAG "BufferPoolConnection"
+
+#include "Accessor.h"
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+void ConnectionDeathRecipient::add(
+        int64_t connectionId,
+        const sp<Accessor> &accessor) {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mAccessors.find(connectionId) == mAccessors.end()) {
+        mAccessors.insert(std::make_pair(connectionId, accessor));
+    }
+}
+
+void ConnectionDeathRecipient::remove(int64_t connectionId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mAccessors.erase(connectionId);
+    auto it = mConnectionToCookie.find(connectionId);
+    if (it != mConnectionToCookie.end()) {
+        uint64_t cookie = it->second;
+        mConnectionToCookie.erase(it);
+        auto cit = mCookieToConnections.find(cookie);
+        if (cit != mCookieToConnections.end()) {
+            cit->second.erase(connectionId);
+            if (cit->second.size() == 0) {
+                mCookieToConnections.erase(cit);
+            }
+        }
+    }
+}
+
+void ConnectionDeathRecipient::addCookieToConnection(
+        uint64_t cookie,
+        int64_t connectionId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mAccessors.find(connectionId) == mAccessors.end()) {
+        return;
+    }
+    mConnectionToCookie.insert(std::make_pair(connectionId, cookie));
+    auto it = mCookieToConnections.find(cookie);
+    if (it != mCookieToConnections.end()) {
+        it->second.insert(connectionId);
+    } else {
+        mCookieToConnections.insert(std::make_pair(
+                cookie, std::set<int64_t>{connectionId}));
+    }
+}
+
+void ConnectionDeathRecipient::serviceDied(
+        uint64_t cookie,
+        const wp<::android::hidl::base::V1_0::IBase>& /* who */
+        ) {
+    std::map<int64_t, const wp<Accessor>> connectionsToClose;
+    {
+        std::lock_guard<std::mutex> lock(mLock);
+
+        auto it = mCookieToConnections.find(cookie);
+        if (it != mCookieToConnections.end()) {
+            for (auto conIt = it->second.begin(); conIt != it->second.end(); ++conIt) {
+                auto accessorIt = mAccessors.find(*conIt);
+                if (accessorIt != mAccessors.end()) {
+                    connectionsToClose.insert(std::make_pair(*conIt, accessorIt->second));
+                    mAccessors.erase(accessorIt);
+                }
+                mConnectionToCookie.erase(*conIt);
+            }
+            mCookieToConnections.erase(it);
+        }
+    }
+
+    if (connectionsToClose.size() > 0) {
+        sp<Accessor> accessor;
+        for (auto it = connectionsToClose.begin(); it != connectionsToClose.end(); ++it) {
+            accessor = it->second.promote();
+
+            if (accessor) {
+                accessor->close(it->first);
+                ALOGD("connection %lld closed on death", (long long)it->first);
+            }
+        }
+    }
+}
+
+namespace {
+static sp<ConnectionDeathRecipient> sConnectionDeathRecipient =
+        new ConnectionDeathRecipient();
+}
+
+sp<ConnectionDeathRecipient> Accessor::getConnectionDeathRecipient() {
+    return sConnectionDeathRecipient;
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
+Return<void> Accessor::connect(connect_cb _hidl_cb) {
+    sp<Connection> connection;
+    ConnectionId connectionId;
+    const StatusDescriptor* fmqDesc;
+
+    ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
+    if (status == ResultStatus::OK) {
+        _hidl_cb(status, connection, connectionId, *fmqDesc,
+                 android::hardware::MQDescriptorSync<BufferInvalidationMessage>(
+                         std::vector<android::hardware::GrantorDescriptor>(),
+                         nullptr /* nhandle */, 0 /* size */));
+    } else {
+        _hidl_cb(status, nullptr, -1LL,
+                 android::hardware::MQDescriptorSync<BufferStatusMessage>(
+                         std::vector<android::hardware::GrantorDescriptor>(),
+                         nullptr /* nhandle */, 0 /* size */),
+                 android::hardware::MQDescriptorSync<BufferInvalidationMessage>(
+                         std::vector<android::hardware::GrantorDescriptor>(),
+                         nullptr /* nhandle */, 0 /* size */));
+    }
+    return Void();
+}
+
+Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
+    : mImpl(new Impl(allocator)) {}
+
+Accessor::~Accessor() {
+}
+
+bool Accessor::isValid() {
+    return (bool)mImpl;
+}
+
+ResultStatus Accessor::allocate(
+        ConnectionId connectionId,
+        const std::vector<uint8_t> &params,
+        BufferId *bufferId, const native_handle_t** handle) {
+    if (mImpl) {
+        return mImpl->allocate(connectionId, params, bufferId, handle);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::fetch(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, const native_handle_t** handle) {
+    if (mImpl) {
+        return mImpl->fetch(connectionId, transactionId, bufferId, handle);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::connect(
+        sp<Connection> *connection, ConnectionId *pConnectionId,
+        const StatusDescriptor** fmqDescPtr, bool local) {
+    if (mImpl) {
+        ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+        if (!local && status == ResultStatus::OK) {
+            sp<Accessor> accessor(this);
+            sConnectionDeathRecipient->add(*pConnectionId, accessor);
+        }
+        return status;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::close(ConnectionId connectionId) {
+    if (mImpl) {
+        ResultStatus status = mImpl->close(connectionId);
+        sConnectionDeathRecipient->remove(connectionId);
+        return status;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::cleanUp(bool clearCache) {
+    if (mImpl) {
+        mImpl->cleanUp(clearCache);
+    }
+}
+
+//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
+//    return new Accessor();
+//}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
new file mode 100644
index 0000000..fa2cb1b
--- /dev/null
+++ b/media/bufferpool/2.0/Accessor.h
@@ -0,0 +1,188 @@
+/*
+ * 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_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/2.0/IAccessor.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferStatus.h"
+
+#include <set>
+
+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 Accessor;
+struct Connection;
+
+/**
+ * Receives death notifications from remote connections.
+ * On death notifications, the connections are closed and used resources
+ * are released.
+ */
+struct ConnectionDeathRecipient : public hardware::hidl_death_recipient {
+    /**
+     * Registers a newly connected connection from remote processes.
+     */
+    void add(int64_t connectionId, const sp<Accessor> &accessor);
+
+    /**
+     * Removes a connection.
+     */
+    void remove(int64_t connectionId);
+
+    void addCookieToConnection(uint64_t cookie, int64_t connectionId);
+
+    virtual void serviceDied(
+            uint64_t /* cookie */,
+            const wp<::android::hidl::base::V1_0::IBase>& /* who */
+            ) override;
+
+private:
+    std::mutex mLock;
+    std::map<uint64_t, std::set<int64_t>>  mCookieToConnections;
+    std::map<int64_t, uint64_t> mConnectionToCookie;
+    std::map<int64_t, const wp<Accessor>> mAccessors;
+};
+
+/**
+ * A buffer pool accessor which enables a buffer pool to communicate with buffer
+ * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
+ */
+struct Accessor : public IAccessor {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
+    Return<void> connect(connect_cb _hidl_cb) override;
+
+    /**
+     * Creates a buffer pool accessor which uses the specified allocator.
+     *
+     * @param allocator buffer allocator.
+     */
+    explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+    /** Destructs a buffer pool accessor. */
+    ~Accessor();
+
+    /** Returns whether the accessor is valid. */
+    bool isValid();
+
+    /** Allocates a buffer from a buffer pool.
+     *
+     * @param connectionId  the connection id of the client.
+     * @param params        the allocation parameters.
+     * @param bufferId      the id of the allocated buffer.
+     * @param handle        the native handle of the allocated buffer.
+     *
+     * @return OK when a buffer is successfully allocated.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus allocate(
+            ConnectionId connectionId,
+            const std::vector<uint8_t>& params,
+            BufferId *bufferId,
+            const native_handle_t** handle);
+
+    /**
+     * Fetches a buffer for the specified transaction.
+     *
+     * @param connectionId  the id of receiving connection(client).
+     * @param transactionId the id of the transfer transaction.
+     * @param bufferId      the id of the buffer to be fetched.
+     * @param handle        the native handle of the fetched buffer.
+     *
+     * @return OK when a buffer is successfully fetched.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus fetch(
+            ConnectionId connectionId,
+            TransactionId transactionId,
+            BufferId bufferId,
+            const native_handle_t** handle);
+
+    /**
+     * Makes a connection to the buffer pool. The buffer pool client uses the
+     * 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 local         true when a connection request comes from local process,
+     *                      false otherwise.
+     *
+     * @return OK when a connection is successfully made.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus connect(
+            sp<Connection> *connection, ConnectionId *pConnectionId,
+            const StatusDescriptor** fmqDescPtr, bool local);
+
+    /**
+     * Closes the specified connection to the client.
+     *
+     * @param connectionId  the id of the connection.
+     *
+     * @return OK when the connection is closed.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus close(ConnectionId connectionId);
+
+    /**
+     * Processes pending buffer status messages and perfoms periodic cache
+     * cleaning.
+     *
+     * @param clearCache    if clearCache is true, it frees all buffers waiting
+     *                      to be recycled.
+     */
+    void cleanUp(bool clearCache);
+
+    /**
+     * Gets a hidl_death_recipient for remote connection death.
+     */
+    static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> mImpl;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
new file mode 100644
index 0000000..ef62d03
--- /dev/null
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -0,0 +1,543 @@
+/*
+ * 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_TAG "BufferPoolAccessor"
+//#define LOG_NDEBUG 0
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+namespace {
+    static constexpr int64_t kCleanUpDurationUs = 500000; // TODO tune 0.5 sec
+    static constexpr int64_t kLogDurationUs = 5000000; // 5 secs
+
+    static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
+    static constexpr size_t kMinBufferCountForEviction = 40;
+}
+
+// Buffer structure in bufferpool process
+struct InternalBuffer {
+    BufferId mId;
+    size_t mOwnerCount;
+    size_t mTransactionCount;
+    const std::shared_ptr<BufferPoolAllocation> mAllocation;
+    const size_t mAllocSize;
+    const std::vector<uint8_t> mConfig;
+
+    InternalBuffer(
+            BufferId id,
+            const std::shared_ptr<BufferPoolAllocation> &alloc,
+            const size_t allocSize,
+            const std::vector<uint8_t> &allocConfig)
+            : mId(id), mOwnerCount(0), mTransactionCount(0),
+            mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
+
+    const native_handle_t *handle() {
+        return mAllocation->handle();
+    }
+};
+
+struct TransactionStatus {
+    TransactionId mId;
+    BufferId mBufferId;
+    ConnectionId mSender;
+    ConnectionId mReceiver;
+    BufferStatus mStatus;
+    int64_t mTimestampUs;
+    bool mSenderValidated;
+
+    TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
+        mId = message.transactionId;
+        mBufferId = message.bufferId;
+        mStatus = message.newStatus;
+        mTimestampUs = timestampUs;
+        if (mStatus == BufferStatus::TRANSFER_TO) {
+            mSender = message.connectionId;
+            mReceiver = message.targetConnectionId;
+            mSenderValidated = true;
+        } else {
+            mSender = -1LL;
+            mReceiver = message.connectionId;
+            mSenderValidated = false;
+        }
+    }
+};
+
+// Helper template methods for handling map of set.
+template<class T, class U>
+bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+    auto iter = mapOfSet->find(key);
+    if (iter == mapOfSet->end()) {
+        std::set<U> valueSet{value};
+        mapOfSet->insert(std::make_pair(key, valueSet));
+        return true;
+    } else if (iter->second.find(value)  == iter->second.end()) {
+        iter->second.insert(value);
+        return true;
+    }
+    return false;
+}
+
+template<class T, class U>
+bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+    bool ret = false;
+    auto iter = mapOfSet->find(key);
+    if (iter != mapOfSet->end()) {
+        if (iter->second.erase(value) > 0) {
+            ret = true;
+        }
+        if (iter->second.size() == 0) {
+            mapOfSet->erase(iter);
+        }
+    }
+    return ret;
+}
+
+template<class T, class U>
+bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+    auto iter = mapOfSet->find(key);
+    if (iter != mapOfSet->end()) {
+        auto setIter = iter->second.find(value);
+        return setIter != iter->second.end();
+    }
+    return false;
+}
+
+int32_t Accessor::Impl::sPid = getpid();
+uint32_t Accessor::Impl::sSeqId = time(nullptr);
+
+Accessor::Impl::Impl(
+        const std::shared_ptr<BufferPoolAllocator> &allocator)
+        : mAllocator(allocator) {}
+
+Accessor::Impl::~Impl() {
+}
+
+ResultStatus Accessor::Impl::connect(
+        const sp<Accessor> &accessor, sp<Connection> *connection,
+        ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr) {
+    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);
+            if (status == ResultStatus::OK) {
+                newConnection->initialize(accessor, id);
+                *connection = newConnection;
+                *pConnectionId = id;
+                ++sSeqId;
+            }
+        }
+        mBufferPool.processStatusMessages();
+        mBufferPool.cleanUp();
+    }
+    return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.handleClose(connectionId);
+    mBufferPool.mObserver.close(connectionId);
+    // Since close# will be called after all works are finished, it is OK to
+    // evict unused buffers.
+    mBufferPool.cleanUp(true);
+    return ResultStatus::OK;
+}
+
+ResultStatus Accessor::Impl::allocate(
+        ConnectionId connectionId, const std::vector<uint8_t>& params,
+        BufferId *bufferId, const native_handle_t** handle) {
+    std::unique_lock<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    ResultStatus status = ResultStatus::OK;
+    if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
+        lock.unlock();
+        std::shared_ptr<BufferPoolAllocation> alloc;
+        size_t allocSize;
+        status = mAllocator->allocate(params, &alloc, &allocSize);
+        lock.lock();
+        if (status == ResultStatus::OK) {
+            status = mBufferPool.addNewBuffer(alloc, allocSize, params, bufferId, handle);
+        }
+        ALOGV("create a buffer %d : %u %p",
+              status == ResultStatus::OK, *bufferId, *handle);
+    }
+    if (status == ResultStatus::OK) {
+        // TODO: handle ownBuffer failure
+        mBufferPool.handleOwnBuffer(connectionId, *bufferId);
+    }
+    mBufferPool.cleanUp();
+    return status;
+}
+
+ResultStatus Accessor::Impl::fetch(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, const native_handle_t** handle) {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    auto found = mBufferPool.mTransactions.find(transactionId);
+    if (found != mBufferPool.mTransactions.end() &&
+            contains(&mBufferPool.mPendingTransactions,
+                     connectionId, transactionId)) {
+        if (found->second->mSenderValidated &&
+                found->second->mStatus == BufferStatus::TRANSFER_FROM &&
+                found->second->mBufferId == bufferId) {
+            found->second->mStatus = BufferStatus::TRANSFER_FETCH;
+            auto bufferIt = mBufferPool.mBuffers.find(bufferId);
+            if (bufferIt != mBufferPool.mBuffers.end()) {
+                mBufferPool.mStats.onBufferFetched();
+                *handle = bufferIt->second->handle();
+                return ResultStatus::OK;
+            }
+        }
+    }
+    mBufferPool.cleanUp();
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::Impl::cleanUp(bool clearCache) {
+    // transaction timeout, buffer cacheing TTL handling
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.cleanUp(clearCache);
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+    : mTimestampUs(getTimestampNow()),
+      mLastCleanUpUs(mTimestampUs),
+      mLastLogUs(mTimestampUs),
+      mSeq(0) {}
+
+
+// Statistics helper
+template<typename T, typename S>
+int percentage(T base, S total) {
+    return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
+}
+
+Accessor::Impl::Impl::BufferPool::~BufferPool() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    ALOGD("Destruction - bufferpool %p "
+          "cached: %zu/%zuM, %zu/%d%% in use; "
+          "allocs: %zu, %d%% recycled; "
+          "transfers: %zu, %d%% unfetced",
+          this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
+          mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
+          mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
+          mStats.mTotalTransfers,
+          percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
+}
+
+bool Accessor::Impl::BufferPool::handleOwnBuffer(
+        ConnectionId connectionId, BufferId bufferId) {
+
+    bool added = insert(&mUsingBuffers, connectionId, bufferId);
+    if (added) {
+        auto iter = mBuffers.find(bufferId);
+        iter->second->mOwnerCount++;
+    }
+    insert(&mUsingConnections, bufferId, connectionId);
+    return added;
+}
+
+bool Accessor::Impl::BufferPool::handleReleaseBuffer(
+        ConnectionId connectionId, BufferId bufferId) {
+    bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
+    if (deleted) {
+        auto iter = mBuffers.find(bufferId);
+        iter->second->mOwnerCount--;
+        if (iter->second->mOwnerCount == 0 &&
+                iter->second->mTransactionCount == 0) {
+            mStats.onBufferUnused(iter->second->mAllocSize);
+            mFreeBuffers.insert(bufferId);
+        }
+    }
+    erase(&mUsingConnections, bufferId, connectionId);
+    ALOGV("release buffer %u : %d", bufferId, deleted);
+    return deleted;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
+    auto completed = mCompletedTransactions.find(
+            message.transactionId);
+    if (completed != mCompletedTransactions.end()) {
+        // already completed
+        mCompletedTransactions.erase(completed);
+        return true;
+    }
+    // the buffer should exist and be owned.
+    auto bufferIter = mBuffers.find(message.bufferId);
+    if (bufferIter == mBuffers.end() ||
+            !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
+        return false;
+    }
+    auto found = mTransactions.find(message.transactionId);
+    if (found != mTransactions.end()) {
+        // transfer_from was received earlier.
+        found->second->mSender = message.connectionId;
+        found->second->mSenderValidated = true;
+        return true;
+    }
+    // TODO: verify there is target connection Id
+    mStats.onBufferSent();
+    mTransactions.insert(std::make_pair(
+            message.transactionId,
+            std::make_unique<TransactionStatus>(message, mTimestampUs)));
+    insert(&mPendingTransactions, message.targetConnectionId,
+           message.transactionId);
+    bufferIter->second->mTransactionCount++;
+    return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
+    auto found = mTransactions.find(message.transactionId);
+    if (found == mTransactions.end()) {
+        // TODO: is it feasible to check ownership here?
+        mStats.onBufferSent();
+        mTransactions.insert(std::make_pair(
+                message.transactionId,
+                std::make_unique<TransactionStatus>(message, mTimestampUs)));
+        insert(&mPendingTransactions, message.connectionId,
+               message.transactionId);
+        auto bufferIter = mBuffers.find(message.bufferId);
+        bufferIter->second->mTransactionCount++;
+    } else {
+        if (message.connectionId == found->second->mReceiver) {
+            found->second->mStatus = BufferStatus::TRANSFER_FROM;
+        }
+    }
+    return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
+    auto found = mTransactions.find(message.transactionId);
+    if (found != mTransactions.end()) {
+        bool deleted = erase(&mPendingTransactions, message.connectionId,
+                             message.transactionId);
+        if (deleted) {
+            if (!found->second->mSenderValidated) {
+                mCompletedTransactions.insert(message.transactionId);
+            }
+            auto bufferIter = mBuffers.find(message.bufferId);
+            if (message.newStatus == BufferStatus::TRANSFER_OK) {
+                handleOwnBuffer(message.connectionId, message.bufferId);
+            }
+            bufferIter->second->mTransactionCount--;
+            if (bufferIter->second->mOwnerCount == 0
+                && bufferIter->second->mTransactionCount == 0) {
+                mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                mFreeBuffers.insert(message.bufferId);
+            }
+            mTransactions.erase(found);
+        }
+        ALOGV("transfer finished %llu %u - %d", (unsigned long long)message.transactionId,
+              message.bufferId, deleted);
+        return deleted;
+    }
+    ALOGV("transfer not found %llu %u", (unsigned long long)message.transactionId,
+          message.bufferId);
+    return false;
+}
+
+void Accessor::Impl::BufferPool::processStatusMessages() {
+    std::vector<BufferStatusMessage> messages;
+    mObserver.getBufferStatusChanges(messages);
+    mTimestampUs = getTimestampNow();
+    for (BufferStatusMessage& message: messages) {
+        bool ret = false;
+        switch (message.newStatus) {
+            case BufferStatus::NOT_USED:
+                ret = handleReleaseBuffer(
+                        message.connectionId, message.bufferId);
+                break;
+            case BufferStatus::USED:
+                // not happening
+                break;
+            case BufferStatus::TRANSFER_TO:
+                ret = handleTransferTo(message);
+                break;
+            case BufferStatus::TRANSFER_FROM:
+                ret = handleTransferFrom(message);
+                break;
+            case BufferStatus::TRANSFER_TIMEOUT:
+                // TODO
+                break;
+            case BufferStatus::TRANSFER_LOST:
+                // TODO
+                break;
+            case BufferStatus::TRANSFER_FETCH:
+                // not happening
+                break;
+            case BufferStatus::TRANSFER_OK:
+            case BufferStatus::TRANSFER_ERROR:
+                ret = handleTransferResult(message);
+                break;
+        }
+        if (ret == false) {
+            ALOGW("buffer status message processing failure - message : %d connection : %lld",
+                  message.newStatus, (long long)message.connectionId);
+        }
+    }
+    messages.clear();
+}
+
+bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
+    // Cleaning buffers
+    auto buffers = mUsingBuffers.find(connectionId);
+    if (buffers != mUsingBuffers.end()) {
+        for (const BufferId& bufferId : buffers->second) {
+            bool deleted = erase(&mUsingConnections, bufferId, connectionId);
+            if (deleted) {
+                auto bufferIter = mBuffers.find(bufferId);
+                bufferIter->second->mOwnerCount--;
+                if (bufferIter->second->mOwnerCount == 0 &&
+                        bufferIter->second->mTransactionCount == 0) {
+                    // TODO: handle freebuffer insert fail
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mFreeBuffers.insert(bufferId);
+                }
+            }
+        }
+        mUsingBuffers.erase(buffers);
+    }
+
+    // Cleaning transactions
+    auto pending = mPendingTransactions.find(connectionId);
+    if (pending != mPendingTransactions.end()) {
+        for (const TransactionId& transactionId : pending->second) {
+            auto iter = mTransactions.find(transactionId);
+            if (iter != mTransactions.end()) {
+                if (!iter->second->mSenderValidated) {
+                    mCompletedTransactions.insert(transactionId);
+                }
+                BufferId bufferId = iter->second->mBufferId;
+                auto bufferIter = mBuffers.find(bufferId);
+                bufferIter->second->mTransactionCount--;
+                if (bufferIter->second->mOwnerCount == 0 &&
+                    bufferIter->second->mTransactionCount == 0) {
+                    // TODO: handle freebuffer insert fail
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mFreeBuffers.insert(bufferId);
+                }
+                mTransactions.erase(iter);
+            }
+        }
+    }
+    return true;
+}
+
+bool Accessor::Impl::BufferPool::getFreeBuffer(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
+        const std::vector<uint8_t> &params, BufferId *pId,
+        const native_handle_t** handle) {
+    auto bufferIt = mFreeBuffers.begin();
+    for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
+        BufferId bufferId = *bufferIt;
+        if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
+            break;
+        }
+    }
+    if (bufferIt != mFreeBuffers.end()) {
+        BufferId id = *bufferIt;
+        mFreeBuffers.erase(bufferIt);
+        mStats.onBufferRecycled(mBuffers[id]->mAllocSize);
+        *handle = mBuffers[id]->handle();
+        *pId = id;
+        ALOGV("recycle a buffer %u %p", id, *handle);
+        return true;
+    }
+    return false;
+}
+
+ResultStatus Accessor::Impl::BufferPool::addNewBuffer(
+        const std::shared_ptr<BufferPoolAllocation> &alloc,
+        const size_t allocSize,
+        const std::vector<uint8_t> &params,
+        BufferId *pId,
+        const native_handle_t** handle) {
+
+    BufferId bufferId = mSeq++;
+    if (mSeq == Connection::SYNC_BUFFERID) {
+        mSeq = 0;
+    }
+    std::unique_ptr<InternalBuffer> buffer =
+            std::make_unique<InternalBuffer>(
+                    bufferId, alloc, allocSize, params);
+    if (buffer) {
+        auto res = mBuffers.insert(std::make_pair(
+                bufferId, std::move(buffer)));
+        if (res.second) {
+            mStats.onBufferAllocated(allocSize);
+            *handle = alloc->handle();
+            *pId = bufferId;
+            return ResultStatus::OK;
+        }
+    }
+    return ResultStatus::NO_MEMORY;
+}
+
+void Accessor::Impl::BufferPool::cleanUp(bool clearCache) {
+    if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) {
+        mLastCleanUpUs = mTimestampUs;
+        if (mTimestampUs > mLastLogUs + kLogDurationUs) {
+            mLastLogUs = mTimestampUs;
+            ALOGD("bufferpool %p : %zu(%zu size) total buffers - "
+                  "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - "
+                  "%zu/%zu (fetch/transfer)",
+                  this, mStats.mBuffersCached, mStats.mSizeCached,
+                  mStats.mBuffersInUse, mStats.mSizeInUse,
+                  mStats.mTotalRecycles, mStats.mTotalAllocations,
+                  mStats.mTotalFetches, mStats.mTotalTransfers);
+        }
+        for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+            if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction
+                    && mBuffers.size() < kMinBufferCountForEviction) {
+                break;
+            }
+            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);
+            } else {
+                ++freeIt;
+                ALOGW("bufferpool inconsistent!");
+            }
+        }
+    }
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
new file mode 100644
index 0000000..1d33880
--- /dev/null
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -0,0 +1,300 @@
+/*
+ * 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_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class 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);
+
+    ResultStatus close(ConnectionId connectionId);
+
+    ResultStatus allocate(ConnectionId connectionId,
+                          const std::vector<uint8_t>& params,
+                          BufferId *bufferId,
+                          const native_handle_t** handle);
+
+    ResultStatus fetch(ConnectionId connectionId,
+                       TransactionId transactionId,
+                       BufferId bufferId,
+                       const native_handle_t** handle);
+
+    void cleanUp(bool clearCache);
+
+private:
+    // ConnectionId = pid : (timestamp_created + seqId)
+    // in order to guarantee uniqueness for each connection
+    static uint32_t sSeqId;
+    static int32_t sPid;
+
+    const std::shared_ptr<BufferPoolAllocator> mAllocator;
+
+    /**
+     * Buffer pool implementation.
+     *
+     * Handles buffer status messages. Handles buffer allocation/recycling.
+     * Handles buffer transfer between buffer pool clients.
+     */
+    struct BufferPool {
+    private:
+        std::mutex mMutex;
+        int64_t mTimestampUs;
+        int64_t mLastCleanUpUs;
+        int64_t mLastLogUs;
+        BufferId mSeq;
+        BufferStatusObserver mObserver;
+
+        std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
+        std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
+
+        std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
+        // Transactions completed before TRANSFER_TO message arrival.
+        // Fetch does not occur for the transactions.
+        // Only transaction id is kept for the transactions in short duration.
+        std::set<TransactionId> mCompletedTransactions;
+        // Currently active(pending) transations' status & information.
+        std::map<TransactionId, std::unique_ptr<TransactionStatus>>
+                mTransactions;
+
+        std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
+        std::set<BufferId> mFreeBuffers;
+
+        /// Buffer pool statistics which tracks allocation and transfer statistics.
+        struct Stats {
+            /// Total size of allocations which are used or available to use.
+            /// (bytes or pixels)
+            size_t mSizeCached;
+            /// # of cached buffers which are used or available to use.
+            size_t mBuffersCached;
+            /// Total size of allocations which are currently used. (bytes or pixels)
+            size_t mSizeInUse;
+            /// # of currently used buffers
+            size_t mBuffersInUse;
+
+            /// # of allocations called on bufferpool. (# of fetched from BlockPool)
+            size_t mTotalAllocations;
+            /// # of allocations that were served from the cache.
+            /// (# of allocator alloc prevented)
+            size_t mTotalRecycles;
+            /// # of buffer transfers initiated.
+            size_t mTotalTransfers;
+            /// # of transfers that had to be fetched.
+            size_t mTotalFetches;
+
+            Stats()
+                : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0),
+                  mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {}
+
+            /// A new buffer is allocated on an allocation request.
+            void onBufferAllocated(size_t allocSize) {
+                mSizeCached += allocSize;
+                mBuffersCached++;
+
+                mSizeInUse += allocSize;
+                mBuffersInUse++;
+
+                mTotalAllocations++;
+            }
+
+            /// A buffer is evicted and destroyed.
+            void onBufferEvicted(size_t allocSize) {
+                mSizeCached -= allocSize;
+                mBuffersCached--;
+            }
+
+            /// A buffer is recycled on an allocation request.
+            void onBufferRecycled(size_t allocSize) {
+                mSizeInUse += allocSize;
+                mBuffersInUse++;
+
+                mTotalAllocations++;
+                mTotalRecycles++;
+            }
+
+            /// A buffer is available to be recycled.
+            void onBufferUnused(size_t allocSize) {
+                mSizeInUse -= allocSize;
+                mBuffersInUse--;
+            }
+
+            /// A buffer transfer is initiated.
+            void onBufferSent() {
+                mTotalTransfers++;
+            }
+
+            /// A buffer fetch is invoked by a buffer transfer.
+            void onBufferFetched() {
+                mTotalFetches++;
+            }
+        } mStats;
+
+    public:
+        /** Creates a buffer pool. */
+        BufferPool();
+
+        /** Destroys a buffer pool. */
+        ~BufferPool();
+
+        /**
+         * Processes all pending buffer status messages, and returns the result.
+         * Each status message is handled by methods with 'handle' prefix.
+         */
+        void processStatusMessages();
+
+        /**
+         * Handles a buffer being owned by a connection.
+         *
+         * @param connectionId  the id of the buffer owning connection.
+         * @param bufferId      the id of the buffer.
+         *
+         * @return {@code true} when the buffer is owned,
+         *         {@code false} otherwise.
+         */
+        bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
+
+        /**
+         * Handles a buffer being released by a connection.
+         *
+         * @param connectionId  the id of the buffer owning connection.
+         * @param bufferId      the id of the buffer.
+         *
+         * @return {@code true} when the buffer ownership is released,
+         *         {@code false} otherwise.
+         */
+        bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
+
+        /**
+         * Handles a transfer transaction start message from the sender.
+         *
+         * @param message   a buffer status message for the transaction.
+         *
+         * @result {@code true} when transfer_to message is acknowledged,
+         *         {@code false} otherwise.
+         */
+        bool handleTransferTo(const BufferStatusMessage &message);
+
+        /**
+         * Handles a transfer transaction being acked by the receiver.
+         *
+         * @param message   a buffer status message for the transaction.
+         *
+         * @result {@code true} when transfer_from message is acknowledged,
+         *         {@code false} otherwise.
+         */
+        bool handleTransferFrom(const BufferStatusMessage &message);
+
+        /**
+         * Handles a transfer transaction result message from the receiver.
+         *
+         * @param message   a buffer status message for the transaction.
+         *
+         * @result {@code true} when the exisitng transaction is finished,
+         *         {@code false} otherwise.
+         */
+        bool handleTransferResult(const BufferStatusMessage &message);
+
+        /**
+         * Handles a connection being closed, and returns the result. All the
+         * buffers and transactions owned by the connection will be cleaned up.
+         * The related FMQ will be cleaned up too.
+         *
+         * @param connectionId  the id of the connection.
+         *
+         * @result {@code true} when the connection existed,
+         *         {@code false} otherwise.
+         */
+        bool handleClose(ConnectionId connectionId);
+
+        /**
+         * Recycles a existing free buffer if it is possible.
+         *
+         * @param allocator the buffer allocator
+         * @param params    the allocation parameters.
+         * @param pId       the id of the recycled buffer.
+         * @param handle    the native handle of the recycled buffer.
+         *
+         * @return {@code true} when a buffer is recycled, {@code false}
+         *         otherwise.
+         */
+        bool getFreeBuffer(
+                const std::shared_ptr<BufferPoolAllocator> &allocator,
+                const std::vector<uint8_t> &params,
+                BufferId *pId, const native_handle_t **handle);
+
+        /**
+         * Adds a newly allocated buffer to bufferpool.
+         *
+         * @param alloc     the newly allocated buffer.
+         * @param allocSize the size of the newly allocated buffer.
+         * @param params    the allocation parameters.
+         * @param pId       the buffer id for the newly allocated buffer.
+         * @param handle    the native handle for the newly allocated buffer.
+         *
+         * @return OK when an allocation is successfully allocated.
+         *         NO_MEMORY when there is no memory.
+         *         CRITICAL_ERROR otherwise.
+         */
+        ResultStatus addNewBuffer(
+                const std::shared_ptr<BufferPoolAllocation> &alloc,
+                const size_t allocSize,
+                const std::vector<uint8_t> &params,
+                BufferId *pId,
+                const native_handle_t **handle);
+
+        /**
+         * Processes pending buffer status messages and performs periodic cache
+         * cleaning.
+         *
+         * @param clearCache    if clearCache is true, it frees all buffers
+         *                      waiting to be recycled.
+         */
+        void cleanUp(bool clearCache = false);
+
+        friend class Accessor::Impl;
+    } mBufferPool;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace ufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
new file mode 100644
index 0000000..413125a
--- /dev/null
+++ b/media/bufferpool/2.0/Android.bp
@@ -0,0 +1,29 @@
+cc_library {
+    name: "libstagefright_bufferpool@2.0",
+    vendor_available: true,
+    srcs: [
+        "Accessor.cpp",
+        "AccessorImpl.cpp",
+        "BufferPoolClient.cpp",
+        "BufferStatus.cpp",
+        "ClientManager.cpp",
+        "Connection.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhwbinder",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "android.hardware.media.bufferpool@2.0",
+    ],
+    export_shared_lib_headers: [
+        "libfmq",
+        "android.hardware.media.bufferpool@2.0",
+    ],
+}
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
new file mode 100644
index 0000000..4eeebb4
--- /dev/null
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -0,0 +1,710 @@
+/*
+ * 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_TAG "BufferPoolClient"
+//#define LOG_NDEBUG 0
+
+#include <thread>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
+static constexpr int kPostMaxRetry = 3;
+static constexpr int kCacheTtlUs = 1000000; // TODO: tune
+
+class BufferPoolClient::Impl
+        : public std::enable_shared_from_this<BufferPoolClient::Impl> {
+public:
+    explicit Impl(const sp<Accessor> &accessor);
+
+    explicit Impl(const sp<IAccessor> &accessor);
+
+    bool isValid() {
+        return mValid;
+    }
+
+    bool isLocal() {
+        return mValid && mLocal;
+    }
+
+    ConnectionId getConnectionId() {
+        return mConnectionId;
+    }
+
+    sp<IAccessor> &getAccessor() {
+        return mAccessor;
+    }
+
+    bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus receive(
+            TransactionId transactionId, BufferId bufferId,
+            int64_t timestampUs,
+            native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer);
+
+    void postBufferRelease(BufferId bufferId);
+
+    bool postSend(
+            BufferId bufferId, ConnectionId receiver,
+            TransactionId *transactionId, int64_t *timestampUs);
+private:
+
+    bool postReceive(
+            BufferId bufferId, TransactionId transactionId,
+            int64_t timestampUs);
+
+    bool postReceiveResult(
+            BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync);
+
+    void trySyncFromRemote();
+
+    bool syncReleased();
+
+    void evictCaches(bool clearCache = false);
+
+    ResultStatus allocateBufferHandle(
+            const std::vector<uint8_t>& params, BufferId *bufferId,
+            native_handle_t **handle);
+
+    ResultStatus fetchBufferHandle(
+            TransactionId transactionId, BufferId bufferId,
+            native_handle_t **handle);
+
+    struct BlockPoolDataDtor;
+    struct ClientBuffer;
+
+    bool mLocal;
+    bool mValid;
+    sp<IAccessor> mAccessor;
+    sp<Connection> mLocalConnection;
+    sp<IConnection> mRemoteConnection;
+    uint32_t mSeqId;
+    ConnectionId mConnectionId;
+    int64_t mLastEvictCacheUs;
+
+    // CachedBuffers
+    struct BufferCache {
+        std::mutex mLock;
+        bool mCreating;
+        std::condition_variable mCreateCv;
+        std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
+        int mActive;
+        int64_t mLastChangeUs;
+
+        BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {}
+
+        void incActive_l() {
+            ++mActive;
+            mLastChangeUs = getTimestampNow();
+        }
+
+        void decActive_l() {
+            --mActive;
+            mLastChangeUs = getTimestampNow();
+        }
+    } mCache;
+
+    // FMQ - release notifier
+    struct {
+        std::mutex mLock;
+        // TODO: use only one list?(using one list may dealy sending messages?)
+        std::list<BufferId> mReleasingIds;
+        std::list<BufferId> mReleasedIds;
+        std::unique_ptr<BufferStatusChannel> mStatusChannel;
+    } mReleasing;
+
+    // This lock is held during synchronization from remote side.
+    // In order to minimize remote calls and locking durtaion, this lock is held
+    // by best effort approach using try_lock().
+    std::mutex mRemoteSyncLock;
+};
+
+struct BufferPoolClient::Impl::BlockPoolDataDtor {
+    BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
+            : mImpl(impl) {}
+
+    void operator()(BufferPoolData *buffer) {
+        BufferId id = buffer->mId;
+        delete buffer;
+
+        auto impl = mImpl.lock();
+        if (impl && impl->isValid()) {
+            impl->postBufferRelease(id);
+        }
+    }
+    const std::weak_ptr<BufferPoolClient::Impl> mImpl;
+};
+
+struct BufferPoolClient::Impl::ClientBuffer {
+private:
+    bool mInvalidated; // TODO: implement
+    int64_t mExpireUs;
+    bool mHasCache;
+    ConnectionId mConnectionId;
+    BufferId mId;
+    native_handle_t *mHandle;
+    std::weak_ptr<BufferPoolData> mCache;
+
+    void updateExpire() {
+        mExpireUs = getTimestampNow() + kCacheTtlUs;
+    }
+
+public:
+    ClientBuffer(
+            ConnectionId connectionId, BufferId id, native_handle_t *handle)
+            : mInvalidated(false), mHasCache(false),
+              mConnectionId(connectionId), mId(id), mHandle(handle) {
+        (void)mInvalidated;
+        mExpireUs = getTimestampNow() + kCacheTtlUs;
+    }
+
+    ~ClientBuffer() {
+        if (mHandle) {
+            native_handle_close(mHandle);
+            native_handle_delete(mHandle);
+        }
+    }
+
+    bool expire() const {
+        int64_t now = getTimestampNow();
+        return now >= mExpireUs;
+    }
+
+    bool hasCache() const {
+        return mHasCache;
+    }
+
+    std::shared_ptr<BufferPoolData> fetchCache(native_handle_t **pHandle) {
+        if (mHasCache) {
+            std::shared_ptr<BufferPoolData> cache = mCache.lock();
+            if (cache) {
+                *pHandle = mHandle;
+            }
+            return cache;
+        }
+        return nullptr;
+    }
+
+    std::shared_ptr<BufferPoolData> createCache(
+            const std::shared_ptr<BufferPoolClient::Impl> &impl,
+            native_handle_t **pHandle) {
+        if (!mHasCache) {
+            // Allocates a raw ptr in order to avoid sending #postBufferRelease
+            // from deleter, in case of native_handle_clone failure.
+            BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId);
+            if (ptr) {
+                std::shared_ptr<BufferPoolData> cache(ptr, BlockPoolDataDtor(impl));
+                if (cache) {
+                    mCache = cache;
+                    mHasCache = true;
+                    *pHandle = mHandle;
+                    return cache;
+                }
+            }
+            if (ptr) {
+                delete ptr;
+            }
+        }
+        return nullptr;
+    }
+
+    bool onCacheRelease() {
+        if (mHasCache) {
+            // TODO: verify mCache is not valid;
+            updateExpire();
+            mHasCache = false;
+            return true;
+        }
+        return false;
+    }
+};
+
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+    : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
+      mLastEvictCacheUs(getTimestampNow()) {
+    const StatusDescriptor *fmqDesc;
+    ResultStatus status = accessor->connect(
+            &mLocalConnection, &mConnectionId, &fmqDesc, true);
+    if (status == ResultStatus::OK) {
+        mReleasing.mStatusChannel =
+                std::make_unique<BufferStatusChannel>(*fmqDesc);
+        mValid = mReleasing.mStatusChannel &&
+                mReleasing.mStatusChannel->isValid();
+    }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+    : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
+      mLastEvictCacheUs(getTimestampNow()) {
+    bool valid = false;
+    sp<IConnection>& outConnection = mRemoteConnection;
+    ConnectionId& id = mConnectionId;
+    std::unique_ptr<BufferStatusChannel>& outChannel =
+            mReleasing.mStatusChannel;
+    Return<void> transResult = accessor->connect(
+            [&valid, &outConnection, &id, &outChannel]
+            (ResultStatus status, sp<IConnection> connection,
+             ConnectionId connectionId, const StatusDescriptor& desc,
+             const InvalidationDescriptor& invDesc) {
+                (void) invDesc;
+                if (status == ResultStatus::OK) {
+                    outConnection = connection;
+                    id = connectionId;
+                    outChannel = std::make_unique<BufferStatusChannel>(desc);
+                    if (outChannel && outChannel->isValid()) {
+                        valid = true;
+                    }
+                }
+            });
+    mValid = transResult.isOk() && valid;
+}
+
+bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) {
+    bool active = false;
+    {
+        std::lock_guard<std::mutex> lock(mCache.mLock);
+        syncReleased();
+        evictCaches(clearCache);
+        *lastTransactionUs = mCache.mLastChangeUs;
+        active = mCache.mActive > 0;
+    }
+    if (mValid && mLocal && mLocalConnection) {
+        mLocalConnection->cleanUp(clearCache);
+        return true;
+    }
+    return active;
+}
+
+ResultStatus BufferPoolClient::Impl::allocate(
+        const std::vector<uint8_t> &params,
+        native_handle_t **pHandle,
+        std::shared_ptr<BufferPoolData> *buffer) {
+    if (!mLocal || !mLocalConnection || !mValid) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    BufferId bufferId;
+    native_handle_t *handle = nullptr;
+    buffer->reset();
+    ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
+    if (status == ResultStatus::OK) {
+        if (handle) {
+            std::unique_lock<std::mutex> lock(mCache.mLock);
+            syncReleased();
+            evictCaches();
+            auto cacheIt = mCache.mBuffers.find(bufferId);
+            if (cacheIt != mCache.mBuffers.end()) {
+                // TODO: verify it is recycled. (not having active ref)
+                mCache.mBuffers.erase(cacheIt);
+            }
+            auto clientBuffer = std::make_unique<ClientBuffer>(
+                    mConnectionId, bufferId, handle);
+            if (clientBuffer) {
+                auto result = mCache.mBuffers.insert(std::make_pair(
+                        bufferId, std::move(clientBuffer)));
+                if (result.second) {
+                    *buffer = result.first->second->createCache(
+                            shared_from_this(), pHandle);
+                    if (*buffer) {
+                        mCache.incActive_l();
+                    }
+                }
+            }
+        }
+        if (!*buffer) {
+            ALOGV("client cache creation failure %d: %lld",
+                  handle != nullptr, (long long)mConnectionId);
+            status = ResultStatus::NO_MEMORY;
+            postBufferRelease(bufferId);
+        }
+    }
+    return status;
+}
+
+ResultStatus BufferPoolClient::Impl::receive(
+        TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+        native_handle_t **pHandle,
+        std::shared_ptr<BufferPoolData> *buffer) {
+    if (!mValid) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    if (timestampUs != 0) {
+        timestampUs += kReceiveTimeoutUs;
+    }
+    if (!postReceive(bufferId, transactionId, timestampUs)) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    ResultStatus status = ResultStatus::CRITICAL_ERROR;
+    buffer->reset();
+    while(1) {
+        std::unique_lock<std::mutex> lock(mCache.mLock);
+        syncReleased();
+        evictCaches();
+        auto cacheIt = mCache.mBuffers.find(bufferId);
+        if (cacheIt != mCache.mBuffers.end()) {
+            if (cacheIt->second->hasCache()) {
+                *buffer = cacheIt->second->fetchCache(pHandle);
+                if (!*buffer) {
+                    // check transfer time_out
+                    lock.unlock();
+                    std::this_thread::yield();
+                    continue;
+                }
+                ALOGV("client receive from reference %lld", (long long)mConnectionId);
+                break;
+            } else {
+                *buffer = cacheIt->second->createCache(shared_from_this(), pHandle);
+                if (*buffer) {
+                    mCache.incActive_l();
+                }
+                ALOGV("client receive from cache %lld", (long long)mConnectionId);
+                break;
+            }
+        } else {
+            if (!mCache.mCreating) {
+                mCache.mCreating = true;
+                lock.unlock();
+                native_handle_t* handle = nullptr;
+                status = fetchBufferHandle(transactionId, bufferId, &handle);
+                lock.lock();
+                if (status == ResultStatus::OK) {
+                    if (handle) {
+                        auto clientBuffer = std::make_unique<ClientBuffer>(
+                                mConnectionId, bufferId, handle);
+                        if (clientBuffer) {
+                            auto result = mCache.mBuffers.insert(
+                                    std::make_pair(bufferId, std::move(
+                                            clientBuffer)));
+                            if (result.second) {
+                                *buffer = result.first->second->createCache(
+                                        shared_from_this(), pHandle);
+                                if (*buffer) {
+                                    mCache.incActive_l();
+                                }
+                            }
+                        }
+                    }
+                    if (!*buffer) {
+                        status = ResultStatus::NO_MEMORY;
+                    }
+                }
+                mCache.mCreating = false;
+                lock.unlock();
+                mCache.mCreateCv.notify_all();
+                break;
+            }
+            mCache.mCreateCv.wait(lock);
+        }
+    }
+    bool needsSync = false;
+    bool posted = postReceiveResult(bufferId, transactionId,
+                                      *buffer ? true : false, &needsSync);
+    ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId,
+          *buffer ? "ok" : "fail", posted);
+    if (mValid && mLocal && mLocalConnection) {
+        mLocalConnection->cleanUp(false);
+    }
+    if (needsSync && mRemoteConnection) {
+        trySyncFromRemote();
+    }
+    if (*buffer) {
+        if (!posted) {
+            buffer->reset();
+            return ResultStatus::CRITICAL_ERROR;
+        }
+        return ResultStatus::OK;
+    }
+    return status;
+}
+
+
+void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
+    std::lock_guard<std::mutex> lock(mReleasing.mLock);
+    mReleasing.mReleasingIds.push_back(bufferId);
+    mReleasing.mStatusChannel->postBufferRelease(
+            mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+// TODO: revise ad-hoc posting data structure
+bool BufferPoolClient::Impl::postSend(
+        BufferId bufferId, ConnectionId receiver,
+        TransactionId *transactionId, int64_t *timestampUs) {
+    bool ret = false;
+    bool needsSync = false;
+    {
+        std::lock_guard<std::mutex> lock(mReleasing.mLock);
+        *timestampUs = getTimestampNow();
+        *transactionId = (mConnectionId << 32) | mSeqId++;
+        // TODO: retry, add timeout, target?
+        ret =  mReleasing.mStatusChannel->postBufferStatusMessage(
+                *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
+                receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+        needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+    }
+    if (mValid && mLocal && mLocalConnection) {
+        mLocalConnection->cleanUp(false);
+    }
+    if (needsSync && mRemoteConnection) {
+        trySyncFromRemote();
+    }
+    return ret;
+}
+
+bool BufferPoolClient::Impl::postReceive(
+        BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
+    for (int i = 0; i < kPostMaxRetry; ++i) {
+        std::unique_lock<std::mutex> lock(mReleasing.mLock);
+        int64_t now = getTimestampNow();
+        if (timestampUs == 0 || now < timestampUs) {
+            bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
+                    transactionId, bufferId, BufferStatus::TRANSFER_FROM,
+                    mConnectionId, -1, mReleasing.mReleasingIds,
+                    mReleasing.mReleasedIds);
+            if (result) {
+                return true;
+            }
+            lock.unlock();
+            std::this_thread::yield();
+        } else {
+            mReleasing.mStatusChannel->postBufferStatusMessage(
+                    transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
+                    mConnectionId, -1, mReleasing.mReleasingIds,
+                    mReleasing.mReleasedIds);
+            return false;
+        }
+    }
+    return false;
+}
+
+bool BufferPoolClient::Impl::postReceiveResult(
+        BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) {
+    std::lock_guard<std::mutex> lock(mReleasing.mLock);
+    // TODO: retry, add timeout
+    bool ret = mReleasing.mStatusChannel->postBufferStatusMessage(
+            transactionId, bufferId,
+            result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
+            mConnectionId, -1, mReleasing.mReleasingIds,
+            mReleasing.mReleasedIds);
+    *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+    return ret;
+}
+
+void BufferPoolClient::Impl::trySyncFromRemote() {
+    if (mRemoteSyncLock.try_lock()) {
+        bool needsSync = false;
+        {
+            std::lock_guard<std::mutex> lock(mReleasing.mLock);
+            needsSync = mReleasing.mStatusChannel->needsSync();
+        }
+        if (needsSync) {
+            TransactionId transactionId = (mConnectionId << 32);
+            BufferId bufferId = Connection::SYNC_BUFFERID;
+            Return<void> transResult = mRemoteConnection->fetch(
+                    transactionId, bufferId,
+                    []
+                    (ResultStatus outStatus, Buffer outBuffer) {
+                        (void) outStatus;
+                        (void) outBuffer;
+                    });
+        }
+        mRemoteSyncLock.unlock();
+    }
+}
+
+// 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();
+                } else {
+                    // should not happen!
+                    ALOGW("client %lld cache release status inconsitent!",
+                          (long long)mConnectionId);
+                }
+            } else {
+                // should not happen!
+                ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
+            }
+        }
+        mReleasing.mReleasedIds.clear();
+        return true;
+    }
+    return false;
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::evictCaches(bool clearCache) {
+    int64_t now = getTimestampNow();
+    if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
+        size_t evicted = 0;
+        for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+            if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
+                it = mCache.mBuffers.erase(it);
+                ++evicted;
+            } else {
+                ++it;
+            }
+        }
+        ALOGV("cache count %lld : total %zu, active %d, evicted %zu",
+              (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted);
+        mLastEvictCacheUs = now;
+    }
+}
+
+ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
+        const std::vector<uint8_t>& params, BufferId *bufferId,
+        native_handle_t** handle) {
+    if (mLocalConnection) {
+        const native_handle_t* allocHandle = nullptr;
+        ResultStatus status = mLocalConnection->allocate(
+                params, bufferId, &allocHandle);
+        if (status == ResultStatus::OK) {
+            *handle = native_handle_clone(allocHandle);
+        }
+        ALOGV("client allocate result %lld %d : %u clone %p",
+              (long long)mConnectionId, status == ResultStatus::OK,
+              *handle ? *bufferId : 0 , *handle);
+        return status;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
+        TransactionId transactionId, BufferId bufferId,
+        native_handle_t **handle) {
+    sp<IConnection> connection;
+    if (mLocal) {
+        connection = mLocalConnection;
+    } else {
+        connection = mRemoteConnection;
+    }
+    ResultStatus status;
+    Return<void> transResult = connection->fetch(
+            transactionId, bufferId,
+            [&status, &handle]
+            (ResultStatus outStatus, Buffer outBuffer) {
+                status = outStatus;
+                if (status == ResultStatus::OK) {
+                    *handle = native_handle_clone(
+                            outBuffer.buffer.getNativeHandle());
+                }
+            });
+    return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR;
+}
+
+
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
+    mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
+    mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::~BufferPoolClient() {
+    // TODO: how to handle orphaned buffers?
+}
+
+bool BufferPoolClient::isValid() {
+    return mImpl && mImpl->isValid();
+}
+
+bool BufferPoolClient::isLocal() {
+    return mImpl && mImpl->isLocal();
+}
+
+bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) {
+    if (!isValid()) {
+        *lastTransactionUs = 0;
+        return false;
+    }
+    return mImpl->isActive(lastTransactionUs, clearCache);
+}
+
+ConnectionId BufferPoolClient::getConnectionId() {
+    if (isValid()) {
+        return mImpl->getConnectionId();
+    }
+    return -1;
+}
+
+ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
+    if (isValid()) {
+        *accessor = mImpl->getAccessor();
+        return ResultStatus::OK;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::allocate(
+        const std::vector<uint8_t> &params,
+        native_handle_t **handle,
+        std::shared_ptr<BufferPoolData> *buffer) {
+    if (isValid()) {
+        return mImpl->allocate(params, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::receive(
+        TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    if (isValid()) {
+        return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::postSend(
+        ConnectionId receiverId,
+        const std::shared_ptr<BufferPoolData> &buffer,
+        TransactionId *transactionId,
+        int64_t *timestampUs) {
+    if (isValid()) {
+        bool result = mImpl->postSend(
+                buffer->mId, receiverId, transactionId, timestampUs);
+        return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/BufferPoolClient.h b/media/bufferpool/2.0/BufferPoolClient.h
new file mode 100644
index 0000000..00d6839
--- /dev/null
+++ b/media/bufferpool/2.0/BufferPoolClient.h
@@ -0,0 +1,102 @@
+/*
+ * 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_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <android/hardware/media/bufferpool/2.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/2.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <cutils/native_handle.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V2_0::IAccessor;
+using ::android::hardware::media::bufferpool::V2_0::IConnection;
+using ::android::hardware::media::bufferpool::V2_0::ResultStatus;
+using ::android::sp;
+
+/**
+ * A buffer pool client for a buffer pool. For a specific buffer pool, at most
+ * one buffer pool client exists per process. This class will not be exposed
+ * outside. A buffer pool client will be used via ClientManager.
+ */
+class BufferPoolClient {
+public:
+    /**
+     * Creates a buffer pool client from a local buffer pool
+     * (via ClientManager#create).
+     */
+    explicit BufferPoolClient(const sp<Accessor> &accessor);
+
+    /**
+     * Creates a buffer pool client from a remote buffer pool
+     * (via ClientManager#registerSender).
+     * Note: A buffer pool client created with remote buffer pool cannot
+     * allocate a buffer.
+     */
+    explicit BufferPoolClient(const sp<IAccessor> &accessor);
+
+    /** Destructs a buffer pool client. */
+    ~BufferPoolClient();
+
+private:
+    bool isValid();
+
+    bool isLocal();
+
+    bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+    ConnectionId getConnectionId();
+
+    ResultStatus getAccessor(sp<IAccessor> *accessor);
+
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus receive(TransactionId transactionId,
+                         BufferId bufferId,
+                         int64_t timestampUs,
+                         native_handle_t **handle,
+                         std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus postSend(ConnectionId receiver,
+                          const std::shared_ptr<BufferPoolData> &buffer,
+                          TransactionId *transactionId,
+                          int64_t *timestampUs);
+
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+
+    friend struct ClientManager;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp
new file mode 100644
index 0000000..0d3f5a3f
--- /dev/null
+++ b/media/bufferpool/2.0/BufferStatus.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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_TAG "BufferPoolStatus"
+//#define LOG_NDEBUG 0
+
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+int64_t getTimestampNow() {
+    int64_t stamp;
+    struct timespec ts;
+    // TODO: CLOCK_MONOTONIC_COARSE?
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    stamp = ts.tv_nsec / 1000;
+    stamp += (ts.tv_sec * 1000000LL);
+    return stamp;
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+static constexpr int kMinElementsToSyncInQueue = 128;
+
+ResultStatus BufferStatusObserver::open(
+        ConnectionId id, const StatusDescriptor** fmqDescPtr) {
+    if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
+        // TODO: id collision log?
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    std::unique_ptr<BufferStatusQueue> queue =
+            std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
+    if (!queue || queue->isValid() == false) {
+        *fmqDescPtr = nullptr;
+        return ResultStatus::NO_MEMORY;
+    } else {
+        *fmqDescPtr = queue->getDesc();
+    }
+    auto result = mBufferStatusQueues.insert(
+            std::make_pair(id, std::move(queue)));
+    if (!result.second) {
+        *fmqDescPtr = nullptr;
+        return ResultStatus::NO_MEMORY;
+    }
+    return ResultStatus::OK;
+}
+
+ResultStatus BufferStatusObserver::close(ConnectionId id) {
+    if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    mBufferStatusQueues.erase(id);
+    return ResultStatus::OK;
+}
+
+void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
+    for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
+        BufferStatusMessage message;
+        size_t avail = it->second->availableToRead();
+        while (avail > 0) {
+            if (!it->second->read(&message, 1)) {
+                // Since avaliable # of reads are already confirmed,
+                // this should not happen.
+                // TODO: error handling (spurious client?)
+                ALOGW("FMQ message cannot be read from %lld", (long long)it->first);
+                return;
+            }
+            message.connectionId = it->first;
+            messages.push_back(message);
+            --avail;
+        }
+    }
+}
+
+BufferStatusChannel::BufferStatusChannel(
+        const StatusDescriptor &fmqDesc) {
+    std::unique_ptr<BufferStatusQueue> queue =
+            std::make_unique<BufferStatusQueue>(fmqDesc);
+    if (!queue || queue->isValid() == false) {
+        mValid = false;
+        return;
+    }
+    mValid  = true;
+    mBufferStatusQueue = std::move(queue);
+}
+
+bool BufferStatusChannel::isValid() {
+    return mValid;
+}
+
+bool BufferStatusChannel::needsSync() {
+    if (mValid) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        return avail + kMinElementsToSyncInQueue < kNumElementsInQueue;
+    }
+    return false;
+}
+
+void BufferStatusChannel::postBufferRelease(
+        ConnectionId connectionId,
+        std::list<BufferId> &pending, std::list<BufferId> &posted) {
+    if (mValid && pending.size() > 0) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        avail = std::min(avail, pending.size());
+        BufferStatusMessage message;
+        for (size_t i = 0 ; i < avail; ++i) {
+            BufferId id = pending.front();
+            message.newStatus = BufferStatus::NOT_USED;
+            message.bufferId = id;
+            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;
+            }
+            pending.pop_front();
+            posted.push_back(id);
+        }
+    }
+}
+
+bool BufferStatusChannel::postBufferStatusMessage(
+        TransactionId transactionId, BufferId bufferId,
+        BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
+        std::list<BufferId> &pending, std::list<BufferId> &posted) {
+    if (mValid) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        size_t numPending = pending.size();
+        if (avail >= numPending + 1) {
+            BufferStatusMessage release, message;
+            for (size_t i = 0; i < numPending; ++i) {
+                BufferId id = pending.front();
+                release.newStatus = BufferStatus::NOT_USED;
+                release.bufferId = id;
+                release.connectionId = connectionId;
+                if (!mBufferStatusQueue->write(&release, 1)) {
+                    // Since avaliable # of writes are already confirmed,
+                    // this should not happen.
+                    // TODO: error handling?
+                    ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+                    return false;
+                }
+                pending.pop_front();
+                posted.push_back(id);
+            }
+            message.transactionId = transactionId;
+            message.bufferId = bufferId;
+            message.newStatus = status;
+            message.connectionId = connectionId;
+            message.targetConnectionId = targetId;
+            // TODO : timesatamp
+            message.timestampUs = 0;
+            if (!mBufferStatusQueue->write(&message, 1)) {
+                // Since avaliable # of writes are already confirmed,
+                // this should not happen.
+                ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+                return false;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h
new file mode 100644
index 0000000..777a320
--- /dev/null
+++ b/media/bufferpool/2.0/BufferStatus.h
@@ -0,0 +1,143 @@
+/*
+ * 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_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/2.0/types.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <list>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+/**
+ * A collection of FMQ for a buffer pool. buffer ownership/status change
+ * messages are sent via the FMQs from the clients.
+ */
+class BufferStatusObserver {
+private:
+    std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
+            mBufferStatusQueues;
+
+public:
+    /** Creates an FMQ for the specified connection(client).
+     *
+     * @param connectionId  connection Id of the specified client.
+     * @param fmqDescPtr    double ptr of created FMQ's descriptor.
+     *
+     * @return OK if FMQ is created successfully.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus open(ConnectionId id, const StatusDescriptor** fmqDescPtr);
+
+    /** Closes an FMQ for the specified connection(client).
+     *
+     * @param connectionId  connection Id of the specified client.
+     *
+     * @return OK if the specified connection is closed successfully.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus close(ConnectionId id);
+
+    /** Retrieves all pending FMQ buffer status messages from clients.
+     *
+     * @param messages  retrieved pending messages.
+     */
+    void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
+};
+
+/**
+ * An FMQ for a buffer pool client. Buffer ownership/status change messages
+ * are sent via the fmq to the buffer pool.
+ */
+class BufferStatusChannel {
+private:
+    bool mValid;
+    std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
+
+public:
+    /**
+     * Connects to an FMQ from a descriptor of the created FMQ.
+     *
+     * @param fmqDesc   Descriptor of the created FMQ.
+     */
+    BufferStatusChannel(const StatusDescriptor &fmqDesc);
+
+    /** Returns whether the FMQ is connected successfully. */
+    bool isValid();
+
+    /** Returns whether the FMQ needs to be synced from the buffer pool */
+    bool needsSync();
+
+    /**
+     * Posts a buffer release message to the buffer pool.
+     *
+     * @param connectionId  connection Id of the client.
+     * @param pending       currently pending buffer release messages.
+     * @param posted        posted buffer release messages.
+     */
+    void postBufferRelease(
+            ConnectionId connectionId,
+            std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+    /**
+     * Posts a buffer status message regarding the specified buffer
+     * transfer transaction.
+     *
+     * @param transactionId Id of the specified transaction.
+     * @param bufferId      buffer Id of the specified transaction.
+     * @param status        new status of the buffer.
+     * @param connectionId  connection Id of the client.
+     * @param targetId      connection Id of the receiver(only when the sender
+     *                      posts a status message).
+     * @param pending       currently pending buffer release messages.
+     * @param posted        posted buffer release messages.
+     *
+     * @return {@code true} when the specified message is posted,
+     *         {@code false} otherwise.
+     */
+    bool postBufferStatusMessage(
+            TransactionId transactionId,
+            BufferId bufferId,
+            BufferStatus status,
+            ConnectionId connectionId,
+            ConnectionId targetId,
+            std::list<BufferId> &pending, std::list<BufferId> &posted);
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
new file mode 100644
index 0000000..eeaf093
--- /dev/null
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -0,0 +1,504 @@
+/*
+ * 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_TAG "BufferPoolManager"
+//#define LOG_NDEBUG 0
+
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlTransportSupport.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
+static constexpr int64_t kCleanUpDurationUs = 1000000; // TODO: 1 sec tune
+static constexpr int64_t kClientTimeoutUs = 5000000; // TODO: 5 secs tune
+
+/**
+ * The holder of the cookie of remote IClientManager.
+ * The cookie is process locally unique for each IClientManager.
+ * (The cookie is used to notify death of clients to bufferpool process.)
+ */
+class ClientManagerCookieHolder {
+public:
+    /**
+     * Creates a cookie holder for remote IClientManager(s).
+     */
+    ClientManagerCookieHolder();
+
+    /**
+     * Gets a cookie for a remote IClientManager.
+     *
+     * @param manager   the specified remote IClientManager.
+     * @param added     true when the specified remote IClientManager is added
+     *                  newly, false otherwise.
+     *
+     * @return the process locally unique cookie for the specified IClientManager.
+     */
+    uint64_t getCookie(const sp<IClientManager> &manager, bool *added);
+
+private:
+    uint64_t mSeqId;
+    std::mutex mLock;
+    std::list<std::pair<const wp<IClientManager>, uint64_t>> mManagers;
+};
+
+ClientManagerCookieHolder::ClientManagerCookieHolder() : mSeqId(0){}
+
+uint64_t ClientManagerCookieHolder::getCookie(
+        const sp<IClientManager> &manager,
+        bool *added) {
+    std::lock_guard<std::mutex> lock(mLock);
+    for (auto it = mManagers.begin(); it != mManagers.end();) {
+        const sp<IClientManager> key = it->first.promote();
+        if (key) {
+            if (interfacesEqual(key, manager)) {
+                *added = false;
+                return it->second;
+            }
+            ++it;
+        } else {
+            it = mManagers.erase(it);
+        }
+    }
+    uint64_t id = mSeqId++;
+    *added = true;
+    mManagers.push_back(std::make_pair(manager, id));
+    return id;
+}
+
+class ClientManager::Impl {
+public:
+    Impl();
+
+    // BnRegisterSender
+    ResultStatus registerSender(const sp<IAccessor> &accessor,
+                                ConnectionId *pConnectionId);
+
+    // BpRegisterSender
+    ResultStatus registerSender(const sp<IClientManager> &receiver,
+                                ConnectionId senderId,
+                                ConnectionId *receiverId);
+
+    ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+                        ConnectionId *pConnectionId);
+
+    ResultStatus close(ConnectionId connectionId);
+
+    ResultStatus allocate(ConnectionId connectionId,
+                          const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus receive(ConnectionId connectionId,
+                         TransactionId transactionId,
+                         BufferId bufferId,
+                         int64_t timestampUs,
+                         native_handle_t **handle,
+                         std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus postSend(ConnectionId receiverId,
+                          const std::shared_ptr<BufferPoolData> &buffer,
+                          TransactionId *transactionId,
+                          int64_t *timestampUs);
+
+    ResultStatus getAccessor(ConnectionId connectionId,
+                             sp<IAccessor> *accessor);
+
+    void cleanUp(bool clearCache = false);
+
+private:
+    // In order to prevent deadlock between multiple locks,
+    // always lock ClientCache.lock before locking ActiveClients.lock.
+    struct ClientCache {
+        // This lock is held for brief duration.
+        // Blocking operation is not performed while holding the lock.
+        std::mutex mMutex;
+        std::list<std::pair<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>>
+                mClients;
+        std::condition_variable mConnectCv;
+        bool mConnecting;
+        int64_t mLastCleanUpUs;
+
+        ClientCache() : mConnecting(false), mLastCleanUpUs(getTimestampNow()) {}
+    } mCache;
+
+    // Active clients which can be retrieved via ConnectionId
+    struct ActiveClients {
+        // This lock is held for brief duration.
+        // Blocking operation is not performed holding the lock.
+        std::mutex mMutex;
+        std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
+                mClients;
+    } mActive;
+
+    ClientManagerCookieHolder mRemoteClientCookies;
+};
+
+ClientManager::Impl::Impl() {}
+
+ResultStatus ClientManager::Impl::registerSender(
+        const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
+    cleanUp();
+    int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
+    do {
+        std::unique_lock<std::mutex> lock(mCache.mMutex);
+        for (auto it = mCache.mClients.begin(); it != mCache.mClients.end(); ++it) {
+            sp<IAccessor> sAccessor = it->first.promote();
+            if (sAccessor && interfacesEqual(sAccessor, accessor)) {
+                const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+                if (client) {
+                    std::lock_guard<std::mutex> lock(mActive.mMutex);
+                    *pConnectionId = client->getConnectionId();
+                    if (mActive.mClients.find(*pConnectionId) != mActive.mClients.end()) {
+                        ALOGV("register existing connection %lld", (long long)*pConnectionId);
+                        return ResultStatus::ALREADY_EXISTS;
+                    }
+                }
+                mCache.mClients.erase(it);
+                break;
+            }
+        }
+        if (!mCache.mConnecting) {
+            mCache.mConnecting = true;
+            lock.unlock();
+            ResultStatus result = ResultStatus::OK;
+            const std::shared_ptr<BufferPoolClient> client =
+                    std::make_shared<BufferPoolClient>(accessor);
+            lock.lock();
+            if (!client) {
+                result = ResultStatus::NO_MEMORY;
+            } else if (!client->isValid()) {
+                result = ResultStatus::CRITICAL_ERROR;
+            }
+            if (result == ResultStatus::OK) {
+                // TODO: handle insert fail. (malloc fail)
+                const std::weak_ptr<BufferPoolClient> wclient = client;
+                mCache.mClients.push_back(std::make_pair(accessor, wclient));
+                ConnectionId conId = client->getConnectionId();
+                {
+                    std::lock_guard<std::mutex> lock(mActive.mMutex);
+                    mActive.mClients.insert(std::make_pair(conId, client));
+                }
+                *pConnectionId = conId;
+                ALOGV("register new connection %lld", (long long)*pConnectionId);
+            }
+            mCache.mConnecting = false;
+            lock.unlock();
+            mCache.mConnectCv.notify_all();
+            return result;
+        }
+        mCache.mConnectCv.wait_for(
+                lock, std::chrono::microseconds(kRegisterTimeoutUs));
+    } while (getTimestampNow() < timeoutUs);
+    // TODO: return timeout error
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::Impl::registerSender(
+        const sp<IClientManager> &receiver,
+        ConnectionId senderId,
+        ConnectionId *receiverId) {
+    sp<IAccessor> accessor;
+    bool local = false;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(senderId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        it->second->getAccessor(&accessor);
+        local = it->second->isLocal();
+    }
+    ResultStatus rs = ResultStatus::CRITICAL_ERROR;
+    if (accessor) {
+       Return<void> transResult = receiver->registerSender(
+                accessor,
+                [&rs, receiverId](
+                        ResultStatus status,
+                        int64_t connectionId) {
+                    rs = status;
+                    *receiverId = connectionId;
+                });
+        if (!transResult.isOk()) {
+            return ResultStatus::CRITICAL_ERROR;
+        } else if (local && rs == ResultStatus::OK) {
+            sp<ConnectionDeathRecipient> recipient = Accessor::getConnectionDeathRecipient();
+            if (recipient)  {
+                ALOGV("client death recipient registered %lld", (long long)*receiverId);
+                bool added;
+                uint64_t cookie = mRemoteClientCookies.getCookie(receiver, &added);
+                recipient->addCookieToConnection(cookie, *receiverId);
+                if (added) {
+                    Return<bool> transResult = receiver->linkToDeath(recipient, cookie);
+                }
+            }
+        }
+    }
+    return rs;
+}
+
+ResultStatus ClientManager::Impl::create(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
+        ConnectionId *pConnectionId) {
+    const sp<Accessor> accessor = new Accessor(allocator);
+    if (!accessor || !accessor->isValid()) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    std::shared_ptr<BufferPoolClient> client =
+            std::make_shared<BufferPoolClient>(accessor);
+    if (!client || !client->isValid()) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    // Since a new bufferpool is created, evict memories which are used by
+    // existing bufferpools and clients.
+    cleanUp(true);
+    {
+        // TODO: handle insert fail. (malloc fail)
+        std::lock_guard<std::mutex> lock(mCache.mMutex);
+        const std::weak_ptr<BufferPoolClient> wclient = client;
+        mCache.mClients.push_back(std::make_pair(accessor, wclient));
+        ConnectionId conId = client->getConnectionId();
+        {
+            std::lock_guard<std::mutex> lock(mActive.mMutex);
+            mActive.mClients.insert(std::make_pair(conId, client));
+        }
+        *pConnectionId = conId;
+        ALOGV("create new connection %lld", (long long)*pConnectionId);
+    }
+    return ResultStatus::OK;
+}
+
+ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock1(mCache.mMutex);
+    std::lock_guard<std::mutex> lock2(mActive.mMutex);
+    auto it = mActive.mClients.find(connectionId);
+    if (it != mActive.mClients.end()) {
+        sp<IAccessor> accessor;
+        it->second->getAccessor(&accessor);
+        mActive.mClients.erase(connectionId);
+        for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+            // clean up dead client caches
+            sp<IAccessor> cAccessor = cit->first.promote();
+            if (!cAccessor || (accessor && interfacesEqual(cAccessor, accessor))) {
+                cit = mCache.mClients.erase(cit);
+            } else {
+                cit++;
+            }
+        }
+        return ResultStatus::OK;
+    }
+    return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::allocate(
+        ConnectionId connectionId, const std::vector<uint8_t> &params,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    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->allocate(params, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::receive(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, int64_t timestampUs,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    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->receive(transactionId, bufferId, timestampUs, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::postSend(
+        ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+        TransactionId *transactionId, int64_t *timestampUs) {
+    ConnectionId connectionId = buffer->mConnectionId;
+    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->postSend(receiverId, buffer, transactionId, timestampUs);
+}
+
+ResultStatus ClientManager::Impl::getAccessor(
+        ConnectionId connectionId, sp<IAccessor> *accessor) {
+    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->getAccessor(accessor);
+}
+
+void ClientManager::Impl::cleanUp(bool clearCache) {
+    int64_t now = getTimestampNow();
+    int64_t lastTransactionUs;
+    std::lock_guard<std::mutex> lock1(mCache.mMutex);
+    if (clearCache || mCache.mLastCleanUpUs + kCleanUpDurationUs < now) {
+        std::lock_guard<std::mutex> lock2(mActive.mMutex);
+        int cleaned = 0;
+        for (auto it = mActive.mClients.begin(); it != mActive.mClients.end();) {
+            if (!it->second->isActive(&lastTransactionUs, clearCache)) {
+                if (lastTransactionUs + kClientTimeoutUs < now) {
+                    sp<IAccessor> accessor;
+                    it->second->getAccessor(&accessor);
+                    it = mActive.mClients.erase(it);
+                    ++cleaned;
+                    continue;
+                }
+            }
+            ++it;
+        }
+        for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+            // clean up dead client caches
+            sp<IAccessor> cAccessor = cit->first.promote();
+            if (!cAccessor) {
+                cit = mCache.mClients.erase(cit);
+            } else {
+                ++cit;
+            }
+        }
+        ALOGV("# of cleaned connections: %d", cleaned);
+        mCache.mLastCleanUpUs = now;
+    }
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V2_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
+    if (mImpl) {
+        ConnectionId connectionId = -1;
+        ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
+        _hidl_cb(status, connectionId);
+    } else {
+        _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
+    }
+    return Void();
+}
+
+// Methods for local use.
+sp<ClientManager> ClientManager::sInstance;
+std::mutex ClientManager::sInstanceLock;
+
+sp<ClientManager> ClientManager::getInstance() {
+    std::lock_guard<std::mutex> lock(sInstanceLock);
+    if (!sInstance) {
+        sInstance = new ClientManager();
+    }
+    return sInstance;
+}
+
+ClientManager::ClientManager() : mImpl(new Impl()) {}
+
+ClientManager::~ClientManager() {
+}
+
+ResultStatus ClientManager::create(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
+        ConnectionId *pConnectionId) {
+    if (mImpl) {
+        return mImpl->create(allocator, pConnectionId);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::registerSender(
+        const sp<IClientManager> &receiver,
+        ConnectionId senderId,
+        ConnectionId *receiverId) {
+    if (mImpl) {
+        return mImpl->registerSender(receiver, senderId, receiverId);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::close(ConnectionId connectionId) {
+    if (mImpl) {
+        return mImpl->close(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) {
+    if (mImpl) {
+        return mImpl->allocate(connectionId, params, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::receive(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, int64_t timestampUs,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    if (mImpl) {
+        return mImpl->receive(connectionId, transactionId, bufferId,
+                              timestampUs, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::postSend(
+        ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+        TransactionId *transactionId, int64_t* timestampUs) {
+    if (mImpl && buffer) {
+        return mImpl->postSend(receiverId, buffer, transactionId, timestampUs);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void ClientManager::cleanUp() {
+    if (mImpl) {
+        mImpl->cleanUp(true);
+    }
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp
new file mode 100644
index 0000000..cd837a1
--- /dev/null
+++ b/media/bufferpool/2.0/Connection.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow.
+Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
+    ResultStatus status = ResultStatus::CRITICAL_ERROR;
+    if (mInitialized && mAccessor) {
+        if (bufferId != SYNC_BUFFERID) {
+            const native_handle_t *handle = nullptr;
+            status = mAccessor->fetch(
+                    mConnectionId, transactionId, bufferId, &handle);
+            if (status == ResultStatus::OK) {
+                _hidl_cb(status, Buffer{bufferId, handle});
+                return Void();
+            }
+        } else {
+            mAccessor->cleanUp(false);
+        }
+    }
+    _hidl_cb(status, Buffer{0, nullptr});
+    return Void();
+}
+
+Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
+
+Connection::~Connection() {
+    if (mInitialized && mAccessor) {
+        mAccessor->close(mConnectionId);
+    }
+}
+
+void Connection::initialize(
+        const sp<Accessor>& accessor, ConnectionId connectionId) {
+    if (!mInitialized) {
+        mAccessor = accessor;
+        mConnectionId = connectionId;
+        mInitialized = true;
+    }
+}
+
+ResultStatus Connection::allocate(
+        const std::vector<uint8_t> &params, BufferId *bufferId,
+        const native_handle_t **handle) {
+    if (mInitialized && mAccessor) {
+        return mAccessor->allocate(mConnectionId, params, bufferId, handle);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void Connection::cleanUp(bool clearCache) {
+    if (mInitialized && mAccessor) {
+        mAccessor->cleanUp(clearCache);
+    }
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
+//    return new Connection();
+//}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Connection.h b/media/bufferpool/2.0/Connection.h
new file mode 100644
index 0000000..e2b47f1
--- /dev/null
+++ b/media/bufferpool/2.0/Connection.h
@@ -0,0 +1,103 @@
+/*
+ * 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_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/2.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "Accessor.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::media::bufferpool::V2_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow.
+    Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+    /**
+     * 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).
+     *
+     * @param params    allocation parameters.
+     * @param bufferId  Id of the allocated buffer.
+     * @param handle    native handle of the allocated buffer.
+     *
+     * @return OK if a buffer is successfully allocated.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          BufferId *bufferId, const native_handle_t **handle);
+
+    /**
+     * Processes pending buffer status messages and performs periodic cache cleaning
+     * from bufferpool.
+     *
+     * @param clearCache    if clearCache is true, bufferpool frees all buffers
+     *                      waiting to be recycled.
+     */
+    void cleanUp(bool clearCache);
+
+    /** Destructs a connection. */
+    ~Connection();
+
+    /** Creates a connection. */
+    Connection();
+
+    /**
+     * Initializes with the specified buffer pool and the connection id.
+     * The connection id should be unique in the whole system.
+     *
+     * @param accessor      the specified buffer pool.
+     * @param connectionId  Id.
+     */
+    void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
+
+    enum : uint32_t {
+        SYNC_BUFFERID = UINT32_MAX,
+    };
+
+private:
+    bool mInitialized;
+    sp<Accessor> mAccessor;
+    ConnectionId mConnectionId;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
diff --git a/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h
new file mode 100644
index 0000000..eb845e1
--- /dev/null
+++ b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h
@@ -0,0 +1,122 @@
+/*
+ * 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_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/2.0/types.h>
+#include <cutils/native_handle.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+
+struct BufferPoolData {
+    // For local use, to specify a bufferpool (client connection) for buffers.
+    // Return value from connect#IAccessor(android.hardware.media.bufferpool@2.0).
+    int64_t mConnectionId;
+    // BufferId
+    uint32_t mId;
+
+    BufferPoolData() : mConnectionId(0), mId(0) {}
+
+    BufferPoolData(
+            int64_t connectionId, uint32_t id)
+            : mConnectionId(connectionId), mId(id) {}
+
+    ~BufferPoolData() {}
+};
+
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+
+typedef uint32_t BufferId;
+typedef uint64_t TransactionId;
+typedef int64_t ConnectionId;
+
+enum : ConnectionId {
+    INVALID_CONNECTIONID = 0,
+};
+
+typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
+typedef BufferStatusQueue::Descriptor StatusDescriptor;
+
+typedef android::hardware::MessageQueue<BufferInvalidationMessage, kSynchronizedReadWrite>
+        BufferInvalidationQueue;
+typedef BufferInvalidationQueue::Descriptor InvalidationDescriptor;
+
+/**
+ * Allocation wrapper class for buffer pool.
+ */
+struct BufferPoolAllocation {
+    const native_handle_t *mHandle;
+
+    const native_handle_t *handle() {
+        return mHandle;
+    }
+
+    BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
+
+    ~BufferPoolAllocation() {};
+};
+
+/**
+ * Allocator wrapper class for buffer pool.
+ */
+class BufferPoolAllocator {
+public:
+
+    /**
+     * Allocate an allocation(buffer) for buffer pool.
+     *
+     * @param params    allocation parameters
+     * @param alloc     created allocation
+     * @param allocSize size of created allocation
+     *
+     * @return OK when an allocation is created successfully.
+     */
+    virtual ResultStatus allocate(
+            const std::vector<uint8_t> &params,
+            std::shared_ptr<BufferPoolAllocation> *alloc,
+            size_t *allocSize) = 0;
+
+    /**
+     * Returns whether allocation parameters of an old allocation are
+     * compatible with new allocation parameters.
+     */
+    virtual bool compatible(const std::vector<uint8_t> &newParams,
+                            const std::vector<uint8_t> &oldParams) = 0;
+
+protected:
+    BufferPoolAllocator() = default;
+
+    virtual ~BufferPoolAllocator() = default;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
diff --git a/media/bufferpool/2.0/include/bufferpool/ClientManager.h b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
new file mode 100644
index 0000000..cfc3bc3
--- /dev/null
+++ b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
@@ -0,0 +1,179 @@
+/*
+ * 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_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include "BufferPoolTypes.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::media::bufferpool::V2_0::IAccessor;
+using ::android::hardware::media::bufferpool::V2_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow.
+    Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V2_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
+
+    /** Gets an instance. */
+    static sp<ClientManager> getInstance();
+
+    /**
+     * Creates a local connection with a newly created buffer pool.
+     *
+     * @param allocator     for new buffer allocation.
+     * @param pConnectionId Id of the created connection. This is
+     *                      system-wide unique.
+     *
+     * @return OK when a buffer pool and a local connection is successfully
+     *         created.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+                        ConnectionId *pConnectionId);
+
+    /**
+     * Register a created connection as sender for remote process.
+     *
+     * @param receiver      The remote receiving process.
+     * @param senderId      A local connection which will send buffers to.
+     * @param receiverId    Id of the created receiving connection on the receiver
+     *                      process.
+     *
+     * @return OK when the receiving connection is successfully created on the
+     *         receiver process.
+     *         NOT_FOUND when the sender connection was not found.
+     *         ALREADY_EXISTS the receiving connection is already made.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus registerSender(const sp<IClientManager> &receiver,
+                                ConnectionId senderId,
+                                ConnectionId *receiverId);
+
+    /**
+     * Closes the specified connection.
+     *
+     * @param connectionId  The id of the connection.
+     *
+     * @return OK when the connection is closed.
+     *         NOT_FOUND when the specified connection was not found.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus close(ConnectionId connectionId);
+
+    /**
+     * Allocates a buffer from the specified connection.
+     *
+     * @param connectionId  The id of the connection.
+     * @param params        The allocation parameters.
+     * @param handle        The native handle to the allocated buffer. handle
+     *                      should be cloned before use.
+     * @param buffer        The allocated buffer.
+     *
+     * @return OK when a buffer was allocated successfully.
+     *         NOT_FOUND when the specified connection was not found.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus allocate(ConnectionId connectionId,
+                          const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    /**
+     * Receives a buffer for the transaction.
+     *
+     * @param connectionId  The id of the receiving connection.
+     * @param transactionId The id for the transaction.
+     * @param bufferId      The id for the buffer.
+     * @param timestampUs   The timestamp of the buffer is being sent.
+     * @param handle        The native handle to the allocated buffer. handle
+     *                      should be cloned before use.
+     * @param buffer        The received buffer.
+     *
+     * @return OK when a buffer was received successfully.
+     *         NOT_FOUND when the specified connection was not found.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus receive(ConnectionId connectionId,
+                         TransactionId transactionId,
+                         BufferId bufferId,
+                         int64_t timestampUs,
+                          native_handle_t **handle,
+                         std::shared_ptr<BufferPoolData> *buffer);
+
+    /**
+     * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
+     * to other remote clients(connection) after this call has been succeeded.
+     *
+     * @param receiverId    The id of the receiving connection.
+     * @param buffer        to transfer
+     * @param transactionId Id of the transfer transaction.
+     * @param timestampUs   The timestamp of the buffer transaction is being
+     *                      posted.
+     *
+     * @return OK when a buffer transaction was posted successfully.
+     *         NOT_FOUND when the sending connection was not found.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus postSend(ConnectionId receiverId,
+                          const std::shared_ptr<BufferPoolData> &buffer,
+                          TransactionId *transactionId,
+                          int64_t *timestampUs);
+
+    /**
+     *  Time out inactive lingering connections and close.
+     */
+    void cleanUp();
+
+    /** Destructs the manager of buffer pool clients.  */
+    ~ClientManager();
+private:
+    static sp<ClientManager> sInstance;
+    static std::mutex sInstanceLock;
+
+    class Impl;
+    const std::unique_ptr<Impl> mImpl;
+
+    ClientManager();
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp
new file mode 100644
index 0000000..8b44f61
--- /dev/null
+++ b/media/bufferpool/2.0/tests/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+cc_test {
+    name: "VtsVndkHidlBufferpoolV2_0TargetSingleTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "allocator.cpp",
+        "single.cpp",
+    ],
+    static_libs: [
+        "android.hardware.media.bufferpool@2.0",
+        "libcutils",
+        "libstagefright_bufferpool@2.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    compile_multilib: "both",
+}
+
+cc_test {
+    name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "allocator.cpp",
+        "multi.cpp",
+    ],
+    static_libs: [
+        "android.hardware.media.bufferpool@2.0",
+        "libcutils",
+        "libstagefright_bufferpool@2.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    compile_multilib: "both",
+}
diff --git a/media/bufferpool/2.0/tests/OWNERS b/media/bufferpool/2.0/tests/OWNERS
new file mode 100644
index 0000000..6733e0c
--- /dev/null
+++ b/media/bufferpool/2.0/tests/OWNERS
@@ -0,0 +1,9 @@
+# Media team
+lajos@google.com
+pawin@google.com
+taklee@google.com
+wonsik@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp
new file mode 100644
index 0000000..843f7ea
--- /dev/null
+++ b/media/bufferpool/2.0/tests/allocator.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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 <cutils/ashmem.h>
+#include <sys/mman.h>
+#include "allocator.h"
+
+union Params {
+  struct {
+    uint32_t capacity;
+  } data;
+  uint8_t array[0];
+  Params() : data{0} {}
+  Params(uint32_t size)
+      : data{size} {}
+};
+
+
+namespace {
+
+struct HandleAshmem : public native_handle_t {
+  HandleAshmem(int ashmemFd, size_t size)
+    : native_handle_t(cHeader),
+    mFds{ ashmemFd },
+    mInts{ int (size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } {}
+
+  int ashmemFd() const { return mFds.mAshmem; }
+  size_t size() const {
+    return size_t(unsigned(mInts.mSizeLo))
+        | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+  }
+
+  static bool isValid(const native_handle_t * const o);
+
+protected:
+  struct {
+    int mAshmem;
+  } mFds;
+  struct {
+    int mSizeLo;
+    int mSizeHi;
+    int mMagic;
+  } mInts;
+
+private:
+  enum {
+    kMagic = 'ahm\x00',
+    numFds = sizeof(mFds) / sizeof(int),
+    numInts = sizeof(mInts) / sizeof(int),
+    version = sizeof(native_handle_t)
+  };
+  const static native_handle_t cHeader;
+};
+
+const native_handle_t HandleAshmem::cHeader = {
+  HandleAshmem::version,
+  HandleAshmem::numFds,
+  HandleAshmem::numInts,
+  {}
+};
+
+bool HandleAshmem::isValid(const native_handle_t * const o) {
+  if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+    return false;
+  }
+  const HandleAshmem *other = static_cast<const HandleAshmem*>(o);
+  return other->mInts.mMagic == kMagic;
+}
+
+class AllocationAshmem {
+private:
+  AllocationAshmem(int ashmemFd, size_t capacity, bool res)
+    : mHandle(ashmemFd, capacity),
+      mInit(res) {}
+
+public:
+  static AllocationAshmem *Alloc(size_t size) {
+    constexpr static const char *kAllocationTag = "bufferpool_test";
+    int ashmemFd = ashmem_create_region(kAllocationTag, size);
+    return new AllocationAshmem(ashmemFd, size, ashmemFd >= 0);
+  }
+
+  ~AllocationAshmem() {
+    if (mInit) {
+      native_handle_close(&mHandle);
+    }
+  }
+
+  const HandleAshmem *handle() {
+    return &mHandle;
+  }
+
+private:
+  HandleAshmem mHandle;
+  bool mInit;
+  // TODO: mapping and map fd
+};
+
+struct AllocationDtor {
+  AllocationDtor(const std::shared_ptr<AllocationAshmem> &alloc)
+      : mAlloc(alloc) {}
+
+  void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+  const std::shared_ptr<AllocationAshmem> mAlloc;
+};
+
+}
+
+
+ResultStatus TestBufferPoolAllocator::allocate(
+    const std::vector<uint8_t> &params,
+    std::shared_ptr<BufferPoolAllocation> *alloc,
+    size_t *allocSize) {
+  Params ashmemParams;
+  memcpy(&ashmemParams, params.data(), std::min(sizeof(Params), params.size()));
+
+  std::shared_ptr<AllocationAshmem> ashmemAlloc =
+      std::shared_ptr<AllocationAshmem>(
+          AllocationAshmem::Alloc(ashmemParams.data.capacity));
+  if (ashmemAlloc) {
+    BufferPoolAllocation *ptr = new BufferPoolAllocation(ashmemAlloc->handle());
+    if (ptr) {
+      *alloc = std::shared_ptr<BufferPoolAllocation>(ptr, AllocationDtor(ashmemAlloc));
+      if (*alloc) {
+          *allocSize = ashmemParams.data.capacity;
+          return ResultStatus::OK;
+      }
+      delete ptr;
+      return ResultStatus::NO_MEMORY;
+    }
+  }
+  return ResultStatus::CRITICAL_ERROR;
+}
+
+bool TestBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
+                                        const std::vector<uint8_t> &oldParams) {
+  size_t newSize = newParams.size();
+  size_t oldSize = oldParams.size();
+  if (newSize == oldSize) {
+    for (size_t i = 0; i < newSize; ++i) {
+      if (newParams[i] != oldParams[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
+bool TestBufferPoolAllocator::Fill(const native_handle_t *handle, const unsigned char val) {
+  if (!HandleAshmem::isValid(handle)) {
+    return false;
+  }
+  const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+  unsigned char *ptr = (unsigned char *)mmap(
+      NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0);
+
+  if (ptr != MAP_FAILED) {
+    for (size_t i = 0; i < o->size(); ++i) {
+      ptr[i] = val;
+    }
+    munmap(ptr, o->size());
+    return true;
+  }
+  return false;
+}
+
+bool TestBufferPoolAllocator::Verify(const native_handle_t *handle, const unsigned char val) {
+  if (!HandleAshmem::isValid(handle)) {
+    return false;
+  }
+  const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+  unsigned char *ptr = (unsigned char *)mmap(
+      NULL, o->size(), PROT_READ, MAP_SHARED, o->ashmemFd(), 0);
+
+  if (ptr != MAP_FAILED) {
+    bool res = true;
+    for (size_t i = 0; i < o->size(); ++i) {
+      if (ptr[i] != val) {
+        res = false;
+        break;
+      }
+    }
+    munmap(ptr, o->size());
+    return res;
+  }
+  return false;
+}
+
+void getTestAllocatorParams(std::vector<uint8_t> *params) {
+  constexpr static int kAllocationSize = 1024 * 10;
+  Params ashmemParams(kAllocationSize);
+
+  params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams));
+}
diff --git a/media/bufferpool/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h
new file mode 100644
index 0000000..5281dc3
--- /dev/null
+++ b/media/bufferpool/2.0/tests/allocator.h
@@ -0,0 +1,51 @@
+/*
+ * 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 VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
+#define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
+
+#include <bufferpool/BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::
+    BufferPoolAllocation;
+using android::hardware::media::bufferpool::V2_0::implementation::
+    BufferPoolAllocator;
+
+// buffer allocator for the tests
+class TestBufferPoolAllocator : public BufferPoolAllocator {
+ public:
+  TestBufferPoolAllocator() {}
+
+  ~TestBufferPoolAllocator() override {}
+
+  ResultStatus allocate(const std::vector<uint8_t> &params,
+                        std::shared_ptr<BufferPoolAllocation> *alloc,
+                        size_t *allocSize) override;
+
+  bool compatible(const std::vector<uint8_t> &newParams,
+                  const std::vector<uint8_t> &oldParams) override;
+
+  static bool Fill(const native_handle_t *handle, const unsigned char val);
+
+  static bool Verify(const native_handle_t *handle, const unsigned char val);
+
+};
+
+// retrieve buffer allocator paramters
+void getTestAllocatorParams(std::vector<uint8_t> *params);
+
+#endif  // VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp
new file mode 100644
index 0000000..68b6992
--- /dev/null
+++ b/media/bufferpool/2.0/tests/multi.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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_TAG "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V2_0::IClientManager;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+    INIT_OK = 0,
+    INIT_ERROR,
+    SEND,
+    RECEIVE_OK,
+    RECEIVE_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+    struct  {
+        int32_t command;
+        BufferId bufferId;
+        ConnectionId connectionId;
+        TransactionId transactionId;
+        int64_t  timestampUs;
+    } data;
+    char array[0];
+};
+
+// media.bufferpool test setup
+class BufferpoolMultiTest : public ::testing::Test {
+ public:
+  virtual void SetUp() override {
+    ResultStatus status;
+    mReceiverPid = -1;
+    mConnectionValid = false;
+
+    ASSERT_TRUE(pipe(mCommandPipeFds) == 0);
+    ASSERT_TRUE(pipe(mResultPipeFds) == 0);
+
+    mReceiverPid = fork();
+    ASSERT_TRUE(mReceiverPid >= 0);
+
+    if (mReceiverPid == 0) {
+      doReceiver();
+      // In order to ignore gtest behaviour, wait for being killed from
+      // tearDown
+      pause();
+    }
+
+    mManager = ClientManager::getInstance();
+    ASSERT_NE(mManager, nullptr);
+
+    mAllocator = std::make_shared<TestBufferPoolAllocator>();
+    ASSERT_TRUE((bool)mAllocator);
+
+    status = mManager->create(mAllocator, &mConnectionId);
+    ASSERT_TRUE(status == ResultStatus::OK);
+    mConnectionValid = true;
+  }
+
+  virtual void TearDown() override {
+    if (mReceiverPid > 0) {
+      kill(mReceiverPid, SIGKILL);
+      int wstatus;
+      wait(&wstatus);
+    }
+
+    if (mConnectionValid) {
+      mManager->close(mConnectionId);
+    }
+  }
+
+ protected:
+  static void description(const std::string& description) {
+    RecordProperty("description", description);
+  }
+
+  android::sp<ClientManager> mManager;
+  std::shared_ptr<BufferPoolAllocator> mAllocator;
+  bool mConnectionValid;
+  ConnectionId mConnectionId;
+  pid_t mReceiverPid;
+  int mCommandPipeFds[2];
+  int mResultPipeFds[2];
+
+  bool sendMessage(int *pipes, const PipeMessage &message) {
+    int ret = write(pipes[1], message.array, sizeof(PipeMessage));
+    return ret == sizeof(PipeMessage);
+  }
+
+  bool receiveMessage(int *pipes, PipeMessage *message) {
+    int ret = read(pipes[0], message->array, sizeof(PipeMessage));
+    return ret == sizeof(PipeMessage);
+  }
+
+  void doReceiver() {
+    configureRpcThreadpool(1, false);
+    PipeMessage message;
+    mManager = ClientManager::getInstance();
+    if (!mManager) {
+      message.data.command = PipeCommand::INIT_ERROR;
+      sendMessage(mResultPipeFds, message);
+      return;
+    }
+    android::status_t status = mManager->registerAsService();
+    if (status != android::OK) {
+      message.data.command = PipeCommand::INIT_ERROR;
+      sendMessage(mResultPipeFds, message);
+      return;
+    }
+    message.data.command = PipeCommand::INIT_OK;
+    sendMessage(mResultPipeFds, message);
+
+    receiveMessage(mCommandPipeFds, &message);
+    {
+      native_handle_t *rhandle = nullptr;
+      std::shared_ptr<BufferPoolData> rbuffer;
+      ResultStatus status = mManager->receive(
+          message.data.connectionId, message.data.transactionId,
+          message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
+      mManager->close(message.data.connectionId);
+      if (status != ResultStatus::OK) {
+        if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) {
+          message.data.command = PipeCommand::RECEIVE_ERROR;
+          sendMessage(mResultPipeFds, message);
+          return;
+        }
+      }
+    }
+    message.data.command = PipeCommand::RECEIVE_OK;
+    sendMessage(mResultPipeFds, message);
+  }
+};
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolMultiTest, TransferBuffer) {
+  ResultStatus status;
+  PipeMessage message;
+
+  ASSERT_TRUE(receiveMessage(mResultPipeFds, &message));
+
+  android::sp<IClientManager> receiver = IClientManager::getService();
+  ConnectionId receiverId;
+  ASSERT_TRUE((bool)receiver);
+
+  status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+  ASSERT_TRUE(status == ResultStatus::OK);
+  {
+    native_handle_t *shandle = nullptr;
+    std::shared_ptr<BufferPoolData> sbuffer;
+    TransactionId transactionId;
+    int64_t postUs;
+    std::vector<uint8_t> vecParams;
+
+    getTestAllocatorParams(&vecParams);
+    status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer);
+    ASSERT_TRUE(status == ResultStatus::OK);
+
+    ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77));
+
+    status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
+    ASSERT_TRUE(status == ResultStatus::OK);
+
+    message.data.command = PipeCommand::SEND;
+    message.data.bufferId = sbuffer->mId;
+    message.data.connectionId = receiverId;
+    message.data.transactionId = transactionId;
+    message.data.timestampUs = postUs;
+    sendMessage(mCommandPipeFds, message);
+  }
+  EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+  ::testing::InitGoogleTest(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  LOG(INFO) << "Test result = " << status;
+  return status;
+}
diff --git a/media/bufferpool/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp
new file mode 100644
index 0000000..777edcf
--- /dev/null
+++ b/media/bufferpool/2.0/tests/single.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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_TAG "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// Number of iteration for buffer allocation test.
+constexpr static int kNumAllocationTest = 3;
+
+// Number of iteration for buffer recycling test.
+constexpr static int kNumRecycleTest = 3;
+
+// media.bufferpool test setup
+class BufferpoolSingleTest : public ::testing::Test {
+ public:
+  virtual void SetUp() override {
+    ResultStatus status;
+    mConnectionValid = false;
+
+    mManager = ClientManager::getInstance();
+    ASSERT_NE(mManager, nullptr);
+
+    mAllocator = std::make_shared<TestBufferPoolAllocator>();
+    ASSERT_TRUE((bool)mAllocator);
+
+    status = mManager->create(mAllocator, &mConnectionId);
+    ASSERT_TRUE(status == ResultStatus::OK);
+
+    mConnectionValid = true;
+
+    status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
+    ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS &&
+                mReceiverId == mConnectionId);
+  }
+
+  virtual void TearDown() override {
+    if (mConnectionValid) {
+      mManager->close(mConnectionId);
+    }
+  }
+
+ protected:
+  static void description(const std::string& description) {
+    RecordProperty("description", description);
+  }
+
+  android::sp<ClientManager> mManager;
+  std::shared_ptr<BufferPoolAllocator> mAllocator;
+  bool mConnectionValid;
+  ConnectionId mConnectionId;
+  ConnectionId mReceiverId;
+
+};
+
+// Buffer allocation test.
+// Check whether each buffer allocation is done successfully with
+// unique buffer id.
+TEST_F(BufferpoolSingleTest, AllocateBuffer) {
+  ResultStatus status;
+  std::vector<uint8_t> vecParams;
+  getTestAllocatorParams(&vecParams);
+
+  std::shared_ptr<BufferPoolData> buffer[kNumAllocationTest];
+  native_handle_t *allocHandle = nullptr;
+  for (int i = 0; i < kNumAllocationTest; ++i) {
+    status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]);
+    ASSERT_TRUE(status == ResultStatus::OK);
+  }
+  for (int i = 0; i < kNumAllocationTest; ++i) {
+    for (int j = i + 1; j < kNumAllocationTest; ++j) {
+      ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId);
+    }
+  }
+  EXPECT_TRUE(kNumAllocationTest > 1);
+}
+
+// Buffer recycle test.
+// Check whether de-allocated buffers are recycled.
+TEST_F(BufferpoolSingleTest, RecycleBuffer) {
+  ResultStatus status;
+  std::vector<uint8_t> vecParams;
+  getTestAllocatorParams(&vecParams);
+
+  BufferId bid[kNumRecycleTest];
+  for (int i = 0; i < kNumRecycleTest; ++i) {
+    std::shared_ptr<BufferPoolData> buffer;
+    native_handle_t *allocHandle = nullptr;
+    status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer);
+    ASSERT_TRUE(status == ResultStatus::OK);
+    bid[i] = buffer->mId;
+  }
+  for (int i = 1; i < kNumRecycleTest; ++i) {
+    ASSERT_TRUE(bid[i - 1] == bid[i]);
+  }
+  EXPECT_TRUE(kNumRecycleTest > 1);
+}
+
+// Buffer transfer test.
+// Check whether buffer is transferred to another client successfully.
+TEST_F(BufferpoolSingleTest, TransferBuffer) {
+  ResultStatus status;
+  std::vector<uint8_t> vecParams;
+  getTestAllocatorParams(&vecParams);
+  std::shared_ptr<BufferPoolData> sbuffer, rbuffer;
+  native_handle_t *allocHandle = nullptr;
+  native_handle_t *recvHandle = nullptr;
+
+  TransactionId transactionId;
+  int64_t postUs;
+
+  status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer);
+  ASSERT_TRUE(status == ResultStatus::OK);
+  ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77));
+  status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs);
+  ASSERT_TRUE(status == ResultStatus::OK);
+  status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs,
+                             &recvHandle, &rbuffer);
+  EXPECT_TRUE(status == ResultStatus::OK);
+  ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77));
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  LOG(INFO) << "Test result = " << status;
+  return status;
+}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index c02d2fb..f52d451 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -393,7 +393,9 @@
     }
     mPssh.clear();
 
-    delete mCachedSource;
+    if (mCachedSource != mDataSource) {
+        delete mCachedSource;
+    }
     delete mDataSource;
 }
 
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index b9e28a0..4090286 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -6,6 +6,7 @@
         "client",
         "core",
         "fifo",
+        "flowgraph",
         "legacy",
         "utility",
     ],
@@ -25,9 +26,9 @@
         "utility/FixedBlockAdapter.cpp",
         "utility/FixedBlockReader.cpp",
         "utility/FixedBlockWriter.cpp",
-        "utility/LinearRamp.cpp",
         "fifo/FifoBuffer.cpp",
         "fifo/FifoControllerBase.cpp",
+        "client/AAudioFlowGraph.cpp",
         "client/AudioEndpoint.cpp",
         "client/AudioStreamInternal.cpp",
         "client/AudioStreamInternalCapture.cpp",
@@ -42,6 +43,16 @@
         "binding/RingBufferParcelable.cpp",
         "binding/SharedMemoryParcelable.cpp",
         "binding/SharedRegionParcelable.cpp",
+        "flowgraph/AudioProcessorBase.cpp",
+        "flowgraph/ClipToRange.cpp",
+        "flowgraph/MonoToMultiConverter.cpp",
+        "flowgraph/RampLinear.cpp",
+        "flowgraph/SinkFloat.cpp",
+        "flowgraph/SinkI16.cpp",
+        "flowgraph/SinkI24.cpp",
+        "flowgraph/SourceFloat.cpp",
+        "flowgraph/SourceI16.cpp",
+        "flowgraph/SourceI24.cpp",
     ],
 
     cflags: [
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index 959db61..3d1bc9b 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "AAudioStreamConfiguration"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
 #include <stdint.h>
 
 #include <sys/mman.h>
@@ -36,6 +40,7 @@
 
 status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const {
     status_t status;
+
     status = parcel->writeInt32(getDeviceId());
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32(getSampleRate());
@@ -46,6 +51,7 @@
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32((int32_t) getFormat());
     if (status != NO_ERROR) goto error;
+
     status = parcel->writeInt32((int32_t) getDirection());
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32(getBufferCapacity());
@@ -60,7 +66,7 @@
     if (status != NO_ERROR) goto error;
     return NO_ERROR;
 error:
-    ALOGE("AAudioStreamConfiguration.writeToParcel(): write failed = %d", status);
+    ALOGE("%s(): write failed = %d", __func__, status);
     return status;
 }
 
@@ -80,7 +86,8 @@
     setSharingMode((aaudio_sharing_mode_t) value);
     status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
-    setFormat((aaudio_format_t) value);
+    setFormat((audio_format_t) value);
+
     status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
     setDirection((aaudio_direction_t) value);
@@ -99,8 +106,9 @@
     status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
     setSessionId(value);
+
     return NO_ERROR;
 error:
-    ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status);
+    ALOGE("%s(): read failed = %d", __func__, status);
     return status;
 }
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index 0b0cf77..67955e8 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -43,7 +43,7 @@
 
 void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) {
     mFd.reset(dup(fd.get())); // store a duplicate fd
-    ALOGV("setup(%d -> %d, %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this);
+    ALOGV("setup(fd = %d -> %d, size = %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this);
     mSizeInBytes = sizeInBytes;
 }
 
@@ -104,7 +104,8 @@
     mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE,
                                         MAP_SHARED, fd.get(), 0);
     if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
-        ALOGE("mmap() failed for fd = %d, errno = %s", fd.get(), strerror(errno));
+        ALOGE("mmap() failed for fd = %d, nBytes = %d, errno = %s",
+              fd.get(), mSizeInBytes, strerror(errno));
         return AAUDIO_ERROR_INTERNAL;
     }
     return AAUDIO_OK;
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 82c2240..4ec38c5 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -70,8 +70,8 @@
     aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
 
     android::base::unique_fd   mFd;
-    int32_t     mSizeInBytes = 0;
-    uint8_t    *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
+    int32_t                    mSizeInBytes = 0;
+    uint8_t                   *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
 
 private:
 
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
new file mode 100644
index 0000000..3e43c6b
--- /dev/null
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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_TAG "AAudioFlowGraph"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "AAudioFlowGraph.h"
+
+#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/RampLinear.h>
+#include <flowgraph/SinkFloat.h>
+#include <flowgraph/SinkI16.h>
+#include <flowgraph/SinkI24.h>
+#include <flowgraph/SourceFloat.h>
+#include <flowgraph/SourceI16.h>
+#include <flowgraph/SourceI24.h>
+
+using namespace flowgraph;
+
+aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat,
+                          int32_t sourceChannelCount,
+                          audio_format_t sinkFormat,
+                          int32_t sinkChannelCount) {
+    AudioFloatOutputPort *lastOutput = nullptr;
+
+    ALOGD("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
+          __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);
+
+    switch (sourceFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            mSource = std::make_unique<SourceFloat>(sourceChannelCount);
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            mSource = std::make_unique<SourceI16>(sourceChannelCount);
+            break;
+        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+            mSource = std::make_unique<SourceI24>(sourceChannelCount);
+            break;
+        default: // TODO add I32
+            ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
+            return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+    lastOutput = &mSource->output;
+
+    // Apply volume as a ramp to avoid pops.
+    mVolumeRamp = std::make_unique<RampLinear>(sourceChannelCount);
+    lastOutput->connect(&mVolumeRamp->input);
+    lastOutput = &mVolumeRamp->output;
+
+    // For a pure float graph, there is chance that the data range may be very large.
+    // So we should clip to a reasonable value that allows a little headroom.
+    if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT && sinkFormat == AUDIO_FORMAT_PCM_FLOAT) {
+        mClipper = std::make_unique<ClipToRange>(sourceChannelCount);
+        lastOutput->connect(&mClipper->input);
+        lastOutput = &mClipper->output;
+    }
+
+    // Expand the number of channels if required.
+    if (sourceChannelCount == 1 && sinkChannelCount > 1) {
+        mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
+        lastOutput->connect(&mChannelConverter->input);
+        lastOutput = &mChannelConverter->output;
+    } else if (sourceChannelCount != sinkChannelCount) {
+        ALOGE("%s() Channel reduction not supported.", __func__);
+        return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+
+    switch (sinkFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            mSink = std::make_unique<SinkFloat>(sinkChannelCount);
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            mSink = std::make_unique<SinkI16>(sinkChannelCount);
+            break;
+        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+            mSink = std::make_unique<SinkI24>(sinkChannelCount);
+            break;
+        default: // TODO add I32
+            ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
+            return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+    lastOutput->connect(&mSink->input);
+
+    return AAUDIO_OK;
+}
+
+void AAudioFlowGraph::process(const void *source, void *destination, int32_t numFrames) {
+    mSource->setData(source, numFrames);
+    mSink->read(destination, numFrames);
+}
+
+/**
+ * @param volume between 0.0 and 1.0
+ */
+void AAudioFlowGraph::setTargetVolume(float volume) {
+    mVolumeRamp->setTarget(volume);
+}
+
+void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) {
+    mVolumeRamp->setLengthInFrames(numFrames);
+}
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
new file mode 100644
index 0000000..a49f64e
--- /dev/null
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -0,0 +1,64 @@
+/*
+ * 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_AAUDIO_FLOW_GRAPH_H
+#define ANDROID_AAUDIO_FLOW_GRAPH_H
+
+#include <memory>
+#include <stdint.h>
+#include <sys/types.h>
+#include <system/audio.h>
+
+#include <aaudio/AAudio.h>
+#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/RampLinear.h>
+
+class AAudioFlowGraph {
+public:
+    /** Connect several modules together to convert from source to sink.
+     * This should only be called once for each instance.
+     *
+     * @param sourceFormat
+     * @param sourceChannelCount
+     * @param sinkFormat
+     * @param sinkChannelCount
+     * @return
+     */
+    aaudio_result_t configure(audio_format_t sourceFormat,
+                              int32_t sourceChannelCount,
+                              audio_format_t sinkFormat,
+                              int32_t sinkChannelCount);
+
+    void process(const void *source, void *destination, int32_t numFrames);
+
+    /**
+     * @param volume between 0.0 and 1.0
+     */
+    void setTargetVolume(float volume);
+
+    void setRampLengthInFrames(int32_t numFrames);
+
+private:
+    std::unique_ptr<flowgraph::AudioSource>          mSource;
+    std::unique_ptr<flowgraph::RampLinear>           mVolumeRamp;
+    std::unique_ptr<flowgraph::ClipToRange>          mClipper;
+    std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter;
+    std::unique_ptr<flowgraph::AudioSink>            mSink;
+};
+
+
+#endif //ANDROID_AAUDIO_FLOW_GRAPH_H
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 9204824..0a8021a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -39,7 +39,6 @@
 #include "core/AudioStreamBuilder.h"
 #include "fifo/FifoBuffer.h"
 #include "utility/AudioClock.h"
-#include "utility/LinearRamp.h"
 
 #include "AudioStreamInternal.h"
 
@@ -92,11 +91,11 @@
     }
 
     // We have to do volume scaling. So we prefer FLOAT format.
-    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
-        setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+        setFormat(AUDIO_FORMAT_PCM_FLOAT);
     }
     // Request FLOAT for the shared mixer.
-    request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT);
 
     // Build the request to send to the server.
     request.setUserId(getuid());
@@ -126,7 +125,7 @@
         // if that failed then try switching from mono to stereo if OUTPUT.
         // Only do this in the client. Otherwise we end up with a mono mixer in the service
         // that writes to a stereo MMAP stream.
-        ALOGD("%s - openStream() returned %d, try switching from MONO to STEREO",
+        ALOGD("%s() - openStream() returned %d, try switching from MONO to STEREO",
               __func__, mServiceStreamHandle);
         request.getConfiguration().setSamplesPerFrame(2); // stereo
         mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
@@ -212,9 +211,7 @@
             mCallbackFrames = mFramesPerBurst;
         }
 
-        int32_t bytesPerFrame = getSamplesPerFrame()
-                                * AAudioConvert_formatToSizeInBytes(getFormat());
-        int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame;
+        const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame();
         mCallbackBuffer = new uint8_t[callbackBufferSize];
     }
 
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 0425cd5..3bb9e1e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -27,7 +27,6 @@
 #include "client/AudioEndpoint.h"
 #include "core/AudioStream.h"
 #include "utility/AudioClock.h"
-#include "utility/LinearRamp.h"
 
 using android::sp;
 using android::IAAudioService;
@@ -193,6 +192,8 @@
 
     int64_t                  mServiceLatencyNanos = 0;
 
+    // Sometimes the hardware is operating with a different channel count from the app.
+    // Then we require conversion in AAudio.
     int32_t                  mDeviceChannelCount = 0;
 };
 
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 0719fe1..4a0e6da 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -20,6 +20,7 @@
 #include <utils/Log.h>
 
 #include <algorithm>
+#include <audio_utils/primitives.h>
 #include <aaudio/AAudio.h>
 
 #include "client/AudioStreamInternalCapture.h"
@@ -165,35 +166,36 @@
     // Read data in one or two parts.
     for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
         int32_t framesToProcess = framesLeft;
-        int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
+        const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
         if (framesAvailable <= 0) break;
 
         if (framesToProcess > framesAvailable) {
             framesToProcess = framesAvailable;
         }
 
-        int32_t numBytes = getBytesPerFrame() * framesToProcess;
-        int32_t numSamples = framesToProcess * getSamplesPerFrame();
+        const int32_t numBytes = getBytesPerFrame() * framesToProcess;
+        const int32_t numSamples = framesToProcess * getSamplesPerFrame();
 
+        const audio_format_t sourceFormat = getDeviceFormat();
+        const audio_format_t destinationFormat = getFormat();
         // TODO factor this out into a utility function
-        if (getDeviceFormat() == getFormat()) {
+        if (sourceFormat == destinationFormat) {
             memcpy(destination, wrappingBuffer.data[partIndex], numBytes);
-        } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
-                   && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
-            AAudioConvert_pcm16ToFloat(
-                    (const int16_t *) wrappingBuffer.data[partIndex],
+        } else if (sourceFormat == AUDIO_FORMAT_PCM_16_BIT
+                   && destinationFormat == AUDIO_FORMAT_PCM_FLOAT) {
+            memcpy_to_float_from_i16(
                     (float *) destination,
-                    numSamples,
-                    1.0f);
-        } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_FLOAT
-                   && getFormat() == AAUDIO_FORMAT_PCM_I16) {
-            AAudioConvert_floatToPcm16(
-                    (const float *) wrappingBuffer.data[partIndex],
+                    (const int16_t *) wrappingBuffer.data[partIndex],
+                    numSamples);
+        } else if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT
+                   && destinationFormat == AUDIO_FORMAT_PCM_16_BIT) {
+            memcpy_to_i16_from_float(
                     (int16_t *) destination,
-                    numSamples,
-                    1.0f);
+                    (const float *) wrappingBuffer.data[partIndex],
+                    numSamples);
         } else {
-            ALOGE("Format conversion not supported!");
+            ALOGE("%s() - Format conversion not supported! audio_format_t source = %u, dest = %u",
+                __func__, sourceFormat, destinationFormat);
             return AAUDIO_ERROR_INVALID_FORMAT;
         }
         destination += numBytes;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 795ba2c..2ae37a5 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -43,9 +43,17 @@
 aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) {
     aaudio_result_t result = AudioStreamInternal::open(builder);
     if (result == AAUDIO_OK) {
+        result = mFlowGraph.configure(getFormat(),
+                             getSamplesPerFrame(),
+                             getDeviceFormat(),
+                             getDeviceChannelCount());
+
+        if (result != AAUDIO_OK) {
+            close();
+        }
         // Sample rate is constrained to common values by now and should not overflow.
         int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
-        mVolumeRamp.setLengthInFrames(numFrames);
+        mFlowGraph.setRampLengthInFrames(numFrames);
     }
     return result;
 }
@@ -216,22 +224,10 @@
             }
 
             int32_t numBytes = getBytesPerFrame() * framesToWrite;
-            // Data conversion.
-            float levelFrom;
-            float levelTo;
-            mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
 
-            AAudioDataConverter::FormattedData source(
-                    (void *)byteBuffer,
-                    getFormat(),
-                    getSamplesPerFrame());
-            AAudioDataConverter::FormattedData destination(
-                    wrappingBuffer.data[partIndex],
-                    getDeviceFormat(),
-                    getDeviceChannelCount());
-
-            AAudioDataConverter::convert(source, destination, framesToWrite,
-                                         levelFrom, levelTo);
+            mFlowGraph.process((void *)byteBuffer,
+                               wrappingBuffer.data[partIndex],
+                               framesToWrite);
 
             byteBuffer += numBytes;
             framesLeft -= framesToWrite;
@@ -313,6 +309,6 @@
     float combinedVolume = mStreamVolume * getDuckAndMuteVolume();
     ALOGD("%s() mStreamVolume * duckAndMuteVolume = %f * %f = %f",
           __func__, mStreamVolume, getDuckAndMuteVolume(), combinedVolume);
-    mVolumeRamp.setTarget(combinedVolume);
+    mFlowGraph.setTargetVolume(combinedVolume);
     return android::NO_ERROR;
 }
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index 977a909..cab2942 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -21,6 +21,7 @@
 #include <aaudio/AAudio.h>
 
 #include "binding/AAudioServiceInterface.h"
+#include "client/AAudioFlowGraph.h"
 #include "client/AudioStreamInternal.h"
 
 using android::sp;
@@ -93,7 +94,7 @@
 
     int64_t                  mLastFramesRead = 0; // used to prevent retrograde motion
 
-    LinearRamp               mVolumeRamp;
+    AAudioFlowGraph          mFlowGraph;
 
 };
 
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index df0db79..8dc31d0 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -167,7 +167,9 @@
                                                    aaudio_format_t format)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
-    streamBuilder->setFormat(format);
+    // Use audio_format_t everywhere internally.
+    const audio_format_t internalFormat = AAudioConvert_aaudioToAndroidDataFormat(format);
+    streamBuilder->setFormat(internalFormat);
 }
 
 AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder,
@@ -408,7 +410,9 @@
 AAUDIO_API aaudio_format_t AAudioStream_getFormat(AAudioStream* stream)
 {
     AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
-    return audioStream->getFormat();
+    // Use audio_format_t internally.
+    audio_format_t internalFormat = audioStream->getFormat();
+    return AAudioConvert_androidToAAudioDataFormat(internalFormat);
 }
 
 AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(AAudioStream* stream,
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index d56701b..bd42697 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -48,6 +48,20 @@
     mInputPreset     = other.mInputPreset;
 }
 
+static aaudio_result_t isFormatValid(audio_format_t format) {
+    switch (format) {
+        case AUDIO_FORMAT_DEFAULT:
+        case AUDIO_FORMAT_PCM_16_BIT:
+        case AUDIO_FORMAT_PCM_FLOAT:
+            break; // valid
+        default:
+            ALOGE("audioFormat not valid, audio_format_t = 0x%08x", format);
+            return AAUDIO_ERROR_INVALID_FORMAT;
+            // break;
+    }
+    return AAUDIO_OK;
+}
+
 aaudio_result_t AAudioStreamParameters::validate() const {
     if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
         && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
@@ -79,16 +93,8 @@
             // break;
     }
 
-    switch (mAudioFormat) {
-        case AAUDIO_FORMAT_UNSPECIFIED:
-        case AAUDIO_FORMAT_PCM_I16:
-        case AAUDIO_FORMAT_PCM_FLOAT:
-            break; // valid
-        default:
-            ALOGE("audioFormat not valid = %d", mAudioFormat);
-            return AAUDIO_ERROR_INVALID_FORMAT;
-            // break;
-    }
+    aaudio_result_t result = isFormatValid (mAudioFormat);
+    if (result != AAUDIO_OK) return result;
 
     if (mSampleRate != AAUDIO_UNSPECIFIED
         && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index ce5dacd..6beb4b2 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -56,11 +56,11 @@
         mSamplesPerFrame = samplesPerFrame;
     }
 
-    aaudio_format_t getFormat() const {
+    audio_format_t getFormat() const {
         return mAudioFormat;
     }
 
-    void setFormat(aaudio_format_t audioFormat) {
+    void setFormat(audio_format_t audioFormat) {
         mAudioFormat = audioFormat;
     }
 
@@ -120,8 +120,11 @@
         mSessionId = sessionId;
     }
 
+    /**
+     * @return bytes per frame of getFormat()
+     */
     int32_t calculateBytesPerFrame() const {
-        return getSamplesPerFrame() * AAudioConvert_formatToSizeInBytes(getFormat());
+        return getSamplesPerFrame() * audio_bytes_per_sample(getFormat());
     }
 
     /**
@@ -139,7 +142,7 @@
     int32_t                    mSampleRate      = AAUDIO_UNSPECIFIED;
     int32_t                    mDeviceId        = AAUDIO_UNSPECIFIED;
     aaudio_sharing_mode_t      mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
-    aaudio_format_t            mAudioFormat     = AAUDIO_FORMAT_UNSPECIFIED;
+    audio_format_t             mAudioFormat     = AUDIO_FORMAT_DEFAULT;
     aaudio_direction_t         mDirection       = AAUDIO_DIRECTION_OUTPUT;
     aaudio_usage_t             mUsage           = AAUDIO_UNSPECIFIED;
     aaudio_content_type_t      mContentType     = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 31b895c..60200b2 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -192,7 +192,7 @@
         return mSampleRate;
     }
 
-    aaudio_format_t getFormat()  const {
+    audio_format_t getFormat()  const {
         return mFormat;
     }
 
@@ -249,21 +249,14 @@
      * This is only valid after setFormat() has been called.
      */
     int32_t getBytesPerSample() const {
-        return AAudioConvert_formatToSizeInBytes(mFormat);
+        return audio_bytes_per_sample(mFormat);
     }
 
     /**
      * This is only valid after setSamplesPerFrame() and setDeviceFormat() have been called.
      */
     int32_t getBytesPerDeviceFrame() const {
-        return mSamplesPerFrame * getBytesPerDeviceSample();
-    }
-
-    /**
-     * This is only valid after setDeviceFormat() has been called.
-     */
-    int32_t getBytesPerDeviceSample() const {
-        return AAudioConvert_formatToSizeInBytes(getDeviceFormat());
+        return getSamplesPerFrame() * audio_bytes_per_sample(getDeviceFormat());
     }
 
     virtual int64_t getFramesWritten() = 0;
@@ -478,18 +471,18 @@
     /**
      * This should not be called after the open() call.
      */
-    void setFormat(aaudio_format_t format) {
+    void setFormat(audio_format_t format) {
         mFormat = format;
     }
 
     /**
      * This should not be called after the open() call.
      */
-    void setDeviceFormat(aaudio_format_t format) {
+    void setDeviceFormat(audio_format_t format) {
         mDeviceFormat = format;
     }
 
-    aaudio_format_t getDeviceFormat() const {
+    audio_format_t getDeviceFormat() const {
         return mDeviceFormat;
     }
 
@@ -565,7 +558,7 @@
     int32_t                     mDeviceId = AAUDIO_UNSPECIFIED;
     aaudio_sharing_mode_t       mSharingMode = AAUDIO_SHARING_MODE_SHARED;
     bool                        mSharingModeMatchRequired = false; // must match sharing mode requested
-    aaudio_format_t             mFormat = AAUDIO_FORMAT_UNSPECIFIED;
+    audio_format_t              mFormat = AUDIO_FORMAT_DEFAULT;
     aaudio_stream_state_t       mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
     aaudio_performance_mode_t   mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
@@ -577,7 +570,7 @@
 
     // Sometimes the hardware is operating with a different format from the app.
     // Then we require conversion in AAudio.
-    aaudio_format_t             mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+    audio_format_t              mDeviceFormat = AUDIO_FORMAT_INVALID;
 
     // callback ----------------------------------
 
diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp
index 14a2be1..9885cb0 100644
--- a/media/libaaudio/src/fifo/FifoControllerBase.cpp
+++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp
@@ -59,5 +59,10 @@
 }
 
 void FifoControllerBase::setThreshold(fifo_frames_t threshold) {
+    if (threshold > mCapacity) {
+        threshold = mCapacity;
+    } else if (threshold < 0) {
+        threshold = 0;
+    }
     mThreshold = threshold;
 }
diff --git a/media/libaaudio/src/fifo/FifoControllerBase.h b/media/libaaudio/src/fifo/FifoControllerBase.h
index 64af777..1edb8a3 100644
--- a/media/libaaudio/src/fifo/FifoControllerBase.h
+++ b/media/libaaudio/src/fifo/FifoControllerBase.h
@@ -102,6 +102,9 @@
     /**
      * You can request that the buffer not be filled above a maximum
      * number of frames.
+     *
+     * The threshold will be clipped between zero and the buffer capacity.
+     *
      * @param threshold effective size of the buffer
      */
     void setThreshold(fifo_frames_t threshold);
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp
new file mode 100644
index 0000000..5667fdb
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 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 <algorithm>
+#include <sys/types.h>
+#include "AudioProcessorBase.h"
+
+using namespace flowgraph;
+
+/***************************************************************************/
+int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) {
+    if (framePosition > mLastFramePosition) {
+        mLastFramePosition = framePosition;
+        mFramesValid = onProcess(framePosition, numFrames);
+    }
+    return mFramesValid;
+}
+
+/***************************************************************************/
+AudioFloatBlockPort::AudioFloatBlockPort(AudioProcessorBase &parent,
+                               int32_t samplesPerFrame,
+                               int32_t framesPerBlock)
+        : AudioPort(parent, samplesPerFrame)
+        , mFramesPerBlock(framesPerBlock)
+        , mSampleBlock(NULL) {
+    int32_t numFloats = framesPerBlock * getSamplesPerFrame();
+    mSampleBlock = new float[numFloats]{0.0f};
+}
+
+AudioFloatBlockPort::~AudioFloatBlockPort() {
+    delete[] mSampleBlock;
+}
+
+/***************************************************************************/
+int32_t AudioFloatOutputPort::pullData(int64_t framePosition, int32_t numFrames) {
+    numFrames = std::min(getFramesPerBlock(), numFrames);
+    return mParent.pullData(framePosition, numFrames);
+}
+
+// These need to be in the .cpp file because of forward cross references.
+void AudioFloatOutputPort::connect(AudioFloatInputPort *port) {
+    port->connect(this);
+}
+
+void AudioFloatOutputPort::disconnect(AudioFloatInputPort *port) {
+    port->disconnect(this);
+}
+
+/***************************************************************************/
+int32_t AudioFloatInputPort::pullData(int64_t framePosition, int32_t numFrames) {
+    return (mConnected == NULL)
+            ? std::min(getFramesPerBlock(), numFrames)
+            : mConnected->pullData(framePosition, numFrames);
+}
+
+float *AudioFloatInputPort::getBlock() {
+    if (mConnected == NULL) {
+        return AudioFloatBlockPort::getBlock(); // loaded using setValue()
+    } else {
+        return mConnected->getBlock();
+    }
+}
+
+/***************************************************************************/
+int32_t AudioSink::pull(int32_t numFrames) {
+    int32_t actualFrames = input.pullData(mFramePosition, numFrames);
+    mFramePosition += actualFrames;
+    return actualFrames;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
new file mode 100644
index 0000000..eda46ae
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/*
+ * AudioProcessorBase.h
+ *
+ * Audio processing node and ports that can be used in a simple data flow graph.
+ */
+
+#ifndef FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
+#define FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
+
+#include <cassert>
+#include <cstring>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+// TODO consider publishing all header files under "include/libaaudio/FlowGraph.h"
+
+namespace flowgraph {
+
+// Default block size that can be overridden when the AudioFloatBlockPort is created.
+// If it is too small then we will have too much overhead from switching between nodes.
+// If it is too high then we will thrash the caches.
+constexpr int kDefaultBlockSize = 8; // arbitrary
+
+class AudioFloatInputPort;
+
+/***************************************************************************/
+class AudioProcessorBase {
+public:
+    virtual ~AudioProcessorBase() = default;
+
+    /**
+     * Perform custom function.
+     *
+     * @param framePosition index of first frame to be processed
+     * @param numFrames maximum number of frames requested for processing
+     * @return number of frames actually processed
+     */
+    virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0;
+
+    /**
+     * If the framePosition is at or after the last frame position then call onProcess().
+     * This prevents infinite recursion in case of cyclic graphs.
+     * It also prevents nodes upstream from a branch from being executed twice.
+     *
+     * @param framePosition
+     * @param numFrames
+     * @return
+     */
+    int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+protected:
+    int64_t  mLastFramePosition = -1; // Start at -1 so that the first pull works.
+
+private:
+    int32_t  mFramesValid = 0; // num valid frames in the block
+};
+
+/***************************************************************************/
+/**
+  * This is a connector that allows data to flow between modules.
+  */
+class AudioPort {
+public:
+    AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+            : mParent(parent)
+            , mSamplesPerFrame(samplesPerFrame) {
+    }
+
+    // Ports are often declared public. So let's make them non-copyable.
+    AudioPort(const AudioPort&) = delete;
+    AudioPort& operator=(const AudioPort&) = delete;
+
+    int32_t getSamplesPerFrame() const {
+        return mSamplesPerFrame;
+    }
+
+protected:
+    AudioProcessorBase &mParent;
+
+private:
+    const int32_t    mSamplesPerFrame = 1;
+};
+
+/***************************************************************************/
+/**
+ * This port contains a float type buffer.
+ * The size is framesPerBlock * samplesPerFrame).
+ */
+class AudioFloatBlockPort  : public AudioPort {
+public:
+    AudioFloatBlockPort(AudioProcessorBase &mParent,
+                   int32_t samplesPerFrame,
+                   int32_t framesPerBlock = kDefaultBlockSize
+                );
+
+    virtual ~AudioFloatBlockPort();
+
+    int32_t getFramesPerBlock() const {
+        return mFramesPerBlock;
+    }
+
+protected:
+
+    /**
+     * @return buffer internal to the port or from a connected port
+     */
+    virtual float *getBlock() {
+        return mSampleBlock;
+    }
+
+
+private:
+    const int32_t    mFramesPerBlock = 1;
+    float           *mSampleBlock = nullptr; // allocated in constructor
+};
+
+/***************************************************************************/
+/**
+  * The results of a module are stored in the buffer of the output ports.
+  */
+class AudioFloatOutputPort : public AudioFloatBlockPort {
+public:
+    AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+            : AudioFloatBlockPort(parent, samplesPerFrame) {
+    }
+
+    virtual ~AudioFloatOutputPort() = default;
+
+    using AudioFloatBlockPort::getBlock;
+
+    /**
+     * Call the parent module's onProcess() method.
+     * That may pull data from its inputs and recursively
+     * process the entire graph.
+     * @return number of frames actually pulled
+     */
+    int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+    /**
+     * Connect to the input of another module.
+     * An input port can only have one connection.
+     * An output port can have multiple connections.
+     * If you connect a second output port to an input port
+     * then it overwrites the previous connection.
+     *
+     * This not thread safe. Do not modify the graph topology form another thread while running.
+     */
+    void connect(AudioFloatInputPort *port);
+
+    /**
+     * Disconnect from the input of another module.
+     * This not thread safe.
+     */
+    void disconnect(AudioFloatInputPort *port);
+};
+
+/***************************************************************************/
+class AudioFloatInputPort : public AudioFloatBlockPort {
+public:
+    AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+            : AudioFloatBlockPort(parent, samplesPerFrame) {
+    }
+
+    virtual ~AudioFloatInputPort() = default;
+
+    /**
+     * If connected to an output port then this will return
+     * that output ports buffers.
+     * If not connected then it returns the input ports own buffer
+     * which can be loaded using setValue().
+     */
+    float *getBlock() override;
+
+    /**
+     * Pull data from any output port that is connected.
+     */
+    int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+    /**
+     * Write every value of the float buffer.
+     * This value will be ignored if an output port is connected
+     * to this port.
+     */
+    void setValue(float value) {
+        int numFloats = kDefaultBlockSize * getSamplesPerFrame();
+        float *buffer = getBlock();
+        for (int i = 0; i < numFloats; i++) {
+            *buffer++ = value;
+        }
+    }
+
+    /**
+     * Connect to the output of another module.
+     * An input port can only have one connection.
+     * An output port can have multiple connections.
+     * This not thread safe.
+     */
+    void connect(AudioFloatOutputPort *port) {
+        assert(getSamplesPerFrame() == port->getSamplesPerFrame());
+        mConnected = port;
+    }
+
+    void disconnect(AudioFloatOutputPort *port) {
+        assert(mConnected == port);
+        (void) port;
+        mConnected = nullptr;
+    }
+
+    void disconnect() {
+        mConnected = nullptr;
+    }
+
+private:
+    AudioFloatOutputPort *mConnected = nullptr;
+};
+
+/***************************************************************************/
+class AudioSource : public AudioProcessorBase {
+public:
+    explicit AudioSource(int32_t channelCount)
+            : output(*this, channelCount) {
+    }
+
+    virtual ~AudioSource() = default;
+
+    AudioFloatOutputPort output;
+
+    void setData(const void *data, int32_t numFrames) {
+        mData = data;
+        mSizeInFrames = numFrames;
+        mFrameIndex = 0;
+    }
+
+protected:
+    const void *mData = nullptr;
+    int32_t     mSizeInFrames = 0; // number of frames in mData
+    int32_t     mFrameIndex = 0; // index of next frame to be processed
+};
+
+/***************************************************************************/
+class AudioSink : public AudioProcessorBase {
+public:
+    explicit AudioSink(int32_t channelCount)
+            : input(*this, channelCount) {
+    }
+
+    virtual ~AudioSink() = default;
+
+    AudioFloatInputPort input;
+
+    /**
+     * Dummy processor. The work happens in the read() method.
+     *
+     * @param framePosition index of first frame to be processed
+     * @param numFrames
+     * @return number of frames actually processed
+     */
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override {
+        (void) framePosition;
+        (void) numFrames;
+        return 0;
+    };
+
+    virtual int32_t read(void *data, int32_t numFrames) = 0;
+
+protected:
+    int32_t pull(int32_t numFrames);
+
+private:
+    int64_t mFramePosition = 0;
+};
+
+} /* namespace flowgraph */
+
+#endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */
diff --git a/media/libaaudio/src/flowgraph/ClipToRange.cpp b/media/libaaudio/src/flowgraph/ClipToRange.cpp
new file mode 100644
index 0000000..bd9c22a
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/ClipToRange.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 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 <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "ClipToRange.h"
+
+using namespace flowgraph;
+
+ClipToRange::ClipToRange(int32_t channelCount)
+        : input(*this, channelCount)
+        , output(*this, channelCount) {
+}
+
+int32_t ClipToRange::onProcess(int64_t framePosition, int32_t numFrames) {
+    int32_t framesToProcess = input.pullData(framePosition, numFrames);
+    const float *inputBuffer = input.getBlock();
+    float *outputBuffer = output.getBlock();
+
+    int32_t numSamples = framesToProcess * output.getSamplesPerFrame();
+    for (int32_t i = 0; i < numSamples; i++) {
+        *outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++));
+    }
+
+    return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/ClipToRange.h b/media/libaaudio/src/flowgraph/ClipToRange.h
new file mode 100644
index 0000000..9eef254
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/ClipToRange.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 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 FLOWGRAPH_CLIP_TO_RANGE_H
+#define FLOWGRAPH_CLIP_TO_RANGE_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
+// It is designed to allow occasional transient peaks.
+constexpr float kDefaultMaxHeadroom = 1.41253754f;
+constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom;
+
+class ClipToRange : public AudioProcessorBase {
+public:
+    explicit ClipToRange(int32_t channelCount);
+
+    virtual ~ClipToRange() = default;
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+    void setMinimum(float min) {
+        mMinimum = min;
+    }
+
+    float getMinimum() const {
+        return mMinimum;
+    }
+
+    void setMaximum(float min) {
+        mMaximum = min;
+    }
+
+    float getMaximum() const {
+        return mMaximum;
+    }
+
+    AudioFloatInputPort input;
+    AudioFloatOutputPort output;
+
+private:
+    float mMinimum = kDefaultMinHeadroom;
+    float mMaximum = kDefaultMaxHeadroom;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_CLIP_TO_RANGE_H
diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp
new file mode 100644
index 0000000..78aad52
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 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 <unistd.h>
+#include "AudioProcessorBase.h"
+#include "MonoToMultiConverter.h"
+
+using namespace flowgraph;
+
+MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount)
+        : input(*this, 1)
+        , output(*this, channelCount) {
+}
+
+MonoToMultiConverter::~MonoToMultiConverter() { }
+
+int32_t MonoToMultiConverter::onProcess(int64_t framePosition, int32_t numFrames) {
+    int32_t framesToProcess = input.pullData(framePosition, numFrames);
+
+    const float *inputBuffer = input.getBlock();
+    float *outputBuffer = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+    // TODO maybe move to audio_util as audio_mono_to_multi()
+    for (int i = 0; i < framesToProcess; i++) {
+        // read one, write many
+        float sample = *inputBuffer++;
+        for (int channel = 0; channel < channelCount; channel++) {
+            *outputBuffer++ = sample;
+        }
+    }
+    return framesToProcess;
+}
+
diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.h b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h
new file mode 100644
index 0000000..34d53c7
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 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 FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
+#define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class MonoToMultiConverter : public AudioProcessorBase {
+public:
+    explicit MonoToMultiConverter(int32_t channelCount);
+
+    virtual ~MonoToMultiConverter();
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+    AudioFloatInputPort input;
+    AudioFloatOutputPort output;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
diff --git a/media/libaaudio/src/flowgraph/RampLinear.cpp b/media/libaaudio/src/flowgraph/RampLinear.cpp
new file mode 100644
index 0000000..a260828
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/RampLinear.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 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_TAG "RampLinear"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "RampLinear.h"
+
+using namespace flowgraph;
+
+RampLinear::RampLinear(int32_t channelCount)
+        : input(*this, channelCount)
+        , output(*this, channelCount) {
+    mTarget.store(1.0f);
+}
+
+void RampLinear::setLengthInFrames(int32_t frames) {
+    mLengthInFrames = frames;
+}
+
+void RampLinear::setTarget(float target) {
+    mTarget.store(target);
+}
+
+float RampLinear::interpolateCurrent() {
+    return mLevelTo - (mRemaining * mScaler);
+}
+
+int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) {
+    int32_t framesToProcess = input.pullData(framePosition, numFrames);
+    const float *inputBuffer = input.getBlock();
+    float *outputBuffer = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    float target = getTarget();
+    if (target != mLevelTo) {
+        // Start new ramp. Continue from previous level.
+        mLevelFrom = interpolateCurrent();
+        mLevelTo = target;
+        mRemaining = mLengthInFrames;
+        ALOGV("%s() mLevelFrom = %f, mLevelTo = %f, mRemaining = %d, mScaler = %f",
+              __func__, mLevelFrom, mLevelTo, mRemaining, mScaler);
+        mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation
+    }
+
+    int32_t framesLeft = framesToProcess;
+
+    if (mRemaining > 0) { // Ramping? This doesn't happen very often.
+        int32_t framesToRamp = std::min(framesLeft, mRemaining);
+        framesLeft -= framesToRamp;
+        while (framesToRamp > 0) {
+            float currentLevel = interpolateCurrent();
+            for (int ch = 0; ch < channelCount; ch++) {
+                *outputBuffer++ = *inputBuffer++ * currentLevel;
+            }
+            mRemaining--;
+            framesToRamp--;
+        }
+    }
+
+    // Process any frames after the ramp.
+    int32_t samplesLeft = framesLeft * channelCount;
+    for (int i = 0; i < samplesLeft; i++) {
+        *outputBuffer++ = *inputBuffer++ * mLevelTo;
+    }
+
+    return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/RampLinear.h b/media/libaaudio/src/flowgraph/RampLinear.h
new file mode 100644
index 0000000..bdc8f41
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/RampLinear.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 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 FLOWGRAPH_RAMP_LINEAR_H
+#define FLOWGRAPH_RAMP_LINEAR_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class RampLinear : public AudioProcessorBase {
+public:
+    explicit RampLinear(int32_t channelCount);
+
+    virtual ~RampLinear() = default;
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+    /**
+     * This is used for the next ramp.
+     * Calling this does not affect a ramp that is in progress.
+     */
+    void setLengthInFrames(int32_t frames);
+
+    int32_t getLengthInFrames() const {
+        return mLengthInFrames;
+    }
+
+    /**
+     * This may be safely called by another thread.
+     * @param target
+     */
+    void setTarget(float target);
+
+    float getTarget() const {
+        return mTarget.load();
+    }
+
+    /**
+     * Force the nextSegment to start from this level.
+     *
+     * WARNING: this can cause a discontinuity if called while the ramp is being used.
+     * Only call this when setting the initial ramp.
+     *
+     * @param level
+     */
+    void forceCurrent(float level) {
+        mLevelFrom = level;
+        mLevelTo = level;
+    }
+
+    AudioFloatInputPort input;
+    AudioFloatOutputPort output;
+
+private:
+
+    float interpolateCurrent();
+
+    std::atomic<float>  mTarget;
+
+    int32_t             mLengthInFrames  = 48000.0f / 100.0f ; // 10 msec at 48000 Hz;
+    int32_t             mRemaining       = 0;
+    float               mScaler          = 0.0f;
+    float               mLevelFrom       = 0.0f;
+    float               mLevelTo         = 0.0f;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_RAMP_LINEAR_H
diff --git a/media/libaaudio/src/flowgraph/SinkFloat.cpp b/media/libaaudio/src/flowgraph/SinkFloat.cpp
new file mode 100644
index 0000000..fb3dcbc
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkFloat.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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 <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "SinkFloat.h"
+
+using namespace flowgraph;
+
+SinkFloat::SinkFloat(int32_t channelCount)
+        : AudioSink(channelCount) {
+}
+
+int32_t SinkFloat::read(void *data, int32_t numFrames) {
+    float *floatData = (float *) data;
+    int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pull(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *signal = input.getBlock();
+        int32_t numSamples = framesRead * channelCount;
+        memcpy(floatData, signal, numSamples * sizeof(float));
+        floatData += numSamples;
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkFloat.h b/media/libaaudio/src/flowgraph/SinkFloat.h
new file mode 100644
index 0000000..7775c08
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkFloat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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 FLOWGRAPH_SINK_FLOAT_H
+#define FLOWGRAPH_SINK_FLOAT_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkFloat : public AudioSink {
+public:
+    explicit SinkFloat(int32_t channelCount);
+
+    int32_t read(void *data, int32_t numFrames) override;
+
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_FLOAT_H
diff --git a/media/libaaudio/src/flowgraph/SinkI16.cpp b/media/libaaudio/src/flowgraph/SinkI16.cpp
new file mode 100644
index 0000000..ffec8f5
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI16.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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 <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SinkI16.h"
+
+using namespace flowgraph;
+
+SinkI16::SinkI16(int32_t channelCount)
+        : AudioSink(channelCount) {}
+
+int32_t SinkI16::read(void *data, int32_t numFrames) {
+    int16_t *shortData = (int16_t *) data;
+    const int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pull(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *signal = input.getBlock();
+        int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+        memcpy_to_i16_from_float(shortData, signal, numSamples);
+        shortData += numSamples;
+        signal += numSamples;
+#else
+        for (int i = 0; i < numSamples; i++) {
+            int32_t n = (int32_t) (*signal++ * 32768.0f);
+            *shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
+        }
+#endif
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI16.h b/media/libaaudio/src/flowgraph/SinkI16.h
new file mode 100644
index 0000000..6d86266
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI16.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 FLOWGRAPH_SINK_I16_H
+#define FLOWGRAPH_SINK_I16_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI16 : public AudioSink {
+public:
+    explicit SinkI16(int32_t channelCount);
+
+    int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I16_H
diff --git a/media/libaaudio/src/flowgraph/SinkI24.cpp b/media/libaaudio/src/flowgraph/SinkI24.cpp
new file mode 100644
index 0000000..6592828
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI24.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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 <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SinkI24.h"
+
+using namespace flowgraph;
+
+
+SinkI24::SinkI24(int32_t channelCount)
+        : AudioSink(channelCount) {}
+
+int32_t SinkI24::read(void *data, int32_t numFrames) {
+    uint8_t *byteData = (uint8_t *) data;
+    const int32_t channelCount = input.getSamplesPerFrame();
+
+    int32_t framesLeft = numFrames;
+    while (framesLeft > 0) {
+        // Run the graph and pull data through the input port.
+        int32_t framesRead = pull(framesLeft);
+        if (framesRead <= 0) {
+            break;
+        }
+        const float *floatData = input.getBlock();
+        int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+        memcpy_to_p24_from_float(byteData, floatData, numSamples);
+        static const int kBytesPerI24Packed = 3;
+        byteData += numSamples * kBytesPerI24Packed;
+        floatData += numSamples;
+#else
+        const int32_t kI24PackedMax = 0x007FFFFF;
+        const int32_t kI24PackedMin = 0xFF800000;
+        for (int i = 0; i < numSamples; i++) {
+            int32_t n = (int32_t) (*floatData++ * 0x00800000);
+            n = std::min(kI24PackedMax, std::max(kI24PackedMin, n)); // clip
+            // Write as a packed 24-bit integer in Little Endian format.
+            *byteData++ = (uint8_t) n;
+            *byteData++ = (uint8_t) (n >> 8);
+            *byteData++ = (uint8_t) (n >> 16);
+        }
+#endif
+        framesLeft -= framesRead;
+    }
+    return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI24.h b/media/libaaudio/src/flowgraph/SinkI24.h
new file mode 100644
index 0000000..5b9b505
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI24.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 FLOWGRAPH_SINK_I24_H
+#define FLOWGRAPH_SINK_I24_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI24 : public AudioSink {
+public:
+    explicit SinkI24(int32_t channelCount);
+
+    int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I24_H
diff --git a/media/libaaudio/src/flowgraph/SourceFloat.cpp b/media/libaaudio/src/flowgraph/SourceFloat.cpp
new file mode 100644
index 0000000..4bb674f
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceFloat.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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 <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "SourceFloat.h"
+
+using namespace flowgraph;
+
+SourceFloat::SourceFloat(int32_t channelCount)
+        : AudioSource(channelCount) {
+}
+
+int32_t SourceFloat::onProcess(int64_t framePosition, int32_t numFrames) {
+
+    float *outputBuffer = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    int32_t framesToProcess = std::min(numFrames, framesLeft);
+    int32_t numSamples = framesToProcess * channelCount;
+
+    const float *floatBase = (float *) mData;
+    const float *floatData = &floatBase[mFrameIndex * channelCount];
+    memcpy(outputBuffer, floatData, numSamples * sizeof(float));
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
+
diff --git a/media/libaaudio/src/flowgraph/SourceFloat.h b/media/libaaudio/src/flowgraph/SourceFloat.h
new file mode 100644
index 0000000..e6eed9f
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceFloat.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 FLOWGRAPH_SOURCE_FLOAT_H
+#define FLOWGRAPH_SOURCE_FLOAT_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceFloat : public AudioSource {
+public:
+    explicit SourceFloat(int32_t channelCount);
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_FLOAT_H
diff --git a/media/libaaudio/src/flowgraph/SourceI16.cpp b/media/libaaudio/src/flowgraph/SourceI16.cpp
new file mode 100644
index 0000000..c3fcec2
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI16.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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 <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI16.h"
+
+using namespace flowgraph;
+
+SourceI16::SourceI16(int32_t channelCount)
+        : AudioSource(channelCount) {
+}
+
+int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) {
+    float *floatData = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    int32_t framesToProcess = std::min(numFrames, framesLeft);
+    int32_t numSamples = framesToProcess * channelCount;
+
+    const int16_t *shortBase = static_cast<const int16_t *>(mData);
+    const int16_t *shortData = &shortBase[mFrameIndex * channelCount];
+
+#ifdef __ANDROID__
+    memcpy_to_float_from_i16(floatData, shortData, numSamples);
+#else
+    for (int i = 0; i < numSamples; i++) {
+        *floatData++ = *shortData++ * (1.0f / 32768);
+    }
+#endif
+
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/SourceI16.h b/media/libaaudio/src/flowgraph/SourceI16.h
new file mode 100644
index 0000000..2b116cf
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI16.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 FLOWGRAPH_SOURCE_I16_H
+#define FLOWGRAPH_SOURCE_I16_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI16 : public AudioSource {
+public:
+    explicit SourceI16(int32_t channelCount);
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I16_H
diff --git a/media/libaaudio/src/flowgraph/SourceI24.cpp b/media/libaaudio/src/flowgraph/SourceI24.cpp
new file mode 100644
index 0000000..f319880
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI24.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 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 <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI24.h"
+
+using namespace flowgraph;
+
+constexpr int kBytesPerI24Packed = 3;
+
+SourceI24::SourceI24(int32_t channelCount)
+        : AudioSource(channelCount) {
+}
+
+int32_t SourceI24::onProcess(int64_t framePosition, int32_t numFrames) {
+    float *floatData = output.getBlock();
+    int32_t channelCount = output.getSamplesPerFrame();
+
+    int32_t framesLeft = mSizeInFrames - mFrameIndex;
+    int32_t framesToProcess = std::min(numFrames, framesLeft);
+    int32_t numSamples = framesToProcess * channelCount;
+
+    const uint8_t *byteBase = (uint8_t *) mData;
+    const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed];
+
+#ifdef __ANDROID__
+    memcpy_to_float_from_p24(floatData, byteData, numSamples);
+#else
+    static const float scale = 1. / (float)(1UL << 31);
+    for (int i = 0; i < numSamples; i++) {
+        // Assemble the data assuming Little Endian format.
+        int32_t pad = byteData[2];
+        pad <<= 8;
+        pad |= byteData[1];
+        pad <<= 8;
+        pad |= byteData[0];
+        pad <<= 8; // Shift to 32 bit data so the sign is correct.
+        byteData += kBytesPerI24Packed;
+        *floatData++ = pad * scale; // scale to range -1.0 to 1.0
+    }
+#endif
+
+    mFrameIndex += framesToProcess;
+    return framesToProcess;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/SourceI24.h b/media/libaaudio/src/flowgraph/SourceI24.h
new file mode 100644
index 0000000..39f14da
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI24.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 FLOWGRAPH_SOURCE_I24_H
+#define FLOWGRAPH_SOURCE_I24_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI24 : public AudioSource {
+public:
+    explicit SourceI24(int32_t channelCount);
+
+    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I24_H
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 494edbc..8e78554 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -77,7 +77,6 @@
 
     virtual int64_t incrementClientFrameCounter(int32_t frames)  = 0;
 
-
     virtual int64_t getFramesWritten() override {
         return mFramesWritten.get();
     }
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 505f2ee..dbf00a9 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -81,8 +81,8 @@
     }
 
     // Preserve behavior of API 26
-    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
-        setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+        setFormat(AUDIO_FORMAT_PCM_FLOAT);
     }
 
     // Maybe change device format to get a FAST path.
@@ -99,12 +99,12 @@
     // We just may not get a FAST track.
     // But we wouldn't have anyway without this hack.
     constexpr int32_t kMostLikelySampleRateForFast = 48000;
-    if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
+    if (getFormat() == AUDIO_FORMAT_PCM_FLOAT
             && perfMode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
             && (samplesPerFrame <= 2) // FAST only for mono and stereo
             && (getSampleRate() == kMostLikelySampleRateForFast
                 || getSampleRate() == AAUDIO_UNSPECIFIED)) {
-        setDeviceFormat(AAUDIO_FORMAT_PCM_I16);
+        setDeviceFormat(AUDIO_FORMAT_PCM_16_BIT);
     } else {
         setDeviceFormat(getFormat());
     }
@@ -147,8 +147,7 @@
     // ----------- open the AudioRecord ---------------------
     // Might retry, but never more than once.
     for (int i = 0; i < 2; i ++) {
-        audio_format_t requestedInternalFormat =
-                AAudioConvert_aaudioToAndroidDataFormat(getDeviceFormat());
+        const audio_format_t requestedInternalFormat = getDeviceFormat();
 
         mAudioRecord = new AudioRecord(
                 mOpPackageName // const String16& opPackageName TODO does not compile
@@ -214,8 +213,8 @@
     }
 
     // Allocate format conversion buffer if needed.
-    if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
-        && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
+    if (getDeviceFormat() == AUDIO_FORMAT_PCM_16_BIT
+        && getFormat() == AUDIO_FORMAT_PCM_FLOAT) {
 
         if (builder.getDataCallbackProc() != nullptr) {
             // If we have a callback then we need to convert the data into an internal float
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 505cd77..1572f0d 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -88,9 +88,9 @@
 
     int32_t notificationFrames = 0;
 
-    audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
+    const audio_format_t format = (getFormat() == AUDIO_FORMAT_DEFAULT)
             ? AUDIO_FORMAT_PCM_FLOAT
-            : AAudioConvert_aaudioToAndroidDataFormat(getFormat());
+            : getFormat();
 
     // Setup the callback if there is one.
     AudioTrack::callback_t callback = nullptr;
@@ -178,10 +178,8 @@
 
     // Get the actual values from the AudioTrack.
     setSamplesPerFrame(mAudioTrack->channelCount());
-    aaudio_format_t aaudioFormat =
-            AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format());
-    setFormat(aaudioFormat);
-    setDeviceFormat(aaudioFormat);
+    setFormat(mAudioTrack->format());
+    setDeviceFormat(mAudioTrack->format());
 
     int32_t actualSampleRate = mAudioTrack->getSampleRate();
     ALOGW_IF(actualSampleRate != getSampleRate(),
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 40ebb76..f5b3ad4 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -33,395 +33,6 @@
 
 using namespace android;
 
-// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
-// It is designed to allow occasional transient peaks.
-#define MAX_HEADROOM (1.41253754f)
-#define MIN_HEADROOM (0 - MAX_HEADROOM)
-
-int32_t AAudioConvert_formatToSizeInBytes(aaudio_format_t format) {
-    int32_t size = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-    switch (format) {
-        case AAUDIO_FORMAT_PCM_I16:
-            size = sizeof(int16_t);
-            break;
-        case AAUDIO_FORMAT_PCM_FLOAT:
-            size = sizeof(float);
-            break;
-        default:
-            break;
-    }
-    return size;
-}
-
-// TODO expose and call clamp16_from_float function in primitives.h
-static inline int16_t clamp16_from_float(float f) {
-    static const float scale = 1 << 15;
-    return (int16_t) roundf(fmaxf(fminf(f * scale, scale - 1.f), -scale));
-}
-
-// Clip to valid range of a float sample to prevent excessive volume.
-// By using fmin and fmax we also protect against NaN.
-static float clipToMinMaxHeadroom(float input) {
-    return fmin(MAX_HEADROOM, fmax(MIN_HEADROOM, input));
-}
-
-static float clipAndClampFloatToPcm16(float sample, float scaler) {
-    // Clip to valid range of a float sample to prevent excessive volume.
-    sample = clipToMinMaxHeadroom(sample);
-
-    // Scale and convert to a short.
-    float fval = sample * scaler;
-    return clamp16_from_float(fval);
-}
-
-void AAudioConvert_floatToPcm16(const float *source,
-                                int16_t *destination,
-                                int32_t numSamples,
-                                float amplitude) {
-    const float scaler = amplitude;
-    for (int i = 0; i < numSamples; i++) {
-        float sample = *source++;
-        *destination++ = clipAndClampFloatToPcm16(sample, scaler);
-    }
-}
-
-void AAudioConvert_floatToPcm16(const float *source,
-                                int16_t *destination,
-                                int32_t numFrames,
-                                int32_t samplesPerFrame,
-                                float amplitude1,
-                                float amplitude2) {
-    float scaler = amplitude1;
-    // divide by numFrames so that we almost reach amplitude2
-    float delta = (amplitude2 - amplitude1) / numFrames;
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
-            float sample = *source++;
-            *destination++ = clipAndClampFloatToPcm16(sample, scaler);
-        }
-        scaler += delta;
-    }
-}
-
-#define SHORT_SCALE  32768
-
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
-                                float *destination,
-                                int32_t numSamples,
-                                float amplitude) {
-    const float scaler = amplitude / SHORT_SCALE;
-    for (int i = 0; i < numSamples; i++) {
-        destination[i] = source[i] * scaler;
-    }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
-                                float *destination,
-                                int32_t numFrames,
-                                int32_t samplesPerFrame,
-                                float amplitude1,
-                                float amplitude2) {
-    float scaler = amplitude1 / SHORT_SCALE;
-    const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
-            *destination++ = *source++ * scaler;
-        }
-        scaler += delta;
-    }
-}
-
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRamp(const float *source,
-                       float *destination,
-                       int32_t numFrames,
-                       int32_t samplesPerFrame,
-                       float amplitude1,
-                       float amplitude2) {
-    float scaler = amplitude1;
-    const float delta = (amplitude2 - amplitude1) / numFrames;
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
-            float sample = *source++;
-            // Clip to valid range of a float sample to prevent excessive volume.
-            sample = clipToMinMaxHeadroom(sample);
-
-            *destination++ = sample * scaler;
-        }
-        scaler += delta;
-    }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRamp(const int16_t *source,
-                       int16_t *destination,
-                       int32_t numFrames,
-                       int32_t samplesPerFrame,
-                       float amplitude1,
-                       float amplitude2) {
-    // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
-    float scaler = amplitude1;
-    const float delta = (amplitude2 - amplitude1) / numFrames;
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
-            // No need to clip because int16_t range is inherently limited.
-            float sample =  *source++ * scaler;
-            *destination++ = (int16_t) roundf(sample);
-        }
-        scaler += delta;
-    }
-}
-
-// *************************************************************************************
-// Convert Mono To Stereo at the same time as converting format.
-void AAudioConvert_formatMonoToStereo(const float *source,
-                                      int16_t *destination,
-                                      int32_t numFrames,
-                                      float amplitude) {
-    const float scaler = amplitude;
-    for (int i = 0; i < numFrames; i++) {
-        float sample = *source++;
-        int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
-        *destination++ = sample16;
-        *destination++ = sample16;
-    }
-}
-
-void AAudioConvert_formatMonoToStereo(const float *source,
-                                      int16_t *destination,
-                                      int32_t numFrames,
-                                      float amplitude1,
-                                      float amplitude2) {
-    // divide by numFrames so that we almost reach amplitude2
-    const float delta = (amplitude2 - amplitude1) / numFrames;
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        const float scaler = amplitude1 + (frameIndex * delta);
-        const float sample = *source++;
-        int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
-        *destination++ = sample16;
-        *destination++ = sample16;
-    }
-}
-
-void AAudioConvert_formatMonoToStereo(const int16_t *source,
-                                      float *destination,
-                                      int32_t numFrames,
-                                      float amplitude) {
-    const float scaler = amplitude / SHORT_SCALE;
-    for (int i = 0; i < numFrames; i++) {
-        float sample = source[i] * scaler;
-        *destination++ = sample;
-        *destination++ = sample;
-    }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudioConvert_formatMonoToStereo(const int16_t *source,
-                                      float *destination,
-                                      int32_t numFrames,
-                                      float amplitude1,
-                                      float amplitude2) {
-    const float scaler1 = amplitude1 / SHORT_SCALE;
-    const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        float scaler = scaler1 + (frameIndex * delta);
-        float sample = source[frameIndex] * scaler;
-        *destination++ = sample;
-        *destination++ = sample;
-    }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRampMonoToStereo(const float *source,
-                                   float *destination,
-                                   int32_t numFrames,
-                                   float amplitude1,
-                                   float amplitude2) {
-    const float delta = (amplitude2 - amplitude1) / numFrames;
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        float sample = *source++;
-
-        // Clip to valid range of a float sample to prevent excessive volume.
-        sample = clipToMinMaxHeadroom(sample);
-
-        const float scaler = amplitude1 + (frameIndex * delta);
-        float sampleScaled = sample * scaler;
-        *destination++ = sampleScaled;
-        *destination++ = sampleScaled;
-    }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRampMonoToStereo(const int16_t *source,
-                                   int16_t *destination,
-                                   int32_t numFrames,
-                                   float amplitude1,
-                                   float amplitude2) {
-    // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
-    const float delta = (amplitude2 - amplitude1) / numFrames;
-    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
-        const float scaler = amplitude1 + (frameIndex * delta);
-        // No need to clip because int16_t range is inherently limited.
-        const float sample =  *source++ * scaler;
-        int16_t sample16 = (int16_t) roundf(sample);
-        *destination++ = sample16;
-        *destination++ = sample16;
-    }
-}
-
-// *************************************************************************************
-void AAudioDataConverter::convert(
-        const FormattedData &source,
-        const FormattedData &destination,
-        int32_t numFrames,
-        float levelFrom,
-        float levelTo) {
-
-    if (source.channelCount == 1 && destination.channelCount == 2) {
-        convertMonoToStereo(source,
-                            destination,
-                            numFrames,
-                            levelFrom,
-                            levelTo);
-    } else {
-        // We only support mono to stereo conversion. Otherwise source and destination
-        // must match.
-        assert(source.channelCount == destination.channelCount);
-        convertChannelsMatch(source,
-                             destination,
-                             numFrames,
-                             levelFrom,
-                             levelTo);
-    }
-}
-
-void AAudioDataConverter::convertMonoToStereo(
-        const FormattedData &source,
-        const FormattedData &destination,
-        int32_t numFrames,
-        float levelFrom,
-        float levelTo) {
-
-    // The formats are validated when the stream is opened so we do not have to
-    // check for illegal combinations here.
-    if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
-        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
-            AAudio_linearRampMonoToStereo(
-                    (const float *) source.data,
-                    (float *) destination.data,
-                    numFrames,
-                    levelFrom,
-                    levelTo);
-        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
-            if (levelFrom != levelTo) {
-                AAudioConvert_formatMonoToStereo(
-                        (const float *) source.data,
-                        (int16_t *) destination.data,
-                        numFrames,
-                        levelFrom,
-                        levelTo);
-            } else {
-                AAudioConvert_formatMonoToStereo(
-                        (const float *) source.data,
-                        (int16_t *) destination.data,
-                        numFrames,
-                        levelTo);
-            }
-        }
-    } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
-        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
-            if (levelFrom != levelTo) {
-                AAudioConvert_formatMonoToStereo(
-                        (const int16_t *) source.data,
-                        (float *) destination.data,
-                        numFrames,
-                        levelFrom,
-                        levelTo);
-            } else {
-                AAudioConvert_formatMonoToStereo(
-                        (const int16_t *) source.data,
-                        (float *) destination.data,
-                        numFrames,
-                        levelTo);
-            }
-        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
-            AAudio_linearRampMonoToStereo(
-                    (const int16_t *) source.data,
-                    (int16_t *) destination.data,
-                    numFrames,
-                    levelFrom,
-                    levelTo);
-        }
-    }
-}
-
-void AAudioDataConverter::convertChannelsMatch(
-        const FormattedData &source,
-        const FormattedData &destination,
-        int32_t numFrames,
-        float levelFrom,
-        float levelTo) {
-    const int32_t numSamples = numFrames * source.channelCount;
-
-    // The formats are validated when the stream is opened so we do not have to
-    // check for illegal combinations here.
-    if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
-        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
-            AAudio_linearRamp(
-                    (const float *) source.data,
-                    (float *) destination.data,
-                    numFrames,
-                    source.channelCount,
-                    levelFrom,
-                    levelTo);
-        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
-            if (levelFrom != levelTo) {
-                AAudioConvert_floatToPcm16(
-                        (const float *) source.data,
-                        (int16_t *) destination.data,
-                        numFrames,
-                        source.channelCount,
-                        levelFrom,
-                        levelTo);
-            } else {
-                AAudioConvert_floatToPcm16(
-                        (const float *) source.data,
-                        (int16_t *) destination.data,
-                        numSamples,
-                        levelTo);
-            }
-        }
-    } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
-        if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
-            if (levelFrom != levelTo) {
-                AAudioConvert_pcm16ToFloat(
-                        (const int16_t *) source.data,
-                        (float *) destination.data,
-                        numFrames,
-                        source.channelCount,
-                        levelFrom,
-                        levelTo);
-            } else {
-                AAudioConvert_pcm16ToFloat(
-                        (const int16_t *) source.data,
-                        (float *) destination.data,
-                        numSamples,
-                        levelTo);
-            }
-        } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
-            AAudio_linearRamp(
-                    (const int16_t *) source.data,
-                    (int16_t *) destination.data,
-                    numFrames,
-                    source.channelCount,
-                    levelFrom,
-                    levelTo);
-        }
-    }
-}
-
 status_t AAudioConvert_aaudioToAndroidStatus(aaudio_result_t result) {
     // This covers the case for AAUDIO_OK and for positive results.
     if (result >= 0) {
@@ -513,6 +124,9 @@
 audio_format_t AAudioConvert_aaudioToAndroidDataFormat(aaudio_format_t aaudioFormat) {
     audio_format_t androidFormat;
     switch (aaudioFormat) {
+    case AAUDIO_FORMAT_UNSPECIFIED:
+        androidFormat = AUDIO_FORMAT_DEFAULT;
+        break;
     case AAUDIO_FORMAT_PCM_I16:
         androidFormat = AUDIO_FORMAT_PCM_16_BIT;
         break;
@@ -520,16 +134,19 @@
         androidFormat = AUDIO_FORMAT_PCM_FLOAT;
         break;
     default:
-        androidFormat = AUDIO_FORMAT_DEFAULT;
-        ALOGE("AAudioConvert_aaudioToAndroidDataFormat 0x%08X unrecognized", aaudioFormat);
+        androidFormat = AUDIO_FORMAT_INVALID;
+        ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat);
         break;
     }
     return androidFormat;
 }
 
 aaudio_format_t AAudioConvert_androidToAAudioDataFormat(audio_format_t androidFormat) {
-    aaudio_format_t aaudioFormat = AAUDIO_FORMAT_INVALID;
+    aaudio_format_t aaudioFormat;
     switch (androidFormat) {
+    case AUDIO_FORMAT_DEFAULT:
+        aaudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+        break;
     case AUDIO_FORMAT_PCM_16_BIT:
         aaudioFormat = AAUDIO_FORMAT_PCM_I16;
         break;
@@ -538,7 +155,7 @@
         break;
     default:
         aaudioFormat = AAUDIO_FORMAT_INVALID;
-        ALOGE("AAudioConvert_androidToAAudioDataFormat 0x%08X unrecognized", androidFormat);
+        ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat);
         break;
     }
     return aaudioFormat;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 4b975e8..dc2b198 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -45,156 +45,6 @@
 audio_session_t AAudioConvert_aaudioToAndroidSessionId(aaudio_session_id_t sessionId);
 
 /**
- * Convert an array of floats to an array of int16_t.
- *
- * @param source
- * @param destination
- * @param numSamples number of values in the array
- * @param amplitude level between 0.0 and 1.0
- */
-void AAudioConvert_floatToPcm16(const float *source,
-                                int16_t *destination,
-                                int32_t numSamples,
-                                float amplitude);
-
-/**
- * Convert floats to int16_t and scale by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame AKA number of channels
- * @param amplitude1 level at start of ramp, between 0.0 and 1.0
- * @param amplitude2 level past end of ramp, between 0.0 and 1.0
- */
-void AAudioConvert_floatToPcm16(const float *source,
-                                int16_t *destination,
-                                int32_t numFrames,
-                                int32_t samplesPerFrame,
-                                float amplitude1,
-                                float amplitude2);
-
-/**
- * Convert int16_t array to float array ranging from -1.0 to +1.0.
- * @param source
- * @param destination
- * @param numSamples
- */
-//void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples,
-//                                float *destination);
-
-/**
- *
- * Convert int16_t array to float array ranging from +/- amplitude.
- * @param source
- * @param destination
- * @param numSamples
- * @param amplitude
- */
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
-                                float *destination,
-                                int32_t numSamples,
-                                float amplitude);
-
-/**
- * Convert floats to int16_t and scale by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame AKA number of channels
- * @param amplitude1 level at start of ramp, between 0.0 and 1.0
- * @param amplitude2 level at end of ramp, between 0.0 and 1.0
- */
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
-                                float *destination,
-                                int32_t numFrames,
-                                int32_t samplesPerFrame,
-                                float amplitude1,
-                                float amplitude2);
-
-/**
- * Scale floats by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame
- * @param amplitude1
- * @param amplitude2
- */
-void AAudio_linearRamp(const float *source,
-                       float *destination,
-                       int32_t numFrames,
-                       int32_t samplesPerFrame,
-                       float amplitude1,
-                       float amplitude2);
-
-/**
- * Scale int16_t's by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame
- * @param amplitude1
- * @param amplitude2
- */
-void AAudio_linearRamp(const int16_t *source,
-                       int16_t *destination,
-                       int32_t numFrames,
-                       int32_t samplesPerFrame,
-                       float amplitude1,
-                       float amplitude2);
-
-class AAudioDataConverter {
-public:
-
-    struct FormattedData {
-
-        FormattedData(void *data, aaudio_format_t format, int32_t channelCount)
-            : data(data)
-            , format(format)
-            , channelCount(channelCount) {}
-
-        const void            *data = nullptr;
-        const aaudio_format_t  format = AAUDIO_FORMAT_UNSPECIFIED;
-        const int32_t          channelCount = 1;
-    };
-
-    static void convert(const FormattedData &source,
-                        const FormattedData &destination,
-                        int32_t numFrames,
-                        float levelFrom,
-                        float levelTo);
-
-private:
-    static void convertMonoToStereo(const FormattedData &source,
-                                    const FormattedData &destination,
-                                    int32_t numFrames,
-                                    float levelFrom,
-                                    float levelTo);
-
-    static void convertChannelsMatch(const FormattedData &source,
-                                     const FormattedData &destination,
-                                     int32_t numFrames,
-                                     float levelFrom,
-                                     float levelTo);
-};
-
-/**
  * Calculate the number of bytes and prevent numeric overflow.
  * The *sizeInBytes will be set to zero if there is an error.
  *
@@ -234,12 +84,6 @@
  */
 audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t preset);
 
-/**
- * @return the size of a sample of the given format in bytes or AAUDIO_ERROR_ILLEGAL_ARGUMENT
- */
-int32_t AAudioConvert_formatToSizeInBytes(aaudio_format_t format);
-
-
 // Note that this code may be replaced by Settings or by some other system configuration tool.
 
 #define AAUDIO_PROP_MMAP_POLICY           "aaudio.mmap_policy"
diff --git a/media/libaaudio/src/utility/LinearRamp.cpp b/media/libaaudio/src/utility/LinearRamp.cpp
deleted file mode 100644
index 1714bbf..0000000
--- a/media/libaaudio/src/utility/LinearRamp.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2017 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 "LinearRamp.h"
-
-bool LinearRamp::isRamping() {
-    float target = mTarget.load();
-    if (target != mLevelTo) {
-        // Update target. Continue from previous level.
-        mLevelTo = target;
-        mRemaining = mLengthInFrames;
-        return true;
-    } else {
-        return mRemaining > 0;
-    }
-}
-
-bool LinearRamp::nextSegment(int32_t frames, float *levelFrom, float *levelTo) {
-    bool ramping = isRamping();
-    *levelFrom = mLevelFrom;
-    if (ramping) {
-        float level;
-        if (frames >= mRemaining) {
-            level = mLevelTo;
-            mRemaining = 0;
-        } else {
-            // Interpolate to a point along the full ramp.
-            level = mLevelFrom + (frames * (mLevelTo - mLevelFrom) / mRemaining);
-            mRemaining -= frames;
-        }
-        mLevelFrom = level; // for next ramp
-        *levelTo = level;
-    } else {
-        *levelTo = mLevelTo;
-    }
-    return ramping;
-}
\ No newline at end of file
diff --git a/media/libaaudio/src/utility/LinearRamp.h b/media/libaaudio/src/utility/LinearRamp.h
deleted file mode 100644
index 2b1b8e0..0000000
--- a/media/libaaudio/src/utility/LinearRamp.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2017 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 AAUDIO_LINEAR_RAMP_H
-#define AAUDIO_LINEAR_RAMP_H
-
-#include <atomic>
-#include <stdint.h>
-
-/**
- * Generate segments along a linear ramp.
- * The ramp target can be updated from another thread.
- * When the target is updated, a new ramp is started from the current position.
- *
- * The first ramp starts at 0.0.
- *
- */
-class LinearRamp {
-public:
-    LinearRamp() {
-        mTarget.store(1.0f);
-    }
-
-    void setLengthInFrames(int32_t frames) {
-        mLengthInFrames = frames;
-    }
-
-    int32_t getLengthInFrames() {
-        return mLengthInFrames;
-    }
-
-    /**
-     * This may be called by another thread.
-     * @param target
-     */
-    void setTarget(float target) {
-        mTarget.store(target);
-    }
-
-    float getTarget() {
-        return mTarget.load();
-    }
-
-    /**
-     * Force the nextSegment to start from this level.
-     *
-     * WARNING: this can cause a discontinuity if called while the ramp is being used.
-     * Only call this when setting the initial ramp.
-     *
-     * @param level
-     */
-    void forceCurrent(float level) {
-        mLevelFrom = level;
-        mLevelTo = level; // forces a ramp if it does not match target
-    }
-
-    float getCurrent() {
-        return mLevelFrom;
-    }
-
-    /**
-     * Get levels for next ramp segment.
-     *
-     * @param frames number of frames in the segment
-     * @param levelFrom pointer to starting amplitude
-     * @param levelTo pointer to ending amplitude
-     * @return true if ramp is still moving towards the target
-     */
-    bool nextSegment(int32_t frames, float *levelFrom, float *levelTo);
-
-private:
-
-    bool isRamping();
-
-    std::atomic<float>   mTarget;
-
-    int32_t mLengthInFrames  = 48000 / 100; // 10 msec at 48000 Hz
-    int32_t mRemaining       = 0;
-    float   mLevelFrom       = 0.0f;
-    float   mLevelTo         = 0.0f;
-};
-
-
-#endif //AAUDIO_LINEAR_RAMP_H
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 68194db..ef272b0 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -34,13 +34,6 @@
 }
 
 cc_test {
-    name: "test_linear_ramp",
-    defaults: ["libaaudio_tests_defaults"],
-    srcs: ["test_linear_ramp.cpp"],
-    shared_libs: ["libaaudio"],
-}
-
-cc_test {
     name: "test_open_params",
     defaults: ["libaaudio_tests_defaults"],
     srcs: ["test_open_params.cpp"],
@@ -167,3 +160,15 @@
     srcs: ["test_atomic_fifo.cpp"],
     shared_libs: ["libaaudio"],
 }
+
+cc_test {
+    name: "test_flowgraph",
+    defaults: ["libaaudio_tests_defaults"],
+    srcs: ["test_flowgraph.cpp"],
+    shared_libs: [
+        "libaaudio",
+        "libbinder",
+        "libcutils",
+        "libutils",
+    ],
+}
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
new file mode 100644
index 0000000..d563a7e
--- /dev/null
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 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.
+ */
+
+/*
+ * Test FlowGraph
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "flowgraph/ClipToRange.h"
+#include "flowgraph/MonoToMultiConverter.h"
+#include "flowgraph/SourceFloat.h"
+#include "flowgraph/RampLinear.h"
+#include "flowgraph/SinkFloat.h"
+#include "flowgraph/SinkI16.h"
+#include "flowgraph/SinkI24.h"
+#include "flowgraph/SourceI16.h"
+#include "flowgraph/SourceI24.h"
+
+using namespace flowgraph;
+
+constexpr int kBytesPerI24Packed = 3;
+
+TEST(test_flowgraph, module_sinki16) {
+    static const float input[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 53.9f, -87.2f};
+    static const int16_t expected[] = {32767, 16384, -8192, -32768, 0, 32767, -32768};
+    int16_t output[20];
+    SourceFloat sourceFloat{1};
+    SinkI16 sinkI16{1};
+
+    int numInputFrames = sizeof(input) / sizeof(input[0]);
+    sourceFloat.setData(input, numInputFrames);
+    sourceFloat.output.connect(&sinkI16.input);
+
+    int numOutputFrames = sizeof(output) / sizeof(int16_t);
+    int32_t numRead = sinkI16.read(output, numOutputFrames);
+    ASSERT_EQ(numInputFrames, numRead);
+    for (int i = 0; i < numRead; i++) {
+        EXPECT_EQ(expected[i], output[i]);
+    }
+}
+
+TEST(test_flowgraph, module_mono_to_stereo) {
+    static const float input[] = {1.0f, 2.0f, 3.0f};
+    float output[100] = {};
+    SourceFloat sourceFloat{1};
+    MonoToMultiConverter monoToStereo{2};
+    SinkFloat sinkFloat{2};
+
+    sourceFloat.setData(input, 3);
+
+    sourceFloat.output.connect(&monoToStereo.input);
+    monoToStereo.output.connect(&sinkFloat.input);
+
+    int32_t numRead = sinkFloat.read(output, 8);
+    ASSERT_EQ(3, numRead);
+    EXPECT_EQ(input[0], output[0]);
+    EXPECT_EQ(input[0], output[1]);
+    EXPECT_EQ(input[1], output[2]);
+    EXPECT_EQ(input[1], output[3]);
+}
+
+TEST(test_flowgraph, module_ramp_linear) {
+    constexpr int rampSize = 5;
+    constexpr int numOutput = 100;
+    constexpr float value = 1.0f;
+    constexpr float target = 100.0f;
+    float output[numOutput] = {};
+    RampLinear rampLinear{1};
+    SinkFloat sinkFloat{1};
+
+    rampLinear.input.setValue(value);
+    rampLinear.setLengthInFrames(rampSize);
+    rampLinear.setTarget(target);
+    rampLinear.forceCurrent(0.0f);
+
+    rampLinear.output.connect(&sinkFloat.input);
+
+    int32_t numRead = sinkFloat.read(output, numOutput);
+    ASSERT_EQ(numOutput, numRead);
+    constexpr float tolerance = 0.0001f; // arbitrary
+    int i = 0;
+    for (; i < rampSize; i++) {
+        float expected = i * value * target / rampSize;
+        EXPECT_NEAR(expected, output[i], tolerance);
+    }
+    for (; i < numOutput; i++) {
+        float expected = value * target;
+        EXPECT_NEAR(expected, output[i], tolerance);
+    }
+}
+
+// It is easiest to represent packed 24-bit data as a byte array.
+// This test will read from input, convert to float, then write
+// back to output as bytes.
+TEST(test_flowgraph, module_packed_24) {
+    static const uint8_t input[] = {0x01, 0x23, 0x45,
+                                    0x67, 0x89, 0xAB,
+                                    0xCD, 0xEF, 0x5A};
+    uint8_t output[99] = {};
+    SourceI24 sourceI24{1};
+    SinkI24 sinkI24{1};
+
+    int numInputFrames = sizeof(input) / kBytesPerI24Packed;
+    sourceI24.setData(input, numInputFrames);
+    sourceI24.output.connect(&sinkI24.input);
+
+    int32_t numRead = sinkI24.read(output, sizeof(output) / kBytesPerI24Packed);
+    ASSERT_EQ(numInputFrames, numRead);
+    for (size_t i = 0; i < sizeof(input); i++) {
+        EXPECT_EQ(input[i], output[i]);
+    }
+}
+
+TEST(test_flowgraph, module_clip_to_range) {
+    constexpr float myMin = -2.0f;
+    constexpr float myMax = 1.5f;
+
+    static const float input[] = {-9.7, 0.5f, -0.25, 1.0f, 12.3};
+    static const float expected[] = {myMin, 0.5f, -0.25, 1.0f, myMax};
+    float output[100];
+    SourceFloat sourceFloat{1};
+    ClipToRange clipper{1};
+    SinkFloat sinkFloat{1};
+
+    int numInputFrames = sizeof(input) / sizeof(input[0]);
+    sourceFloat.setData(input, numInputFrames);
+
+    clipper.setMinimum(myMin);
+    clipper.setMaximum(myMax);
+
+    sourceFloat.output.connect(&clipper.input);
+    clipper.output.connect(&sinkFloat.input);
+
+    int numOutputFrames = sizeof(output) / sizeof(output[0]);
+    int32_t numRead = sinkFloat.read(output, numOutputFrames);
+    ASSERT_EQ(numInputFrames, numRead);
+    constexpr float tolerance = 0.000001f; // arbitrary
+    for (int i = 0; i < numRead; i++) {
+        EXPECT_NEAR(expected[i], output[i], tolerance);
+    }
+}
diff --git a/media/libaaudio/tests/test_linear_ramp.cpp b/media/libaaudio/tests/test_linear_ramp.cpp
deleted file mode 100644
index 93226ba..0000000
--- a/media/libaaudio/tests/test_linear_ramp.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2017 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 <iostream>
-#include <math.h>
-
-#include <gtest/gtest.h>
-
-#include "utility/AAudioUtilities.h"
-#include "utility/LinearRamp.h"
-
-TEST(test_linear_ramp, linear_ramp_segments) {
-    LinearRamp ramp;
-    const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
-    float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
-
-    float levelFrom = -1.0f;
-    float levelTo = -1.0f;
-    ramp.setLengthInFrames(8);
-    ramp.setTarget(8.0f);
-
-    EXPECT_EQ(8, ramp.getLengthInFrames());
-
-    bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
-    EXPECT_EQ(1, ramping);
-    EXPECT_EQ(0.0f, levelFrom);
-    EXPECT_EQ(4.0f, levelTo);
-
-    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
-    EXPECT_EQ(0.0f, destination[0]);
-    EXPECT_EQ(1.0f, destination[1]);
-    EXPECT_EQ(2.0f, destination[2]);
-    EXPECT_EQ(3.0f, destination[3]);
-
-    ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
-    EXPECT_EQ(1, ramping);
-    EXPECT_EQ(4.0f, levelFrom);
-    EXPECT_EQ(8.0f, levelTo);
-
-    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
-    EXPECT_EQ(4.0f, destination[0]);
-    EXPECT_EQ(5.0f, destination[1]);
-    EXPECT_EQ(6.0f, destination[2]);
-    EXPECT_EQ(7.0f, destination[3]);
-
-    ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
-    EXPECT_EQ(0, ramping);
-    EXPECT_EQ(8.0f, levelFrom);
-    EXPECT_EQ(8.0f, levelTo);
-
-    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
-    EXPECT_EQ(8.0f, destination[0]);
-    EXPECT_EQ(8.0f, destination[1]);
-    EXPECT_EQ(8.0f, destination[2]);
-    EXPECT_EQ(8.0f, destination[3]);
-
-};
-
-
-TEST(test_linear_ramp, linear_ramp_forced) {
-    LinearRamp ramp;
-    const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
-    float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
-
-    float levelFrom = -1.0f;
-    float levelTo = -1.0f;
-    ramp.setLengthInFrames(4);
-    ramp.setTarget(8.0f);
-    ramp.forceCurrent(4.0f);
-    EXPECT_EQ(4.0f, ramp.getCurrent());
-
-    bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
-    EXPECT_EQ(1, ramping);
-    EXPECT_EQ(4.0f, levelFrom);
-    EXPECT_EQ(8.0f, levelTo);
-
-    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
-    EXPECT_EQ(4.0f, destination[0]);
-    EXPECT_EQ(5.0f, destination[1]);
-    EXPECT_EQ(6.0f, destination[2]);
-    EXPECT_EQ(7.0f, destination[3]);
-
-    ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
-    EXPECT_EQ(0, ramping);
-    EXPECT_EQ(8.0f, levelFrom);
-    EXPECT_EQ(8.0f, levelTo);
-
-    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
-    EXPECT_EQ(8.0f, destination[0]);
-    EXPECT_EQ(8.0f, destination[1]);
-    EXPECT_EQ(8.0f, destination[2]);
-    EXPECT_EQ(8.0f, destination[3]);
-
-};
-
-constexpr int16_t kMaxI16 = INT16_MAX;
-constexpr int16_t kMinI16 = INT16_MIN;
-constexpr int16_t kHalfI16 = 16384;
-constexpr int16_t kTenthI16 = 3277;
-
-//void AAudioConvert_floatToPcm16(const float *source,
-//                                int16_t *destination,
-//                                int32_t numSamples,
-//                                float amplitude);
-TEST(test_linear_ramp, float_to_i16) {
-    const float source[] = {12345.6f, 1.0f, 0.5f, 0.1f, 0.0f, -0.1f, -0.5f, -1.0f, -12345.6f};
-    constexpr size_t count = sizeof(source) / sizeof(source[0]);
-    int16_t destination[count];
-    const int16_t expected[count] = {kMaxI16, kMaxI16, kHalfI16, kTenthI16, 0,
-                                     -kTenthI16, -kHalfI16, kMinI16, kMinI16};
-
-    AAudioConvert_floatToPcm16(source, destination, count, 1.0f);
-    for (size_t i = 0; i < count; i++) {
-        EXPECT_EQ(expected[i], destination[i]);
-    }
-
-}
-
-//void AAudioConvert_pcm16ToFloat(const int16_t *source,
-//                                float *destination,
-//                                int32_t numSamples,
-//                                float amplitude);
-TEST(test_linear_ramp, i16_to_float) {
-    const int16_t source[] = {kMaxI16, kHalfI16, kTenthI16, 0,
-                              -kTenthI16, -kHalfI16, kMinI16};
-    constexpr size_t count = sizeof(source) / sizeof(source[0]);
-    float destination[count];
-    const float expected[count] = {(32767.0f / 32768.0f), 0.5f, 0.1f, 0.0f, -0.1f, -0.5f, -1.0f};
-
-    AAudioConvert_pcm16ToFloat(source, destination, count, 1.0f);
-    for (size_t i = 0; i < count; i++) {
-        EXPECT_NEAR(expected[i], destination[i], 0.0001f);
-    }
-
-}
-
-//void AAudio_linearRamp(const int16_t *source,
-//                       int16_t *destination,
-//                       int32_t numFrames,
-//                       int32_t samplesPerFrame,
-//                       float amplitude1,
-//                       float amplitude2);
-TEST(test_linear_ramp, ramp_i16_to_i16) {
-    const int16_t source[] = {1, 1, 1, 1, 1, 1, 1, 1};
-    constexpr size_t count = sizeof(source) / sizeof(source[0]);
-    int16_t destination[count];
-    // Ramp will sweep from -1 to almost +1
-    const int16_t expected[count] = {
-            -1, // from -1.00
-            -1, // from -0.75
-            -1, // from -0.55, round away from zero
-            0,  // from -0.25, round up to zero
-            0,  // from  0.00
-            0,  // from  0.25, round down to zero
-            1,  // from  0.50, round away from zero
-            1   // from  0.75
-    };
-
-    // sweep across zero to test symmetry
-    constexpr float amplitude1 = -1.0;
-    constexpr float amplitude2 = 1.0;
-    AAudio_linearRamp(source, destination, count, 1, amplitude1, amplitude2);
-    for (size_t i = 0; i < count; i++) {
-        EXPECT_EQ(expected[i], destination[i]);
-    }
-
-}
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f3ea826..af54b21 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -337,6 +337,7 @@
 
 void AudioMixer::Track::reconfigureBufferProviders()
 {
+    // configure from upstream to downstream buffer providers.
     bufferProvider = mInputBufferProvider;
     if (mReformatBufferProvider.get() != nullptr) {
         mReformatBufferProvider->setBufferProvider(bufferProvider);
@@ -813,14 +814,15 @@
     if (track->mInputBufferProvider == bufferProvider) {
         return; // don't reset any buffer providers if identical.
     }
-    if (track->mReformatBufferProvider.get() != nullptr) {
-        track->mReformatBufferProvider->reset();
-    } else if (track->mDownmixerBufferProvider != nullptr) {
-        track->mDownmixerBufferProvider->reset();
+    // reset order from downstream to upstream buffer providers.
+    if (track->mTimestretchBufferProvider.get() != nullptr) {
+        track->mTimestretchBufferProvider->reset();
     } else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) {
         track->mPostDownmixReformatBufferProvider->reset();
-    } else if (track->mTimestretchBufferProvider.get() != nullptr) {
-        track->mTimestretchBufferProvider->reset();
+    } else if (track->mDownmixerBufferProvider != nullptr) {
+        track->mDownmixerBufferProvider->reset();
+    } else if (track->mReformatBufferProvider.get() != nullptr) {
+        track->mReformatBufferProvider->reset();
     }
 
     track->mInputBufferProvider = bufferProvider;
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 0fb5abc..0672ca9 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -56,9 +56,11 @@
 
     static_libs: [
         "libmedia_helper",
+        "libmediaplayer2-protos",
+        "libprotobuf-cpp-lite",
         "libstagefright_nuplayer2",
         "libstagefright_rtsp",
-        "libstagefright_timedtext",
+        "libstagefright_timedtext2",
     ],
 
     export_include_dirs: [
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index a6bf543..55d812b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -33,6 +33,10 @@
 #include <media/stagefright/foundation/AHandler.h>
 #include <mediaplayer2/MediaPlayer2Types.h>
 
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
 // Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
 // global, and not in android::
 struct sockaddr_in;
@@ -56,7 +60,8 @@
 class MediaPlayer2InterfaceListener: public RefBase
 {
 public:
-    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+           const PlayerMessage *obj) = 0;
 };
 
 class MediaPlayer2Interface : public AHandler {
@@ -217,7 +222,7 @@
     //                data sent by the java layer.
     // @param[out] reply Parcel to hold the reply data. Cannot be null.
     // @return OK if the call was successful.
-    virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+    virtual status_t invoke(const PlayerMessage &request, PlayerMessage *reply) = 0;
 
     // The Client in the MetadataPlayerService calls this method on
     // the native player to retrieve all or a subset of metadata.
@@ -236,7 +241,7 @@
         mListener = listener;
     }
 
-    void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+    void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const PlayerMessage *obj=NULL) {
         sp<MediaPlayer2InterfaceListener> listener;
         {
             Mutex::Autolock autoLock(mListenerLock);
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 43fba23..e03b499 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -42,7 +42,8 @@
 class MediaPlayer2Listener: virtual public RefBase
 {
 public:
-    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+            const PlayerMessage *obj = NULL) = 0;
 };
 
 class MediaPlayer2 : public MediaPlayer2InterfaceListener
@@ -89,8 +90,8 @@
             bool            isLooping();
             status_t        setVolume(float leftVolume, float rightVolume);
             void            notify(int64_t srcId, int msg, int ext1, int ext2,
-                                   const Parcel *obj = NULL);
-            status_t        invoke(const Parcel& request, Parcel *reply);
+                                   const PlayerMessage *obj = NULL);
+            status_t        invoke(const PlayerMessage &request, PlayerMessage *reply);
             status_t        setMetadataFilter(const Parcel& filter);
             status_t        getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
             status_t        setAudioSessionId(audio_session_t sessionId);
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index f0ea59e..8d0df8a 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -223,7 +223,8 @@
 
     ~proxyListener() { };
 
-    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+            const PlayerMessage *obj) override {
         sp<MediaPlayer2> player = mPlayer.promote();
         if (player != NULL) {
             player->notify(srcId, msg, ext1, ext2, obj);
@@ -565,16 +566,15 @@
     return mPlayer->playNextDataSource(srcId);
 }
 
-status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply) {
+status_t MediaPlayer2::invoke(const PlayerMessage &request, PlayerMessage *reply) {
     Mutex::Autolock _l(mLock);
     const bool hasBeenInitialized =
             (mCurrentState != MEDIA_PLAYER2_STATE_ERROR) &&
             ((mCurrentState & MEDIA_PLAYER2_IDLE) != MEDIA_PLAYER2_IDLE);
     if ((mPlayer == NULL) || !hasBeenInitialized) {
-        ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+        ALOGE("invoke() failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
         return INVALID_OPERATION;
     }
-    ALOGV("invoke %zu", request.dataSize());
     return mPlayer->invoke(request, reply);
 }
 
@@ -1272,7 +1272,7 @@
     }
 }
 
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) {
     ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
           (long long)srcId, msg, ext1, ext2);
 
diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp
index 1634f35..dd91628 100644
--- a/media/libmediaplayer2/nuplayer2/Android.bp
+++ b/media/libmediaplayer2/nuplayer2/Android.bp
@@ -54,6 +54,7 @@
 
     static_libs: [
         "libmedia_helper",
+        "libmediaplayer2-protos",
     ],
 
     name: "libstagefright_nuplayer2",
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index e317e23..4ce1a88 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1590,15 +1590,20 @@
         return OK; // source without DRM info
     }
 
-    sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(psshInfo);
-    ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)drmInfoBuffer->size());
+    PlayerMessage playerMsg;
+    status_t ret = NuPlayer2Drm::retrieveDrmInfo(psshInfo, &playerMsg);
+    ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)playerMsg.ByteSize());
 
-    if (drmInfoBuffer->size() == 0) {
-        ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+    if (ret != OK) {
+        ALOGE("checkDrmInfo: failed to retrive DrmInfo %d", ret);
         return UNKNOWN_ERROR;
     }
 
-    notifyDrmInfo(drmInfoBuffer);
+    int size = playerMsg.ByteSize();
+    sp<ABuffer> drmInfoBuf = new ABuffer(size);
+    playerMsg.SerializeToArray(drmInfoBuf->data(), size);
+    drmInfoBuf->setRange(0, size);
+    notifyDrmInfo(drmInfoBuf);
 
     return OK;
 }
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index b6b9b78..2f90825 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -33,7 +33,7 @@
 #include "NuPlayer2Source.h"
 #include "RTSPSource2.h"
 #include "GenericSource2.h"
-#include "TextDescriptions.h"
+#include "TextDescriptions2.h"
 
 #include "ATSParser.h"
 
@@ -584,9 +584,8 @@
     msg->post();
 }
 
-
 void NuPlayer2::writeTrackInfo(
-        Parcel* reply, const sp<AMessage>& format) const {
+        PlayerMessage* reply, const sp<AMessage>& format) const {
     if (format == NULL) {
         ALOGE("NULL format");
         return;
@@ -619,10 +618,9 @@
         return;
     }
 
-    reply->writeInt32(2); // write something non-zero
-    reply->writeInt32(trackType);
-    reply->writeString16(String16(mime.c_str()));
-    reply->writeString16(String16(lang.c_str()));
+    reply->add_values()->set_int32_value(trackType);
+    reply->add_values()->set_string_value(mime.c_str());
+    reply->add_values()->set_string_value(lang.c_str());
 
     if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
         int32_t isAuto, isDefault, isForced;
@@ -630,9 +628,9 @@
         CHECK(format->findInt32("default", &isDefault));
         CHECK(format->findInt32("forced", &isForced));
 
-        reply->writeInt32(isAuto);
-        reply->writeInt32(isDefault);
-        reply->writeInt32(isForced);
+        reply->add_values()->set_int32_value(isAuto);
+        reply->add_values()->set_int32_value(isDefault);
+        reply->add_values()->set_int32_value(isForced);
     }
 }
 
@@ -764,7 +762,7 @@
             sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            Parcel* reply;
+            PlayerMessage* reply;
             CHECK(msg->findPointer("reply", (void**)&reply));
 
             size_t inbandTracks = 0;
@@ -778,7 +776,7 @@
             }
 
             // total track count
-            reply->writeInt32(inbandTracks + ccTracks);
+            reply->add_values()->set_int32_value(inbandTracks + ccTracks);
 
             // write inband tracks
             for (size_t i = 0; i < inbandTracks; ++i) {
@@ -806,9 +804,9 @@
                 media_track_type type = (media_track_type)type32;
                 ssize_t selectedTrack = mSource->getSelectedTrack(type);
 
-                Parcel* reply;
+                PlayerMessage* reply;
                 CHECK(msg->findPointer("reply", (void**)&reply));
-                reply->writeInt32(selectedTrack);
+                reply->add_values()->set_int32_value(selectedTrack);
             }
 
             sp<AMessage> response = new AMessage;
@@ -2147,7 +2145,8 @@
             displayHeight);
 }
 
-void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+void NuPlayer2::notifyListener(
+        int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
     if (mDriver == NULL) {
         return;
     }
@@ -2231,7 +2230,7 @@
     return OK;
 }
 
-status_t NuPlayer2::getTrackInfo(Parcel* reply) const {
+status_t NuPlayer2::getTrackInfo(PlayerMessage* reply) const {
     sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, this);
     msg->setPointer("reply", reply);
 
@@ -2240,7 +2239,7 @@
     return err;
 }
 
-status_t NuPlayer2::getSelectedTrack(int32_t type, Parcel* reply) const {
+status_t NuPlayer2::getSelectedTrack(int32_t type, PlayerMessage* reply) const {
     sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
     msg->setPointer("reply", reply);
     msg->setInt32("type", type);
@@ -2618,15 +2617,15 @@
         // Modular DRM
         case Source::kWhatDrmInfo:
         {
-            Parcel parcel;
+            PlayerMessage playerMsg;
             sp<ABuffer> drmInfo;
             CHECK(msg->findBuffer("drmInfo", &drmInfo));
-            parcel.setData(drmInfo->data(), drmInfo->size());
+            playerMsg.ParseFromArray(drmInfo->data(), drmInfo->size());
 
-            ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p  parcel size: %zu",
-                    drmInfo.get(), parcel.dataSize());
+            ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p  playerMsg size: %d",
+                    drmInfo.get(), playerMsg.ByteSize());
 
-            notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+            notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &playerMsg);
 
             break;
         }
@@ -2847,35 +2846,31 @@
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
     CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
 
-    Parcel in;
-    in.writeInt32(trackIndex + baseIndex);
-    in.writeInt64(timeUs);
-    in.writeInt64(durationUs);
-    in.writeInt32(buffer->size());
-    in.writeInt32(buffer->size());
-    in.write(buffer->data(), buffer->size());
+    PlayerMessage playerMsg;
+    playerMsg.add_values()->set_int32_value(trackIndex + baseIndex);
+    playerMsg.add_values()->set_int64_value(timeUs);
+    playerMsg.add_values()->set_int64_value(durationUs);
+    playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
 
-    notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in);
+    notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &playerMsg);
 }
 
 void NuPlayer2::sendTimedMetaData(const sp<ABuffer> &buffer) {
     int64_t timeUs;
     CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
-    Parcel in;
-    in.writeInt64(timeUs);
-    in.writeInt32(buffer->size());
-    in.writeInt32(buffer->size());
-    in.write(buffer->data(), buffer->size());
+    PlayerMessage playerMsg;
+    playerMsg.add_values()->set_int64_value(timeUs);
+    playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
 
-    notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in);
+    notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &playerMsg);
 }
 
 void NuPlayer2::sendTimedTextData(const sp<ABuffer> &buffer) {
     const void *data;
     size_t size = 0;
     int64_t timeUs;
-    int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP;
+    int32_t flag = TextDescriptions2::IN_BAND_TEXT_3GPP;
 
     AString mime;
     CHECK(buffer->meta()->findString("mime", &mime));
@@ -2884,21 +2879,21 @@
     data = buffer->data();
     size = buffer->size();
 
-    Parcel parcel;
+    PlayerMessage playerMsg;
     if (size > 0) {
         CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
         int32_t global = 0;
         if (buffer->meta()->findInt32("global", &global) && global) {
-            flag |= TextDescriptions::GLOBAL_DESCRIPTIONS;
+            flag |= TextDescriptions2::GLOBAL_DESCRIPTIONS;
         } else {
-            flag |= TextDescriptions::LOCAL_DESCRIPTIONS;
+            flag |= TextDescriptions2::LOCAL_DESCRIPTIONS;
         }
-        TextDescriptions::getParcelOfDescriptions(
-                (const uint8_t *)data, size, flag, timeUs / 1000, &parcel);
+        TextDescriptions2::getPlayerMessageOfDescriptions(
+                (const uint8_t *)data, size, flag, timeUs / 1000, &playerMsg);
     }
 
-    if ((parcel.dataSize() > 0)) {
-        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+    if (playerMsg.values_size() > 0) {
+        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &playerMsg);
     } else {  // send an empty timed text
         notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
     }
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index 96f85f9..77845ac 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -23,6 +23,10 @@
 
 #include <mediaplayer2/MediaPlayer2Interface.h>
 
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
 namespace android {
 
 struct ABuffer;
@@ -77,8 +81,8 @@
             bool needNotify = false);
 
     status_t setVideoScalingMode(int32_t mode);
-    status_t getTrackInfo(Parcel* reply) const;
-    status_t getSelectedTrack(int32_t type, Parcel* reply) const;
+    status_t getTrackInfo(PlayerMessage* reply) const;
+    status_t getSelectedTrack(int32_t type, PlayerMessage* reply) const;
     status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
     status_t getCurrentPosition(int64_t *mediaUs);
     void getStats(Vector<sp<AMessage> > *mTrackStats);
@@ -292,7 +296,7 @@
             const sp<AMessage> &inputFormat,
             const sp<AMessage> &outputFormat = NULL);
 
-    void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL);
+    void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in = NULL);
 
     void handleFlushComplete(bool audio, bool isDecoder);
     void finishFlushIfPossible();
@@ -333,7 +337,7 @@
     void sendTimedMetaData(const sp<ABuffer> &buffer);
     void sendTimedTextData(const sp<ABuffer> &buffer);
 
-    void writeTrackInfo(Parcel* reply, const sp<AMessage>& format) const;
+    void writeTrackInfo(PlayerMessage* reply, const sp<AMessage>& format) const;
 
     status_t onPrepareDrm(const sp<AMessage> &msg);
     status_t onReleaseDrm();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index f85e3a2..4d799b8 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -36,45 +36,47 @@
 
 #include <media/IMediaAnalyticsService.h>
 
+using google::protobuf::RepeatedPtrField;
+using android::media::MediaPlayer2Proto::Value;
+
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleepUs = 20000;
 
 namespace android {
 
-struct ParcelWrapper : public RefBase {
-    static sp<ParcelWrapper> Create(const Parcel *p) {
+struct PlayerMessageWrapper : public RefBase {
+    static sp<PlayerMessageWrapper> Create(const PlayerMessage *p) {
         if (p != NULL) {
-            sp<ParcelWrapper> pw = new ParcelWrapper();
-            if (pw->appendFrom(p) == OK) {
-                return pw;
-            }
+            sp<PlayerMessageWrapper> pw = new PlayerMessageWrapper();
+            pw->copyFrom(p);
+            return pw;
         }
         return NULL;
     }
 
-    const Parcel *getParcel() {
-        return mParcel;
+    const PlayerMessage *getPlayerMessage() {
+        return mPlayerMessage;
     }
 
 protected:
-    virtual ~ParcelWrapper() {
-        if (mParcel != NULL) {
-            delete mParcel;
+    virtual ~PlayerMessageWrapper() {
+        if (mPlayerMessage != NULL) {
+            delete mPlayerMessage;
         }
     }
 
 private:
-    ParcelWrapper()
-        : mParcel(NULL) { }
+    PlayerMessageWrapper()
+        : mPlayerMessage(NULL) { }
 
-    status_t appendFrom(const Parcel *p) {
-        if (mParcel == NULL) {
-            mParcel = new Parcel;
+    void copyFrom(const PlayerMessage *p) {
+        if (mPlayerMessage == NULL) {
+            mPlayerMessage = new PlayerMessage;
         }
-        return mParcel->appendFrom(p, 0 /* start */, p->dataSize());
+        mPlayerMessage->CopyFrom(*p);
     }
 
-    Parcel *mParcel;
+    PlayerMessage *mPlayerMessage;
 };
 
 // key for media statistics
@@ -591,34 +593,30 @@
     return OK;
 }
 
-status_t NuPlayer2Driver::invoke(const Parcel &request, Parcel *reply) {
-    if (reply == NULL) {
+status_t NuPlayer2Driver::invoke(const PlayerMessage &request, PlayerMessage *response) {
+    if (response == NULL) {
         ALOGE("reply is a NULL pointer");
         return BAD_VALUE;
     }
 
-    int32_t methodId;
-    status_t ret = request.readInt32(&methodId);
-    if (ret != OK) {
-        ALOGE("Failed to retrieve the requested method to invoke, err(%d)", ret);
-        return ret;
-    }
+    RepeatedPtrField<const Value>::const_iterator it = request.values().cbegin();
+    int32_t methodId = (it++)->int32_value();
 
     switch (methodId) {
         case MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE:
         {
-            int mode = request.readInt32();
+            int mode = (it++)->int32_value();
             return mPlayer->setVideoScalingMode(mode);
         }
 
         case MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO:
         {
-            return mPlayer->getTrackInfo(reply);
+            return mPlayer->getTrackInfo(response);
         }
 
         case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK:
         {
-            int trackIndex = request.readInt32();
+            int trackIndex = (it++)->int32_value();
             int64_t msec = 0;
             // getCurrentPosition should always return OK
             getCurrentPosition(&msec);
@@ -627,14 +625,14 @@
 
         case MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK:
         {
-            int trackIndex = request.readInt32();
+            int trackIndex = (it++)->int32_value();
             return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
         }
 
         case MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK:
         {
-            int32_t type = request.readInt32();
-            return mPlayer->getSelectedTrack(type, reply);
+            int32_t type = (it++)->int32_value();
+            return mPlayer->getSelectedTrack(type, response);
         }
 
         default:
@@ -829,12 +827,12 @@
             CHECK(msg->findInt32("messageId", &msgId));
             msg->findInt32("ext1", &ext1);
             msg->findInt32("ext2", &ext2);
-            sp<ParcelWrapper> in;
+            sp<PlayerMessageWrapper> in;
             sp<RefBase> obj;
-            if (msg->findObject("parcel", &obj) && obj != NULL) {
-                in = static_cast<ParcelWrapper *>(obj.get());
+            if (msg->findObject("obj", &obj) && obj != NULL) {
+                in = static_cast<PlayerMessageWrapper *>(obj.get());
             }
-            sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
+            sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getPlayerMessage()));
             break;
         }
         default:
@@ -843,16 +841,16 @@
 }
 
 void NuPlayer2Driver::notifyListener(
-        int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+        int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
     Mutex::Autolock autoLock(mLock);
     notifyListener_l(srcId, msg, ext1, ext2, in);
 }
 
 void NuPlayer2Driver::notifyListener_l(
-        int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+        int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
     ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
             this, (long long)srcId, msg, ext1, ext2,
-            (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
+            (in == NULL ? -1 : (int)in->ByteSize()), mAutoLoop, mLooping);
     if (srcId == mSrcId) {
         switch (msg) {
             case MEDIA2_PLAYBACK_COMPLETE:
@@ -919,7 +917,7 @@
     notify->setInt32("messageId", msg);
     notify->setInt32("ext1", ext1);
     notify->setInt32("ext2", ext2);
-    notify->setObject("parcel", ParcelWrapper::Create(in));
+    notify->setObject("obj", PlayerMessageWrapper::Create((PlayerMessage*)in));
     notify->post();
 }
 
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 6d5a007..50ee173 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -56,7 +56,7 @@
     virtual status_t reset() override;
     virtual status_t notifyAt(int64_t mediaTimeUs) override;
     virtual status_t setLooping(int loop) override;
-    virtual status_t invoke(const Parcel &request, Parcel *reply) override;
+    virtual status_t invoke(const PlayerMessage &request, PlayerMessage *response) override;
     virtual void setAudioSink(const sp<AudioSink> &audioSink) override;
     virtual status_t setParameter(int key, const Parcel &request) override;
     virtual status_t getParameter(int key, Parcel *reply) override;
@@ -78,7 +78,7 @@
     void notifyRebufferingWhenExit(int64_t srcId, bool status);
     void notifySeekComplete(int64_t srcId);
     void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
-                        const Parcel *in = NULL);
+                        const PlayerMessage *in = NULL);
     void notifyFlagsChanged(int64_t srcId, uint32_t flags);
 
     // Modular DRM
@@ -145,7 +145,7 @@
 
     status_t start_l();
     void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
-                          const Parcel *in = NULL);
+                          const PlayerMessage *in = NULL);
 
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver);
 };
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
index 4853ae1..57d1147 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
@@ -133,9 +133,8 @@
     return drmInfoBuffer;
 }
 
-sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo)
+status_t NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo, PlayerMessage *playerMsg)
 {
-
     std::ostringstream pssh, drmInfo;
 
     // 0) Generate PSSH bytes
@@ -153,22 +152,20 @@
     ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  PSSH: size: %u %s", psshSize, psshHex);
 
     // 1) Write PSSH bytes
-    drmInfo.write(reinterpret_cast<const char *>(&psshSize), sizeof(psshSize));
-    drmInfo.write(reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
+    playerMsg->add_values()->set_bytes_value(
+            reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
 
     // 2) Write supportedDRMs
     uint32_t numentries = psshInfo->numentries;
-    drmInfo.write(reinterpret_cast<const char *>(&numentries), sizeof(numentries));
+    playerMsg->add_values()->set_int32_value(numentries);
     for (size_t i = 0; i < numentries; i++) {
         PsshEntry *entry = &psshInfo->entries[i];
-        drmInfo.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+        playerMsg->add_values()->set_bytes_value(
+                reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
         ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  supportedScheme[%zu] %s", i,
                 DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
     }
-
-    sp<ABuffer> drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp());
-    drmInfoBuf->setRange(0, drmInfo.tellp());
-    return drmInfoBuf;
+    return OK;
 }
 
 }   // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
index 99d2415..968d1be 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
@@ -23,6 +23,10 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
 namespace android {
 
     struct DrmUUID {
@@ -80,7 +84,7 @@
 
     public:
         static sp<ABuffer> retrieveDrmInfo(const void *pssh, uint32_t psshsize);
-        static sp<ABuffer> retrieveDrmInfo(PsshInfo *);
+        static status_t retrieveDrmInfo(PsshInfo *, PlayerMessage *);
 
     };  // NuPlayer2Drm
 
diff --git a/media/libnblog/Android.bp b/media/libnblog/Android.bp
index 74aaf77..4a4aac1 100644
--- a/media/libnblog/Android.bp
+++ b/media/libnblog/Android.bp
@@ -13,9 +13,14 @@
         "libbinder",
         "libcutils",
         "liblog",
+        "libmediametrics",
         "libutils",
     ],
 
+    static_libs: [
+        "libjsoncpp",
+    ],
+
     cflags: [
         "-Werror",
         "-Wall",
diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp
index d659445..aee0ea3 100644
--- a/media/libnblog/NBLog.cpp
+++ b/media/libnblog/NBLog.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <climits>
 #include <math.h>
+#include <memory>
 #include <unordered_set>
 #include <vector>
 #include <stdarg.h>
@@ -31,12 +32,14 @@
 #include <time.h>
 #include <new>
 #include <audio_utils/roundup.h>
+#include <json/json.h>
 #include <media/nblog/NBLog.h>
 #include <media/nblog/PerformanceAnalysis.h>
 #include <media/nblog/ReportPerformance.h>
 #include <utils/CallStack.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
 
 #include <queue>
 #include <utility>
@@ -68,7 +71,7 @@
     }
     const uint8_t type = EntryIterator(ptr)->type;
     switch (type) {
-    case EVENT_START_FMT:
+    case EVENT_FMT_START:
         return std::make_unique<FormatEntry>(FormatEntry(ptr));
     case EVENT_AUDIO_STATE:
     case EVENT_HISTOGRAM_ENTRY_TS:
@@ -107,7 +110,7 @@
     ++it; // skip timestamp
     ++it; // skip hash
     // Skip author if present
-    if (it->type == EVENT_AUTHOR) {
+    if (it->type == EVENT_FMT_AUTHOR) {
         ++it;
     }
     return it;
@@ -138,7 +141,7 @@
     ++it; // skip timestamp
     ++it; // skip hash
     // if there is an author entry, return it, return -1 otherwise
-    return it->type == EVENT_AUTHOR ? it.payload<int>() : -1;
+    return it->type == EVENT_FMT_AUTHOR ? it.payload<int>() : -1;
 }
 
 NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor(
@@ -151,14 +154,14 @@
     // insert author entry
     size_t authorEntrySize = Entry::kOverhead + sizeof(author);
     uint8_t authorEntry[authorEntrySize];
-    authorEntry[offsetof(entry, type)] = EVENT_AUTHOR;
+    authorEntry[offsetof(entry, type)] = EVENT_FMT_AUTHOR;
     authorEntry[offsetof(entry, length)] =
         authorEntry[authorEntrySize + Entry::kPreviousLengthOffset] =
         sizeof(author);
     *(int*) (&authorEntry[offsetof(entry, data)]) = author;
     dst->write(authorEntry, authorEntrySize);
     // copy rest of entries
-    while ((++it)->type != EVENT_END_FMT) {
+    while ((++it)->type != EVENT_FMT_END) {
         it.copyTo(dst);
     }
     it.copyTo(dst);
@@ -394,46 +397,12 @@
     if (!mEnabled) {
         return;
     }
-    int64_t ts = get_monotonic_ns();
-    if (ts > 0) {
+    struct timespec ts;
+    if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
         log(EVENT_TIMESTAMP, &ts, sizeof(ts));
-    } else {
-        ALOGE("Failed to get timestamp");
     }
 }
 
-void NBLog::Writer::logTimestamp(const int64_t ts)
-{
-    if (!mEnabled) {
-        return;
-    }
-    log(EVENT_TIMESTAMP, &ts, sizeof(ts));
-}
-
-void NBLog::Writer::logInteger(const int x)
-{
-    if (!mEnabled) {
-        return;
-    }
-    log(EVENT_INTEGER, &x, sizeof(x));
-}
-
-void NBLog::Writer::logFloat(const float x)
-{
-    if (!mEnabled) {
-        return;
-    }
-    log(EVENT_FLOAT, &x, sizeof(x));
-}
-
-void NBLog::Writer::logPID()
-{
-    if (!mEnabled) {
-        return;
-    }
-    log(EVENT_PID, mPidTag, mPidTagSize);
-}
-
 void NBLog::Writer::logStart(const char *fmt)
 {
     if (!mEnabled) {
@@ -443,24 +412,20 @@
     if (length > Entry::kMaxLength) {
         length = Entry::kMaxLength;
     }
-    log(EVENT_START_FMT, fmt, length);
+    log(EVENT_FMT_START, fmt, length);
 }
 
-void NBLog::Writer::logEnd()
+void NBLog::Writer::logTimestampFormat()
 {
     if (!mEnabled) {
         return;
     }
-    Entry entry = Entry(EVENT_END_FMT, NULL, 0);
-    log(entry, true);
-}
-
-void NBLog::Writer::logHash(log_hash_t hash)
-{
-    if (!mEnabled) {
-        return;
+    const nsecs_t ts = systemTime();
+    if (ts > 0) {
+        log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts));
+    } else {
+        ALOGE("Failed to get timestamp");
     }
-    log(EVENT_HASH, &hash, sizeof(hash));
 }
 
 void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash)
@@ -470,7 +435,7 @@
     }
     HistTsEntry data;
     data.hash = hash;
-    data.ts = get_monotonic_ns();
+    data.ts = systemTime();
     if (data.ts > 0) {
         log(event, &data, sizeof(data));
     } else {
@@ -478,14 +443,6 @@
     }
 }
 
-void NBLog::Writer::logMonotonicCycleTime(uint32_t monotonicNs)
-{
-    if (!mEnabled) {
-        return;
-    }
-    log(EVENT_MONOTONIC_CYCLE_TIME, &monotonicNs, sizeof(&monotonicNs));
-}
-
 void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...)
 {
     if (!mEnabled) {
@@ -504,11 +461,13 @@
     }
     Writer::logStart(fmt);
     int i;
-    double f;
+    double d;
+    float f;
     char* s;
+    size_t length;
     int64_t t;
-    Writer::logTimestamp();
-    Writer::logHash(hash);
+    Writer::logTimestampFormat();
+    log(EVENT_FMT_HASH, &hash, sizeof(hash));
     for (const char *p = fmt; *p != '\0'; p++) {
         // TODO: implement more complex formatting such as %.3f
         if (*p != '%') {
@@ -517,26 +476,31 @@
         switch(*++p) {
         case 's': // string
             s = va_arg(argp, char *);
-            Writer::log(s);
+            length = strlen(s);
+            if (length > Entry::kMaxLength) {
+                length = Entry::kMaxLength;
+            }
+            log(EVENT_FMT_STRING, s, length);
             break;
 
         case 't': // timestamp
             t = va_arg(argp, int64_t);
-            Writer::logTimestamp(t);
+            log(EVENT_FMT_TIMESTAMP, &t, sizeof(t));
             break;
 
         case 'd': // integer
             i = va_arg(argp, int);
-            Writer::logInteger(i);
+            log(EVENT_FMT_INTEGER, &i, sizeof(i));
             break;
 
         case 'f': // float
-            f = va_arg(argp, double); // float arguments are promoted to double in vararg lists
-            Writer::logFloat((float)f);
+            d = va_arg(argp, double); // float arguments are promoted to double in vararg lists
+            f = (float)d;
+            log(EVENT_FMT_FLOAT, &f, sizeof(f));
             break;
 
         case 'p': // pid
-            Writer::logPID();
+            log(EVENT_FMT_PID, mPidTag, mPidTagSize);
             break;
 
         // the "%\0" case finishes parsing
@@ -552,7 +516,8 @@
             break;
         }
     }
-    Writer::logEnd();
+    Entry etr(EVENT_FMT_END, nullptr, 0);
+    log(etr, true);
 }
 
 void NBLog::Writer::log(Event event, const void *data, size_t length)
@@ -622,79 +587,6 @@
 {
 }
 
-void NBLog::LockedWriter::log(const char *string)
-{
-    Mutex::Autolock _l(mLock);
-    Writer::log(string);
-}
-
-void NBLog::LockedWriter::logf(const char *fmt, ...)
-{
-    // FIXME should not take the lock until after formatting is done
-    Mutex::Autolock _l(mLock);
-    va_list ap;
-    va_start(ap, fmt);
-    Writer::logvf(fmt, ap);
-    va_end(ap);
-}
-
-void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
-{
-    // FIXME should not take the lock until after formatting is done
-    Mutex::Autolock _l(mLock);
-    Writer::logvf(fmt, ap);
-}
-
-void NBLog::LockedWriter::logTimestamp()
-{
-    // FIXME should not take the lock until after the clock_gettime() syscall
-    Mutex::Autolock _l(mLock);
-    Writer::logTimestamp();
-}
-
-void NBLog::LockedWriter::logTimestamp(const int64_t ts)
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logTimestamp(ts);
-}
-
-void NBLog::LockedWriter::logInteger(const int x)
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logInteger(x);
-}
-
-void NBLog::LockedWriter::logFloat(const float x)
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logFloat(x);
-}
-
-void NBLog::LockedWriter::logPID()
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logPID();
-}
-
-void NBLog::LockedWriter::logStart(const char *fmt)
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logStart(fmt);
-}
-
-
-void NBLog::LockedWriter::logEnd()
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logEnd();
-}
-
-void NBLog::LockedWriter::logHash(log_hash_t hash)
-{
-    Mutex::Autolock _l(mLock);
-    Writer::logHash(hash);
-}
-
 bool NBLog::LockedWriter::isEnabled() const
 {
     Mutex::Autolock _l(mLock);
@@ -707,19 +599,47 @@
     return Writer::setEnabled(enabled);
 }
 
+void NBLog::LockedWriter::log(const Entry &entry, bool trusted) {
+    Mutex::Autolock _l(mLock);
+    Writer::log(entry, trusted);
+}
+
 // ---------------------------------------------------------------------------
 
-const std::unordered_set<NBLog::Event> NBLog::Reader::startingTypes {
-        NBLog::Event::EVENT_START_FMT,
-        NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
-        NBLog::Event::EVENT_AUDIO_STATE,
-        NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME
+// We make a set of the invalid types rather than the valid types when aligning
+// Snapshot EntryIterators to valid entries during log corruption checking.
+// This is done in order to avoid the maintenance overhead of adding a new NBLog::Event
+// type to the two sets below whenever a new NBLog::Event type is created, as it is
+// very likely that new types added will be valid types.
+// Currently, invalidBeginTypes and invalidEndTypes are used to handle the special
+// case of a Format Entry, which consists of a variable number of simple log entries.
+// If a new NBLog::Event is added that consists of a variable number of simple log entries,
+// then these sets need to be updated.
+
+// We want the beginning of a Snapshot to point to an entry that is not in
+// the middle of a formatted entry and not an FMT_END.
+const std::unordered_set<NBLog::Event> NBLog::Reader::invalidBeginTypes {
+    NBLog::Event::EVENT_FMT_TIMESTAMP,
+    NBLog::Event::EVENT_FMT_HASH,
+    NBLog::Event::EVENT_FMT_STRING,
+    NBLog::Event::EVENT_FMT_INTEGER,
+    NBLog::Event::EVENT_FMT_FLOAT,
+    NBLog::Event::EVENT_FMT_PID,
+    NBLog::Event::EVENT_FMT_AUTHOR,
+    NBLog::Event::EVENT_FMT_END
 };
-const std::unordered_set<NBLog::Event> NBLog::Reader::endingTypes   {
-        NBLog::Event::EVENT_END_FMT,
-        NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
-        NBLog::Event::EVENT_AUDIO_STATE,
-        NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME
+
+// We want the end of a Snapshot to point to an entry that is not in
+// the middle of a formatted entry and not a FMT_START.
+const std::unordered_set<NBLog::Event> NBLog::Reader::invalidEndTypes   {
+    NBLog::Event::EVENT_FMT_START,
+    NBLog::Event::EVENT_FMT_TIMESTAMP,
+    NBLog::Event::EVENT_FMT_HASH,
+    NBLog::Event::EVENT_FMT_STRING,
+    NBLog::Event::EVENT_FMT_INTEGER,
+    NBLog::Event::EVENT_FMT_FLOAT,
+    NBLog::Event::EVENT_FMT_PID,
+    NBLog::Event::EVENT_FMT_AUTHOR
 };
 
 NBLog::Reader::Reader(const void *shared, size_t size, const std::string &name)
@@ -744,17 +664,22 @@
     delete mFifo;
 }
 
-const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
-                                            const std::unordered_set<Event> &types) {
+const uint8_t *NBLog::Reader::findLastValidEntry(const uint8_t *front, const uint8_t *back,
+                                            const std::unordered_set<Event> &invalidTypes) {
+    if (front == nullptr || back == nullptr) {
+        return nullptr;
+    }
     while (back + Entry::kPreviousLengthOffset >= front) {
         const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
-        if (prev < front || prev + prev[offsetof(entry, length)] +
-                            Entry::kOverhead != back) {
-
+        const Event type = (const Event)prev[offsetof(entry, type)];
+        if (prev < front
+                || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back
+                || type <= NBLog::EVENT_RESERVED || type >= NBLog::EVENT_UPPER_BOUND) {
             // prev points to an out of limits or inconsistent entry
             return nullptr;
         }
-        if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) {
+        // if invalidTypes does not contain the type, then the type is valid.
+        if (invalidTypes.find(type) == invalidTypes.end()) {
             return prev;
         }
         back = prev;
@@ -768,7 +693,7 @@
 std::unique_ptr<NBLog::Snapshot> NBLog::Reader::getSnapshot()
 {
     if (mFifoReader == NULL) {
-        return std::make_unique<Snapshot>();
+        return std::unique_ptr<Snapshot>(new Snapshot());
     }
 
     // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the
@@ -798,9 +723,20 @@
 
     if (availToRead <= 0) {
         ALOGW_IF(availToRead < 0, "NBLog Reader %s failed to catch up with Writer", mName.c_str());
-        return std::make_unique<Snapshot>();
+        return std::unique_ptr<Snapshot>(new Snapshot());
     }
 
+    // Change to #if 1 for debugging. This statement is useful for checking buffer fullness levels
+    // (as seen by reader) and how much data was lost. If you find that the fullness level is
+    // getting close to full, or that data loss is happening to often, then you should
+    // probably try some of the following:
+    // - log less data
+    // - log less often
+    // - increase the initial shared memory allocation for the buffer
+#if 0
+    ALOGD("getSnapshot name=%s, availToRead=%zd, capacity=%zu, fullness=%.3f, lost=%zu",
+            name().c_str(), availToRead, capacity, (double)availToRead / (double)capacity, lost);
+#endif
     std::unique_ptr<Snapshot> snapshot(new Snapshot(availToRead));
     memcpy(snapshot->mData, (const char *) mFifo->buffer() + iovec[0].mOffset, iovec[0].mLength);
     if (iovec[1].mLength > 0) {
@@ -811,28 +747,28 @@
     // Handle corrupted buffer
     // Potentially, a buffer has corrupted data on both beginning (due to overflow) and end
     // (due to incomplete format entry). But even if the end format entry is incomplete,
-    // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards.
+    // it ends in a complete entry (which is not an FMT_END). So is safe to traverse backwards.
     // TODO: handle client corruption (in the middle of a buffer)
 
     const uint8_t *back = snapshot->mData + availToRead;
     const uint8_t *front = snapshot->mData;
 
-    // Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry.
-    // We go backwards until we find an EVENT_END_FMT.
-    const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes);
+    // Find last FMT_END. <back> is sitting on an entry which might be the middle of a FormatEntry.
+    // We go backwards until we find an EVENT_FMT_END.
+    const uint8_t *lastEnd = findLastValidEntry(front, back, invalidEndTypes);
     if (lastEnd == nullptr) {
         snapshot->mEnd = snapshot->mBegin = EntryIterator(front);
     } else {
-        // end of snapshot points to after last END_FMT entry
+        // end of snapshot points to after last FMT_END entry
         snapshot->mEnd = EntryIterator(lastEnd).next();
-        // find first START_FMT
+        // find first FMT_START
         const uint8_t *firstStart = nullptr;
         const uint8_t *firstStartTmp = snapshot->mEnd;
-        while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes))
+        while ((firstStartTmp = findLastValidEntry(front, firstStartTmp, invalidBeginTypes))
                 != nullptr) {
             firstStart = firstStartTmp;
         }
-        // firstStart is null if no START_FMT entry was found before lastEnd
+        // firstStart is null if no FMT_START entry was found before lastEnd
         if (firstStart == nullptr) {
             snapshot->mBegin = snapshot->mEnd;
         } else {
@@ -847,36 +783,58 @@
     return snapshot;
 }
 
+// TODO separate this method from the rest of NBLog
 // Takes raw content of the local merger FIFO, processes log entries, and
 // writes the data to a map of class PerformanceAnalysis, based on their thread ID.
-void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author)
+void NBLog::MergeReader::processSnapshot(NBLog::Snapshot &snapshot, int author)
 {
-    for (const entry &etr : snapshot) {
-        switch (etr.type) {
+    ReportPerformance::PerformanceData& data = mThreadPerformanceData[author];
+    // We don't do "auto it" because it reduces readability in this case.
+    for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) {
+        switch (it->type) {
         case EVENT_HISTOGRAM_ENTRY_TS: {
-            HistTsEntry *data = (HistTsEntry *) (etr.data);
-            // TODO This memcpies are here to avoid unaligned memory access crash.
-            // There's probably a more efficient way to do it
-            log_hash_t hash;
-            memcpy(&hash, &(data->hash), sizeof(hash));
-            int64_t ts;
-            memcpy(&ts, &data->ts, sizeof(ts));
+            const HistTsEntry payload = it.payload<HistTsEntry>();
             // TODO: hash for histogram ts and audio state need to match
             // and correspond to audio production source file location
-            mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(ts);
+            mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(payload.ts);
         } break;
         case EVENT_AUDIO_STATE: {
-            HistTsEntry *data = (HistTsEntry *) (etr.data);
-            // TODO This memcpies are here to avoid unaligned memory access crash.
-            // There's probably a more efficient way to do it
-            log_hash_t hash;
-            memcpy(&hash, &(data->hash), sizeof(hash));
             mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange();
         } break;
-        case EVENT_END_FMT:
+        case EVENT_THREAD_INFO: {
+            const thread_info_t info = it.payload<thread_info_t>();
+            // TODO make PerformanceData hold a type of thread_info_t.
+            // Currently, thread_info_t is defined in NBLog.h, which includes
+            // PerformanceAnalysis.h. PerformanceData is defined in PerformanceAnalysis.h,
+            // where including NBLog.h would result in circular includes. The organization
+            // of files will need to change to avoid this problem.
+            data.type = info.type;
+            data.frameCount = info.frameCount;
+            data.sampleRate = info.sampleRate;
+        } break;
+        case EVENT_LATENCY: {
+            const double latencyMs = it.payload<double>();
+            data.latencyHist.add(latencyMs);
+        } break;
+        case EVENT_WORK_TIME: {
+            const int64_t monotonicNs = it.payload<int64_t>();
+            const double monotonicMs = monotonicNs * 1e-6;
+            data.workHist.add(monotonicMs);
+            data.active += monotonicNs;
+        } break;
+        case EVENT_WARMUP_TIME: {
+            const double timeMs = it.payload<double>();
+            data.warmupHist.add(timeMs);
+        } break;
+        case EVENT_UNDERRUN:
+            data.underruns++;
+            break;
+        case EVENT_OVERRUN:
+            data.overruns++;
+            break;
         case EVENT_RESERVED:
         case EVENT_UPPER_BOUND:
-            ALOGW("warning: unexpected event %d", etr.type);
+            ALOGW("warning: unexpected event %d", it->type);
         default:
             break;
         }
@@ -895,7 +853,20 @@
     // TODO unlock lock here
     for (size_t i = 0; i < nLogs; i++) {
         if (snapshots[i] != nullptr) {
-            getAndProcessSnapshot(*(snapshots[i]), i);
+            processSnapshot(*(snapshots[i]), i);
+        }
+    }
+    checkPushToMediaMetrics();
+}
+
+void NBLog::MergeReader::checkPushToMediaMetrics()
+{
+    const nsecs_t now = systemTime();
+    for (auto& item : mThreadPerformanceData) {
+        ReportPerformance::PerformanceData& data = item.second;
+        if (now - data.start >= kPeriodicMediaMetricsPush) {
+            (void)ReportPerformance::sendToMediaMetrics(data);
+            data.reset();   // data is persistent per thread
         }
     }
 }
@@ -904,6 +875,19 @@
 {
     // TODO: add a mutex around media.log dump
     ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis);
+    Json::Value root(Json::arrayValue);
+    for (const auto& item : mThreadPerformanceData) {
+        const ReportPerformance::PerformanceData& data = item.second;
+        std::unique_ptr<Json::Value> threadData = ReportPerformance::dumpToJson(data);
+        if (threadData == nullptr) {
+            continue;
+        }
+        (*threadData)["threadNum"] = item.first;
+        root.append(*threadData);
+    }
+    Json::StyledWriter writer;
+    std::string rootStr = writer.write(root);
+    dprintf(fd, "%s", rootStr.c_str());
 }
 
 // TODO for future compatibility, would prefer to have a dump() go to string, and then go
@@ -918,17 +902,30 @@
     String8 timestamp, body;
 
     // TODO all logged types should have a printable format.
-    for (auto it = snapshot->begin(); it != snapshot->end(); ++it) {
+    for (EntryIterator it = snapshot->begin(); it != snapshot->end(); ++it) {
         switch (it->type) {
-        case EVENT_START_FMT:
+        case EVENT_FMT_START:
             it = handleFormat(FormatEntry(it), &timestamp, &body);
             break;
-        case EVENT_MONOTONIC_CYCLE_TIME: {
-            uint32_t monotonicNs;
-            memcpy(&monotonicNs, it->data, sizeof(monotonicNs));
-            body.appendFormat("Thread cycle took %u ns", monotonicNs);
+        case EVENT_WORK_TIME: {
+            const int64_t monotonicNs = it.payload<int64_t>();
+            body.appendFormat("Thread cycle: %ld ns", (long)monotonicNs);
         } break;
-        case EVENT_END_FMT:
+        case EVENT_LATENCY: {
+            const double latencyMs = it.payload<double>();
+            body.appendFormat("latency: %.3f ms", latencyMs);
+        } break;
+        case EVENT_WARMUP_TIME: {
+            const double timeMs = it.payload<double>();
+            body.appendFormat("warmup time: %.3f ms", timeMs);
+        } break;
+        case EVENT_UNDERRUN:
+            body.appendFormat("underrun");
+            break;
+        case EVENT_OVERRUN:
+            body.appendFormat("overrun");
+            break;
+        case EVENT_FMT_END:
         case EVENT_RESERVED:
         case EVENT_UPPER_BOUND:
             body.appendFormat("warning: unexpected event %d", it->type);
@@ -1012,7 +1009,7 @@
                                                            String8 *body)
 {
     // log timestamp
-    int64_t ts = fmtEntry.timestamp();
+    const int64_t ts = fmtEntry.timestamp();
     timestamp->clear();
     timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
                     (int) ((ts / (1000 * 1000)) % 1000));
@@ -1051,7 +1048,7 @@
 
         // TODO check length for event type is correct
 
-        if (event == EVENT_END_FMT) {
+        if (event == EVENT_FMT_END) {
             break;
         }
 
@@ -1060,31 +1057,31 @@
         switch(fmt[fmt_offset])
         {
         case 's': // string
-            ALOGW_IF(event != EVENT_STRING,
+            ALOGW_IF(event != EVENT_FMT_STRING,
                 "NBLog Reader incompatible event for string specifier: %d", event);
             body->append((const char*) datum, length);
             break;
 
         case 't': // timestamp
-            ALOGW_IF(event != EVENT_TIMESTAMP,
+            ALOGW_IF(event != EVENT_FMT_TIMESTAMP,
                 "NBLog Reader incompatible event for timestamp specifier: %d", event);
             appendTimestamp(body, datum);
             break;
 
         case 'd': // integer
-            ALOGW_IF(event != EVENT_INTEGER,
+            ALOGW_IF(event != EVENT_FMT_INTEGER,
                 "NBLog Reader incompatible event for integer specifier: %d", event);
             appendInt(body, datum);
             break;
 
         case 'f': // float
-            ALOGW_IF(event != EVENT_FLOAT,
+            ALOGW_IF(event != EVENT_FMT_FLOAT,
                 "NBLog Reader incompatible event for float specifier: %d", event);
             appendFloat(body, datum);
             break;
 
         case 'p': // pid
-            ALOGW_IF(event != EVENT_PID,
+            ALOGW_IF(event != EVENT_FMT_PID,
                 "NBLog Reader incompatible event for pid specifier: %d", event);
             appendPID(body, datum, length);
             break;
@@ -1094,7 +1091,7 @@
         }
         ++arg;
     }
-    ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type);
+    ALOGW_IF(arg->type != EVENT_FMT_END, "Expected end of format, got %d", arg->type);
     return arg;
 }
 
diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp
index 3418dc0..ca9bc2c 100644
--- a/media/libnblog/PerformanceAnalysis.cpp
+++ b/media/libnblog/PerformanceAnalysis.cpp
@@ -17,13 +17,15 @@
 
 #define LOG_TAG "PerformanceAnalysis"
 // #define LOG_NDEBUG 0
+// #define WRITE_TO_FILE
 
 #include <algorithm>
 #include <climits>
 #include <deque>
-#include <iostream>
 #include <math.h>
 #include <numeric>
+#include <sstream>
+#include <string>
 #include <vector>
 #include <stdarg.h>
 #include <stdint.h>
@@ -39,6 +41,7 @@
 #include <media/nblog/ReportPerformance.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
 
 #include <queue>
 #include <utility>
@@ -47,6 +50,65 @@
 
 namespace ReportPerformance {
 
+void Histogram::add(double value)
+{
+    // TODO Handle domain and range error exceptions?
+    const int binIndex = lround((value - mLow) / mBinSize);
+    if (binIndex < 0) {
+        mLowCount++;
+    } else if (binIndex >= mNumBins) {
+        mHighCount++;
+    } else {
+        mBins[binIndex]++;
+    }
+    mTotalCount++;
+}
+
+void Histogram::clear()
+{
+    std::fill(mBins.begin(), mBins.end(), 0);
+    mLowCount = 0;
+    mHighCount = 0;
+    mTotalCount = 0;
+}
+
+uint64_t Histogram::totalCount() const
+{
+    return mTotalCount;
+}
+
+std::string Histogram::toString() const {
+    std::stringstream ss;
+    static constexpr char kDivider = '|';
+    ss << mBinSize << "," << mNumBins << "," << mLow << ",{";
+    bool first = true;
+    if (mLowCount != 0) {
+        ss << "-1" << kDivider << mLowCount;
+        first = false;
+    }
+    for (size_t i = 0; i < mNumBins; i++) {
+        if (mBins[i] != 0) {
+            if (!first) {
+                ss << ",";
+            }
+            ss << i << kDivider << mBins[i];
+            first = false;
+        }
+    }
+    if (mHighCount != 0) {
+        if (!first) {
+            ss << ",";
+        }
+        ss << mNumBins << kDivider << mHighCount;
+        first = false;
+    }
+    ss << "}";
+
+    return ss.str();
+}
+
+//------------------------------------------------------------------------------
+
 // Given an audio processing wakeup timestamp, buckets the time interval
 // since the previous timestamp into a histogram, searches for
 // outliers, analyzes the outlier series for unexpectedly
@@ -277,7 +339,9 @@
 // writes summary of performance into specified file descriptor
 void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) {
     String8 body;
+#ifdef WRITE_TO_FILE
     const char* const kDirectory = "/data/misc/audioserver/";
+#endif
     for (auto & thread : threadPerformanceAnalysis) {
         for (auto & hash: thread.second) {
             PerformanceAnalysis& curr = hash.second;
@@ -287,9 +351,11 @@
                 dumpLine(fd, indent, body);
                 body.clear();
             }
-            // write to file
+#ifdef WRITE_TO_FILE
+            // write to file. Enable by uncommenting macro at top of file.
             writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps,
                         kDirectory, false, thread.first, hash.first);
+#endif
         }
     }
 }
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index 827e731..aab67c6 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -18,6 +18,7 @@
 
 #include <fstream>
 #include <iostream>
+#include <memory>
 #include <queue>
 #include <stdarg.h>
 #include <stdint.h>
@@ -27,6 +28,8 @@
 #include <sys/prctl.h>
 #include <sys/time.h>
 #include <utility>
+#include <json/json.h>
+#include <media/MediaAnalyticsItem.h>
 #include <media/nblog/NBLog.h>
 #include <media/nblog/PerformanceAnalysis.h>
 #include <media/nblog/ReportPerformance.h>
@@ -37,13 +40,93 @@
 
 namespace ReportPerformance {
 
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data)
+{
+    std::unique_ptr<Json::Value> rootPtr = std::make_unique<Json::Value>(Json::objectValue);
+    Json::Value& root = *rootPtr;
+    root["type"] = data.type;
+    root["frameCount"] = (Json::Value::Int)data.frameCount;
+    root["sampleRate"] = (Json::Value::Int)data.sampleRate;
+    root["workMsHist"] = data.workHist.toString();
+    root["latencyMsHist"] = data.latencyHist.toString();
+    root["warmupMsHist"] = data.warmupHist.toString();
+    root["underruns"] = (Json::Value::Int64)data.underruns;
+    root["overruns"] = (Json::Value::Int64)data.overruns;
+    root["activeMs"] = (Json::Value::Int64)ns2ms(data.active);
+    root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start);
+    return rootPtr;
+}
+
+bool sendToMediaMetrics(const PerformanceData& data)
+{
+    // See documentation for these metrics here:
+    // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing
+    static constexpr char kThreadType[] = "android.media.audiothread.type";
+    static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount";
+    static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate";
+    static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist";
+    static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist";
+    static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist";
+    static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns";
+    static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns";
+    static constexpr char kThreadActive[] = "android.media.audiothread.activeMs";
+    static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs";
+
+    std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
+
+    const Histogram &workHist = data.workHist;
+    if (workHist.totalCount() > 0) {
+        item->setCString(kThreadWorkHist, workHist.toString().c_str());
+    }
+
+    const Histogram &latencyHist = data.latencyHist;
+    if (latencyHist.totalCount() > 0) {
+        item->setCString(kThreadLatencyHist, latencyHist.toString().c_str());
+    }
+
+    const Histogram &warmupHist = data.warmupHist;
+    if (warmupHist.totalCount() > 0) {
+        item->setCString(kThreadWarmupHist, warmupHist.toString().c_str());
+    }
+
+    if (data.underruns > 0) {
+        item->setInt64(kThreadUnderruns, data.underruns);
+    }
+
+    if (data.overruns > 0) {
+        item->setInt64(kThreadOverruns, data.overruns);
+    }
+
+    // Send to Media Metrics if the record is not empty.
+    // The thread and time info are added inside the if statement because
+    // we want to send them only if there are performance metrics to send.
+    if (item->count() > 0) {
+        // Add thread info fields.
+        const int type = data.type;
+        // TODO have a int-to-string mapping defined somewhere else for other thread types.
+        if (type == 2) {
+            item->setCString(kThreadType, "FASTMIXER");
+        } else {
+            item->setCString(kThreadType, "UNKNOWN");
+        }
+        item->setInt32(kThreadFrameCount, data.frameCount);
+        item->setInt32(kThreadSampleRate, data.sampleRate);
+        // Add time info fields.
+        item->setInt64(kThreadActive, data.active / 1000000);
+        item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000);
+        return item->selfrecord();
+    }
+    return false;
+}
+
+//------------------------------------------------------------------------------
 
 // TODO: use a function like this to extract logic from writeToFile
 // https://stackoverflow.com/a/9279620
 
 // Writes outlier intervals, timestamps, and histograms spanning long time intervals to file.
 // TODO: write data in binary format
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
                  const std::deque<std::pair<msInterval, timestamp>> &outlierData,
                  const std::deque<timestamp> &peakTimestamps,
                  const char * directory, bool append, int author, log_hash_t hash) {
diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h
index c9bfaae..e43b026 100644
--- a/media/libnblog/include/media/nblog/NBLog.h
+++ b/media/libnblog/include/media/nblog/NBLog.h
@@ -20,6 +20,8 @@
 #define ANDROID_MEDIA_NBLOG_H
 
 #include <map>
+#include <memory>
+#include <type_traits>
 #include <unordered_set>
 #include <vector>
 
@@ -29,6 +31,7 @@
 #include <media/nblog/ReportPerformance.h>
 #include <utils/Mutex.h>
 #include <utils/threads.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -38,40 +41,85 @@
 
 public:
 
-    using log_hash_t = ReportPerformance::log_hash_t;
+    using log_hash_t = uint64_t;
 
     // FIXME Everything needed for client (writer API and registration) should be isolated
     //       from the rest of the implementation.
     class Writer;
     class Reader;
 
+    // TODO have a comment somewhere explaining the whole process for adding a new EVENT_
+
+    // NBLog Event types. The Events are named to provide contextual meaning for what is logged.
+    // If adding a new standalone Event here, update the event-to-type mapping by adding a
+    // MAP_EVENT_TO_TYPE statement below.
     enum Event : uint8_t {
         EVENT_RESERVED,
         EVENT_STRING,               // ASCII string, not NUL-terminated
                                     // TODO: make timestamp optional
         EVENT_TIMESTAMP,            // clock_gettime(CLOCK_MONOTONIC)
-        EVENT_INTEGER,              // integer value entry
-        EVENT_FLOAT,                // floating point value entry
-        EVENT_PID,                  // process ID and process name
-        EVENT_AUTHOR,               // author index (present in merged logs) tracks entry's
-                                    // original log
-        EVENT_START_FMT,            // logFormat start event: entry includes format string,
+
+        // Types for Format Entry, i.e. formatted entry
+        EVENT_FMT_START,            // logFormat start event: entry includes format string,
                                     // following entries contain format arguments
-        EVENT_HASH,                 // unique HASH of log origin, originates from hash of file name
+        // format arguments
+        EVENT_FMT_TIMESTAMP,        // timestamp value entry
+        EVENT_FMT_HASH,             // unique HASH of log origin, originates from hash of file name
                                     // and line number
+        EVENT_FMT_STRING,           // string value entry
+        EVENT_FMT_INTEGER,          // integer value entry
+        EVENT_FMT_FLOAT,            // floating point value entry
+        EVENT_FMT_PID,              // process ID and process name
+        EVENT_FMT_AUTHOR,           // author index (present in merged logs) tracks entry's
+                                    // original log
+        // end of format arguments
+        EVENT_FMT_END,              // end of logFormat argument list
+
+        // Types for wakeup timestamp histograms
         EVENT_HISTOGRAM_ENTRY_TS,   // single datum for timestamp histogram
         EVENT_AUDIO_STATE,          // audio on/off event: logged on FastMixer::onStateChange call
-        EVENT_END_FMT,              // end of logFormat argument list
 
         // Types representing audio performance metrics
-        EVENT_LATENCY,              // TODO classify specifically what this is
-        EVENT_CPU_FREQUENCY,        // instantaneous CPU frequency in kHz
-        EVENT_MONOTONIC_CYCLE_TIME, // thread per-cycle monotonic time
-        EVENT_CPU_CYCLE_TIME,       // thread per-cycle cpu time
+        EVENT_THREAD_INFO,          // thread type, frameCount and sampleRate, which give context
+                                    // for the metrics below.
+        EVENT_LATENCY,              // difference between frames presented by HAL and frames
+                                    // written to HAL output sink, divided by sample rate.
+        EVENT_WORK_TIME,            // the time a thread takes to do work, e.g. read, write, etc.
+        EVENT_WARMUP_TIME,          // thread warmup time
+        EVENT_UNDERRUN,             // predicted thread underrun event timestamp
+        EVENT_OVERRUN,              // predicted thread overrun event timestamp
 
         EVENT_UPPER_BOUND,          // to check for invalid events
     };
 
+    // NBLog custom-defined structs. Some NBLog Event types map to these structs.
+
+    // mapped from EVENT_THREAD_INFO
+    struct thread_info_t {
+        // TODO make type an enum
+        int type;               // Thread type: 0 for MIXER, 1 for CAPTURE,
+                                // 2 for FASTMIXER, 3 for FASTCAPTURE
+        size_t frameCount;      // number of frames per read or write buffer
+        unsigned sampleRate;    // in frames per second
+    };
+
+    template <Event E> struct get_mapped;
+#define MAP_EVENT_TO_TYPE(E, T) \
+    template<> struct get_mapped<E> { \
+        static_assert(std::is_trivially_copyable<T>::value \
+                && !std::is_pointer<T>::value, \
+                "NBLog::Event must map to trivially copyable, non-pointer type."); \
+        typedef T type; \
+    }
+
+    // Maps an NBLog Event type to a C++ POD type.
+    MAP_EVENT_TO_TYPE(EVENT_THREAD_INFO, thread_info_t);
+    MAP_EVENT_TO_TYPE(EVENT_LATENCY, double);
+    MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, int64_t);
+    MAP_EVENT_TO_TYPE(EVENT_WARMUP_TIME, double);
+    MAP_EVENT_TO_TYPE(EVENT_UNDERRUN, int64_t);
+    MAP_EVENT_TO_TYPE(EVENT_OVERRUN, int64_t);
+
 private:
 
     // ---------------------------------------------------------------------------
@@ -119,10 +167,24 @@
         void            copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
         void            copyData(uint8_t *dst) const;
 
+        // memcpy preferred to reinterpret_cast to avoid potentially unsupported
+        // unaligned memory access.
+#if 0
         template<typename T>
         inline const T& payload() {
             return *reinterpret_cast<const T *>(mPtr + offsetof(entry, data));
         }
+#else
+        template<typename T>
+        inline T payload() const {
+            static_assert(std::is_trivially_copyable<T>::value
+                    && !std::is_pointer<T>::value,
+                    "NBLog::EntryIterator payload must be trivially copyable, non-pointer type.");
+            T payload;
+            memcpy(&payload, mPtr + offsetof(entry, data), sizeof(payload));
+            return payload;
+        }
+#endif
 
         inline operator const uint8_t*() const {
             return mPtr;
@@ -169,14 +231,14 @@
     // API for handling format entry operations
 
     // a formatted entry has the following structure:
-    //    * START_FMT entry, containing the format string
+    //    * FMT_START entry, containing the format string
     //    * TIMESTAMP entry
     //    * HASH entry
     //    * author entry of the thread that generated it (optional, present in merged log)
     //    * format arg1
     //    * format arg2
     //    * ...
-    //    * END_FMT entry
+    //    * FMT_END entry
     class FormatEntry : public AbstractEntry {
     public:
         // explicit FormatEntry(const EntryIterator &it);
@@ -262,6 +324,7 @@
             offsetof(ending, length);
     };
 
+    // TODO move these somewhere else
     struct HistTsEntry {
         log_hash_t hash;
         int64_t ts;
@@ -320,6 +383,8 @@
     };
 
     // ---------------------------------------------------------------------------
+    // NBLog Writer API
+    // ---------------------------------------------------------------------------
 
     // Writer is thread-safe with respect to Reader, but not with respect to multiple threads
     // calling Writer methods.  If you need multi-thread safety for writing, use LockedWriter.
@@ -335,23 +400,22 @@
         virtual ~Writer();
 
         // FIXME needs comments, and some should be private
-        virtual void    log(const char *string);
-        virtual void    logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
-        virtual void    logvf(const char *fmt, va_list ap);
-        virtual void    logTimestamp();
-        virtual void    logTimestamp(const int64_t ts);
-        virtual void    logInteger(const int x);
-        virtual void    logFloat(const float x);
-        virtual void    logPID();
-        virtual void    logStart(const char *fmt);
-        virtual void    logEnd();
-        virtual void    logHash(log_hash_t hash);
-        // The functions below are not in LockedWriter yet.
-        virtual void    logFormat(const char *fmt, log_hash_t hash, ...);
-        virtual void    logVFormat(const char *fmt, log_hash_t hash, va_list ap);
-        virtual void    logEventHistTs(Event event, log_hash_t hash);
-        virtual void    logMonotonicCycleTime(uint32_t monotonicNs);
-        // End of functions that are not in LockedWriter yet.
+        void    log(const char *string);
+        void    logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+        void    logTimestamp();
+        void    logFormat(const char *fmt, log_hash_t hash, ...);
+        void    logEventHistTs(Event event, log_hash_t hash);
+
+        // Log data related to Event E. See the event-to-type mapping for the type of data
+        // corresponding to the event. For example, if you see a mapping statement:
+        //     MAP_TYPE_TO_EVENT(E, T);
+        // then the usage of this method would be:
+        //     T data = doComputation();
+        //     tlNBLogWriter->log<NBLog::E>(data);
+        template<Event E>
+        void    log(typename get_mapped<E>::type data) {
+            log(E, &data, sizeof(data));
+        }
 
         virtual bool    isEnabled() const;
 
@@ -362,12 +426,25 @@
 
         sp<IMemory>     getIMemory() const  { return mIMemory; }
 
+        // Public logging function implementations should always use one of the
+        // two log() function calls below to write to shared memory.
+    protected:
+        // Writes a single Entry to the FIFO if the writer is enabled.
+        // This is protected and virtual because LockedWriter uses a lock to protect
+        // writing to the FIFO before writing to this function.
+        virtual void log(const Entry &entry, bool trusted = false);
+
     private:
         // 0 <= length <= kMaxLength
-        // writes a single Entry to the FIFO
+        // Log a single Entry with corresponding event, data, and length.
         void    log(Event event, const void *data, size_t length);
-        // checks validity of an event before calling log above this one
-        void    log(const Entry &entry, bool trusted = false);
+
+        void    logvf(const char *fmt, va_list ap);
+
+        // helper functions for logging parts of a formatted entry
+        void    logStart(const char *fmt);
+        void    logTimestampFormat();
+        void    logVFormat(const char *fmt, log_hash_t hash, va_list ap);
 
         Shared* const   mShared;    // raw pointer to shared memory
         sp<IMemory>     mIMemory;   // ref-counted version, initialized in constructor
@@ -392,54 +469,20 @@
         LockedWriter();
         LockedWriter(void *shared, size_t size);
 
-        virtual void    log(const char *string);
-        virtual void    logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
-        virtual void    logvf(const char *fmt, va_list ap);
-        virtual void    logTimestamp();
-        virtual void    logTimestamp(const int64_t ts);
-        virtual void    logInteger(const int x);
-        virtual void    logFloat(const float x);
-        virtual void    logPID();
-        virtual void    logStart(const char *fmt);
-        virtual void    logEnd();
-        virtual void    logHash(log_hash_t hash);
-
-        virtual bool    isEnabled() const;
-        virtual bool    setEnabled(bool enabled);
+        bool    isEnabled() const override;
+        bool    setEnabled(bool enabled) override;
 
     private:
+        // Lock needs to be obtained before writing to FIFO.
+        void log(const Entry &entry, bool trusted = false) override;
         mutable Mutex   mLock;
     };
 
     // ---------------------------------------------------------------------------
+    // NBLog Reader API
+    // ---------------------------------------------------------------------------
 
-    // A snapshot of a readers buffer
-    // This is raw data. No analysis has been done on it
-    class Snapshot {
-    public:
-        Snapshot() = default;
-
-        explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
-
-        ~Snapshot() { delete[] mData; }
-
-        // amount of data lost (given by audio_utils_fifo_reader)
-        size_t   lost() const { return mLost; }
-
-        // iterator to beginning of readable segment of snapshot
-        // data between begin and end has valid entries
-        EntryIterator begin() const { return mBegin; }
-
-        // iterator to end of readable segment of snapshot
-        EntryIterator end() const { return mEnd; }
-
-    private:
-        friend class Reader;
-        uint8_t * const       mData{};
-        size_t                mLost{0};
-        EntryIterator mBegin;
-        EntryIterator mEnd;
-    };
+    class Snapshot;     // Forward declaration needed for Reader::getSnapshot()
 
     class Reader : public RefBase {
     public:
@@ -455,10 +498,14 @@
         const std::string &name() const { return mName; }
 
     private:
+        // Amount of tries for reader to catch up with writer in getSnapshot().
         static constexpr int kMaxObtainTries = 3;
-        // startingTypes and endingTypes are used to check for log corruption.
-        static const std::unordered_set<Event> startingTypes;
-        static const std::unordered_set<Event> endingTypes;
+
+        // invalidBeginTypes and invalidEndTypes are used to align the Snapshot::begin() and
+        // Snapshot::end() EntryIterators to valid entries.
+        static const std::unordered_set<Event> invalidBeginTypes;
+        static const std::unordered_set<Event> invalidEndTypes;
+
         // declared as const because audio_utils_fifo() constructor
         sp<IMemory> mIMemory;       // ref-counted version, assigned only in constructor
 
@@ -469,12 +516,39 @@
         audio_utils_fifo_reader * const mFifoReader;    // used to read from FIFO,
                                                         // non-NULL unless constructor fails
 
-        // Searches for the last entry of type <type> in the range [front, back)
+        // Searches for the last valid entry in the range [front, back)
         // back has to be entry-aligned. Returns nullptr if none enconuntered.
-        static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
-                                                   const std::unordered_set<Event> &types);
+        static const uint8_t *findLastValidEntry(const uint8_t *front, const uint8_t *back,
+                                                   const std::unordered_set<Event> &invalidTypes);
     };
 
+    // A snapshot of a readers buffer
+    // This is raw data. No analysis has been done on it
+    class Snapshot {
+    public:
+        ~Snapshot() { delete[] mData; }
+
+        // amount of data lost (given by audio_utils_fifo_reader)
+        size_t   lost() const { return mLost; }
+
+        // iterator to beginning of readable segment of snapshot
+        // data between begin and end has valid entries
+        EntryIterator begin() const { return mBegin; }
+
+        // iterator to end of readable segment of snapshot
+        EntryIterator end() const { return mEnd; }
+
+    private:
+        Snapshot() = default;
+        explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
+        friend std::unique_ptr<Snapshot> Reader::getSnapshot();
+        uint8_t * const       mData = nullptr;
+        size_t                mLost = 0;
+        EntryIterator mBegin;
+        EntryIterator mEnd;
+    };
+
+    // TODO move this to MediaLogService?
     class DumpReader : public Reader {
     public:
         DumpReader(const void *shared, size_t size, const std::string &name)
@@ -490,12 +564,14 @@
         static void    appendFloat(String8 *body, const void *data);
         static void    appendPID(String8 *body, const void *data, size_t length);
         static void    appendTimestamp(String8 *body, const void *data);
-        //static size_t  fmtEntryLength(const uint8_t *data);   // TODO Eric remove if not used
+
+        // The bufferDump functions are used for debugging only.
         static String8 bufferDump(const uint8_t *buffer, size_t size);
         static String8 bufferDump(const EntryIterator &it);
     };
 
     // ---------------------------------------------------------------------------
+    // TODO move Merger, MergeReader, and MergeThread to a separate file.
 
     // This class is used to read data from each thread's individual FIFO in shared memory
     // and write it to a single FIFO in local memory.
@@ -531,11 +607,17 @@
         MergeReader(const void *shared, size_t size, Merger &merger);
 
         void dump(int fd, int indent = 0);
+
         // process a particular snapshot of the reader
-        void getAndProcessSnapshot(Snapshot &snap, int author);
+        void processSnapshot(Snapshot &snap, int author);
+
         // call getSnapshot of the content of the reader's buffer and process the data
         void getAndProcessSnapshot();
 
+        // check for periodic push of performance data to media metrics, and perform
+        // the send if it is time to do so.
+        void checkPushToMediaMetrics();
+
     private:
         // FIXME Needs to be protected by a lock,
         //       because even though our use of it is read-only there may be asynchronous updates
@@ -547,6 +629,13 @@
         // location within each author
         ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis;
 
+        // compresses and stores audio performance data from each thread's buffers.
+        // first parameter is author, i.e. thread index.
+        std::map<int, ReportPerformance::PerformanceData> mThreadPerformanceData;
+
+        // how often to push data to Media Metrics
+        static constexpr nsecs_t kPeriodicMediaMetricsPush = s2ns((nsecs_t)2 * 60 * 60); // 2 hours
+
         // handle author entry by looking up the author's name and appending it to the body
         // returns number of bytes read from fmtEntry
         void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
@@ -594,15 +683,6 @@
 
 };  // class NBLog
 
-// TODO put somewhere else
-static inline int64_t get_monotonic_ns() {
-    timespec ts;
-    if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
-        return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
-    }
-    return 0; // should not happen.
-}
-
 }   // namespace android
 
 #endif  // ANDROID_MEDIA_NBLOG_H
diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
index 56e0ea6..80eddb1 100644
--- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h
+++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
@@ -19,9 +19,11 @@
 
 #include <deque>
 #include <map>
+#include <utility>
 #include <vector>
 
 #include <media/nblog/ReportPerformance.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -29,6 +31,145 @@
 
 namespace ReportPerformance {
 
+// TODO make this a templated class and put it in a separate file.
+// The templated parameters would be bin size and low limit.
+/*
+ * Histogram provides a way to store numeric data in histogram format and read it as a serialized
+ * string. The terms "bin" and "bucket" are used interchangeably.
+ *
+ * This class is not thread-safe.
+ */
+class Histogram {
+public:
+    struct Config {
+        const double binSize;   // TODO template type
+        const size_t numBins;
+        const double low;       // TODO template type
+    };
+
+    // Histograms are constructed with fixed configuration numbers. Dynamic configuration based
+    // the data is possible but complex because
+    // - data points are added one by one, not processed as a batch.
+    // - Histograms with different configuration parameters are tricky to aggregate, and they
+    //   will need to be aggregated at the Media Metrics cloud side.
+    // - not providing limits theoretically allows for infinite number of buckets.
+
+    /**
+     * \brief Creates a Histogram object.
+     *
+     * \param binSize the width of each bin of the histogram.
+     *                Units are whatever data the caller decides to store.
+     * \param numBins the number of bins desired in the histogram range.
+     * \param low     the lower bound of the histogram bucket values.
+     *                Units are whatever data the caller decides to store.
+     *                Note that the upper bound can be calculated by the following:
+     *                  upper = lower + binSize * numBins.
+     */
+    Histogram(double binSize, size_t numBins, double low = 0.)
+        : mBinSize(binSize), mNumBins(numBins), mLow(low), mBins(mNumBins) {}
+
+    Histogram(const Config &c)
+        : Histogram(c.binSize, c.numBins, c.low) {}
+
+    /**
+     * \brief Add a data point to the histogram. The value of the data point
+     *        is rounded to the nearest multiple of the bin size (before accounting
+     *        for the lower bound offset, which may not be a multiple of the bin size).
+     *
+     * \param value the value of the data point to add.
+     */
+    void add(double value);
+
+    /**
+     * \brief Removes all data points from the histogram.
+     */
+    void clear();
+
+    /**
+     * \brief Returns the total number of data points added to the histogram.
+     *
+     * \return the total number of data points in the histogram.
+     */
+    uint64_t totalCount() const;
+
+    /**
+     * \brief Serializes the histogram into a string. The format is chosen to be compatible with
+     *        the histogram representation to send to the Media Metrics service.
+     *
+     *        The string is as follows:
+     *          binSize,numBins,low,{-1|lowCount,...,binIndex|count,...,numBins|highCount}
+     *
+     *        - binIndex is an integer with 0 <= binIndex < numBins.
+     *        - count is the number of occurrences of the (rounded) value
+     *          low + binSize * bucketIndex.
+     *        - lowCount is the number of (rounded) values less than low.
+     *        - highCount is the number of (rounded) values greater than or equal to
+     *          low + binSize * numBins.
+     *        - a binIndex may be skipped if its count is 0.
+     *
+     * \return the histogram serialized as a string.
+     */
+    std::string toString() const;
+
+private:
+    const double mBinSize;      // Size of each bucket
+    const size_t mNumBins;      // Number of buckets in histogram range
+    const double mLow;          // Lower bound of values
+    std::vector<int> mBins;     // Data structure to store the actual histogram
+
+    int mLowCount = 0;          // Number of values less than mLow
+    int mHighCount = 0;         // Number of values >= mLow + mBinSize * mNumBins
+    uint64_t mTotalCount = 0;   // Total number of values recorded
+};
+
+// This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis
+// also does some additional analyzing of data, while the purpose of this struct is
+// to hold data.
+struct PerformanceData {
+    // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192
+    // and mSampleRate = 48000, which correspond to 2 and 7 seconds.
+    static constexpr Histogram::Config kWorkConfig = { 0.25, 20, 2.};
+
+    // Values based on trial and error logging. Need a better way to determine
+    // bin size and lower/upper limits.
+    static constexpr Histogram::Config kLatencyConfig = { 2., 10, 10.};
+
+    // Values based on trial and error logging. Need a better way to determine
+    // bin size and lower/upper limits.
+    static constexpr Histogram::Config kWarmupConfig = { 5., 10, 10.};
+
+    // Thread Info
+    // TODO make type an enum
+    int type = -1;              // Thread type: 0 for MIXER, 1 for CAPTURE,
+                                // 2 for FASTMIXER, 3 for FASTCAPTURE
+    size_t frameCount = 0;
+    unsigned sampleRate = 0;
+
+    // Performance Data
+    Histogram workHist{kWorkConfig};
+    Histogram latencyHist{kLatencyConfig};
+    Histogram warmupHist{kWarmupConfig};
+    int64_t underruns = 0;
+    int64_t overruns = 0;
+    nsecs_t active = 0;
+    nsecs_t start{systemTime()};
+
+    // Reset the performance data. This does not represent a thread state change.
+    // Thread info is not reset here because the data is meant to be a continuation of the thread
+    // that struct PerformanceData is associated with.
+    void reset() {
+        workHist.clear();
+        latencyHist.clear();
+        warmupHist.clear();
+        underruns = 0;
+        overruns = 0;
+        active = 0;
+        start = systemTime();
+    }
+};
+
+//------------------------------------------------------------------------------
+
 class PerformanceAnalysis;
 
 // a map of PerformanceAnalysis instances
@@ -82,7 +223,7 @@
     std::deque<timestamp> mPeakTimestamps;
 
     // stores buffer period histograms with timestamp of first sample
-    std::deque<std::pair<timestamp, Histogram>> mHists;
+    std::deque<std::pair<timestamp, Hist>> mHists;
 
     // Parameters used when detecting outliers
     struct BufferPeriod {
diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h
index 1b11197..09bb2a0 100644
--- a/media/libnblog/include/media/nblog/ReportPerformance.h
+++ b/media/libnblog/include/media/nblog/ReportPerformance.h
@@ -19,19 +19,38 @@
 
 #include <deque>
 #include <map>
+#include <memory>
 #include <vector>
 
+namespace Json {
+class Value;
+}
+
 namespace android {
 
 namespace ReportPerformance {
 
+struct PerformanceData;
+
+// Dumps performance data to a JSON format.
+// Return by pointer instead of by value to avoid dependency side effects of including
+// the header of an external library.
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data);
+
+// Send one thread's data to media metrics, if the performance data is nontrivial (i.e. not
+// all zero values). Return true if data was sent, false if there is nothing to write
+// or an error occurred while writing.
+bool sendToMediaMetrics(const PerformanceData& data);
+
+//------------------------------------------------------------------------------
+
 constexpr int kMsPerSec = 1000;
 constexpr int kSecPerMin = 60;
 
 constexpr int kJiffyPerMs = 10; // time unit for histogram as a multiple of milliseconds
 
 // stores a histogram: key: observed buffer period (multiple of jiffy). value: count
-using Histogram = std::map<int, int>;
+using Hist = std::map<int, int>;
 
 using msInterval = double;
 using jiffyInterval = double;
@@ -54,7 +73,7 @@
 }
 
 // Writes outlier intervals, timestamps, peaks timestamps, and histograms to a file.
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
                  const std::deque<std::pair<msInterval, timestamp>> &outlierData,
                  const std::deque<timestamp> &peakTimestamps,
                  const char * kDirectory, bool append, int author, log_hash_t hash);
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/AHierarchicalStateMachine.cpp
similarity index 97%
rename from media/libstagefright/foundation/AHierarchicalStateMachine.cpp
rename to media/libstagefright/AHierarchicalStateMachine.cpp
index b837f66..f89b8b0 100644
--- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
+++ b/media/libstagefright/AHierarchicalStateMachine.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "AHierarchicalStateMachine"
 #include <utils/Log.h>
 
-#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <media/stagefright/AHierarchicalStateMachine.h>
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index fec40b6..ae2f61b 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -90,9 +90,10 @@
     name: "libstagefright",
 
     srcs: [
+        "AACWriter.cpp",
         "ACodec.cpp",
         "ACodecBufferChannel.cpp",
-        "AACWriter.cpp",
+        "AHierarchicalStateMachine.cpp",
         "AMRWriter.cpp",
         "AudioPlayer.cpp",
         "AudioPresentationInfo.cpp",
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 72ddb71..a0a3a75 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -319,12 +319,6 @@
 #endif
             "/extractors", *newList);
 
-    RegisterExtractorsInSystem("/vendor/lib"
-#ifdef __LP64__
-            "64"
-#endif
-            "/extractors", *newList);
-
     if (newUpdateApkPath != nullptr) {
         RegisterExtractorsInApk(newUpdateApkPath, *newList);
     }
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
new file mode 100644
index 0000000..96818eb
--- /dev/null
+++ b/media/libstagefright/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "postsubmit": [
+    {
+      "name": "CtsMediaTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.RequiresDevice"
+        }
+      ]
+    }
+  ]
+}
diff --git a/media/libstagefright/bqhelper/Conversion.cpp b/media/libstagefright/bqhelper/Conversion.cpp
index ffed005..91d7c74 100644
--- a/media/libstagefright/bqhelper/Conversion.cpp
+++ b/media/libstagefright/bqhelper/Conversion.cpp
@@ -98,6 +98,17 @@
  */
 
 /**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+status_t toStatusT(Return<void> const& t) {
+    return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR);
+}
+
+/**
  * \brief Convert `Return<void>` to `binder::Status`.
  *
  * \param[in] t The source `Return<void>`.
@@ -107,22 +118,11 @@
 ::android::binder::Status toBinderStatus(
         Return<void> const& t) {
     return ::android::binder::Status::fromExceptionCode(
-            t.isOk() ? OK : UNKNOWN_ERROR,
+            toStatusT(t),
             t.description().c_str());
 }
 
 /**
- * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `status_t`.
- */
-// convert: Return<void> -> status_t
-status_t toStatusT(Return<void> const& t) {
-    return t.isOk() ? OK : UNKNOWN_ERROR;
-}
-
-/**
  * \brief Wrap `native_handle_t*` in `hidl_handle`.
  *
  * \param[in] nh The source `native_handle_t*`.
diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp
deleted file mode 100644
index eafdc37..0000000
--- a/media/libstagefright/foundation/ANetworkSession.cpp
+++ /dev/null
@@ -1,1401 +0,0 @@
-/*
- * Copyright 2012, 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 "NetworkSession"
-#include <utils/Log.h>
-
-#include "ANetworkSession.h"
-#include "ParsedMessage.h"
-
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <linux/tcp.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ByteUtils.h>
-#include <media/stagefright/foundation/hexdump.h>
-
-namespace android {
-
-static const size_t kMaxUDPSize = 1500;
-static const int32_t kMaxUDPRetries = 200;
-
-struct ANetworkSession::NetworkThread : public Thread {
-    explicit NetworkThread(ANetworkSession *session);
-
-protected:
-    virtual ~NetworkThread();
-
-private:
-    ANetworkSession *mSession;
-
-    virtual bool threadLoop();
-
-    DISALLOW_EVIL_CONSTRUCTORS(NetworkThread);
-};
-
-struct ANetworkSession::Session : public RefBase {
-    enum Mode {
-        MODE_RTSP,
-        MODE_DATAGRAM,
-        MODE_WEBSOCKET,
-    };
-
-    enum State {
-        CONNECTING,
-        CONNECTED,
-        LISTENING_RTSP,
-        LISTENING_TCP_DGRAMS,
-        DATAGRAM,
-    };
-
-    Session(int32_t sessionID,
-            State state,
-            int s,
-            const sp<AMessage> &notify);
-
-    int32_t sessionID() const;
-    int socket() const;
-    sp<AMessage> getNotificationMessage() const;
-
-    bool isRTSPServer() const;
-    bool isTCPDatagramServer() const;
-
-    bool wantsToRead();
-    bool wantsToWrite();
-
-    status_t readMore();
-    status_t writeMore();
-
-    status_t sendRequest(
-            const void *data, ssize_t size, bool timeValid, int64_t timeUs);
-
-    void setMode(Mode mode);
-
-    status_t switchToWebSocketMode();
-
-protected:
-    virtual ~Session();
-
-private:
-    enum {
-        FRAGMENT_FLAG_TIME_VALID = 1,
-    };
-    struct Fragment {
-        uint32_t mFlags;
-        int64_t mTimeUs;
-        sp<ABuffer> mBuffer;
-    };
-
-    int32_t mSessionID;
-    State mState;
-    Mode mMode;
-    int mSocket;
-    sp<AMessage> mNotify;
-    bool mSawReceiveFailure, mSawSendFailure;
-    int32_t mUDPRetries;
-
-    List<Fragment> mOutFragments;
-
-    AString mInBuffer;
-
-    int64_t mLastStallReportUs;
-
-    void notifyError(bool send, status_t err, const char *detail);
-    void notify(NotificationReason reason);
-
-    void dumpFragmentStats(const Fragment &frag);
-
-    DISALLOW_EVIL_CONSTRUCTORS(Session);
-};
-////////////////////////////////////////////////////////////////////////////////
-
-ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session)
-    : mSession(session) {
-}
-
-ANetworkSession::NetworkThread::~NetworkThread() {
-}
-
-bool ANetworkSession::NetworkThread::threadLoop() {
-    mSession->threadLoop();
-
-    return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-ANetworkSession::Session::Session(
-        int32_t sessionID,
-        State state,
-        int s,
-        const sp<AMessage> &notify)
-    : mSessionID(sessionID),
-      mState(state),
-      mMode(MODE_DATAGRAM),
-      mSocket(s),
-      mNotify(notify),
-      mSawReceiveFailure(false),
-      mSawSendFailure(false),
-      mUDPRetries(kMaxUDPRetries),
-      mLastStallReportUs(-1ll) {
-    if (mState == CONNECTED) {
-        struct sockaddr_in localAddr;
-        socklen_t localAddrLen = sizeof(localAddr);
-
-        int res = getsockname(
-                mSocket, (struct sockaddr *)&localAddr, &localAddrLen);
-        CHECK_GE(res, 0);
-
-        struct sockaddr_in remoteAddr;
-        socklen_t remoteAddrLen = sizeof(remoteAddr);
-
-        res = getpeername(
-                mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
-        CHECK_GE(res, 0);
-
-        in_addr_t addr = ntohl(localAddr.sin_addr.s_addr);
-        AString localAddrString = AStringPrintf(
-                "%d.%d.%d.%d",
-                (addr >> 24),
-                (addr >> 16) & 0xff,
-                (addr >> 8) & 0xff,
-                addr & 0xff);
-
-        addr = ntohl(remoteAddr.sin_addr.s_addr);
-        AString remoteAddrString = AStringPrintf(
-                "%d.%d.%d.%d",
-                (addr >> 24),
-                (addr >> 16) & 0xff,
-                (addr >> 8) & 0xff,
-                addr & 0xff);
-
-        sp<AMessage> msg = mNotify->dup();
-        msg->setInt32("sessionID", mSessionID);
-        msg->setInt32("reason", kWhatClientConnected);
-        msg->setString("server-ip", localAddrString.c_str());
-        msg->setInt32("server-port", ntohs(localAddr.sin_port));
-        msg->setString("client-ip", remoteAddrString.c_str());
-        msg->setInt32("client-port", ntohs(remoteAddr.sin_port));
-        msg->post();
-    }
-}
-
-ANetworkSession::Session::~Session() {
-    ALOGV("Session %d gone", mSessionID);
-
-    close(mSocket);
-    mSocket = -1;
-}
-
-int32_t ANetworkSession::Session::sessionID() const {
-    return mSessionID;
-}
-
-int ANetworkSession::Session::socket() const {
-    return mSocket;
-}
-
-void ANetworkSession::Session::setMode(Mode mode) {
-    mMode = mode;
-}
-
-status_t ANetworkSession::Session::switchToWebSocketMode() {
-    if (mState != CONNECTED || mMode != MODE_RTSP) {
-        return INVALID_OPERATION;
-    }
-
-    mMode = MODE_WEBSOCKET;
-
-    return OK;
-}
-
-sp<AMessage> ANetworkSession::Session::getNotificationMessage() const {
-    return mNotify;
-}
-
-bool ANetworkSession::Session::isRTSPServer() const {
-    return mState == LISTENING_RTSP;
-}
-
-bool ANetworkSession::Session::isTCPDatagramServer() const {
-    return mState == LISTENING_TCP_DGRAMS;
-}
-
-bool ANetworkSession::Session::wantsToRead() {
-    return !mSawReceiveFailure && mState != CONNECTING;
-}
-
-bool ANetworkSession::Session::wantsToWrite() {
-    return !mSawSendFailure
-        && (mState == CONNECTING
-            || (mState == CONNECTED && !mOutFragments.empty())
-            || (mState == DATAGRAM && !mOutFragments.empty()));
-}
-
-status_t ANetworkSession::Session::readMore() {
-    if (mState == DATAGRAM) {
-        CHECK_EQ(mMode, MODE_DATAGRAM);
-
-        status_t err;
-        do {
-            sp<ABuffer> buf = new ABuffer(kMaxUDPSize);
-
-            struct sockaddr_in remoteAddr;
-            socklen_t remoteAddrLen = sizeof(remoteAddr);
-
-            ssize_t n;
-            do {
-                n = recvfrom(
-                        mSocket, buf->data(), buf->capacity(), 0,
-                        (struct sockaddr *)&remoteAddr, &remoteAddrLen);
-            } while (n < 0 && errno == EINTR);
-
-            err = OK;
-            if (n < 0) {
-                err = -errno;
-            } else if (n == 0) {
-                err = -ECONNRESET;
-            } else {
-                buf->setRange(0, n);
-
-                int64_t nowUs = ALooper::GetNowUs();
-                buf->meta()->setInt64("arrivalTimeUs", nowUs);
-
-                sp<AMessage> notify = mNotify->dup();
-                notify->setInt32("sessionID", mSessionID);
-                notify->setInt32("reason", kWhatDatagram);
-
-                uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr);
-                notify->setString(
-                        "fromAddr",
-                        AStringPrintf(
-                            "%u.%u.%u.%u",
-                            ip >> 24,
-                            (ip >> 16) & 0xff,
-                            (ip >> 8) & 0xff,
-                            ip & 0xff).c_str());
-
-                notify->setInt32("fromPort", ntohs(remoteAddr.sin_port));
-
-                notify->setBuffer("data", buf);
-                notify->post();
-            }
-        } while (err == OK);
-
-        if (err == -EAGAIN) {
-            err = OK;
-        }
-
-        if (err != OK) {
-            if (!mUDPRetries) {
-                notifyError(false /* send */, err, "Recvfrom failed.");
-                mSawReceiveFailure = true;
-            } else {
-                mUDPRetries--;
-                ALOGE("Recvfrom failed, %d/%d retries left",
-                        mUDPRetries, kMaxUDPRetries);
-                err = OK;
-            }
-        } else {
-            mUDPRetries = kMaxUDPRetries;
-        }
-
-        return err;
-    }
-
-    char tmp[512];
-    ssize_t n;
-    do {
-        n = recv(mSocket, tmp, sizeof(tmp), 0);
-    } while (n < 0 && errno == EINTR);
-
-    status_t err = OK;
-
-    if (n > 0) {
-        mInBuffer.append(tmp, n);
-
-#if 0
-        ALOGI("in:");
-        hexdump(tmp, n);
-#endif
-    } else if (n < 0) {
-        err = -errno;
-    } else {
-        err = -ECONNRESET;
-    }
-
-    if (mMode == MODE_DATAGRAM) {
-        // TCP stream carrying 16-bit length-prefixed datagrams.
-
-        while (mInBuffer.size() >= 2) {
-            size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str());
-
-            if (mInBuffer.size() < packetSize + 2) {
-                break;
-            }
-
-            sp<ABuffer> packet = new ABuffer(packetSize);
-            memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
-
-            int64_t nowUs = ALooper::GetNowUs();
-            packet->meta()->setInt64("arrivalTimeUs", nowUs);
-
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("sessionID", mSessionID);
-            notify->setInt32("reason", kWhatDatagram);
-            notify->setBuffer("data", packet);
-            notify->post();
-
-            mInBuffer.erase(0, packetSize + 2);
-        }
-    } else if (mMode == MODE_RTSP) {
-        for (;;) {
-            size_t length;
-
-            if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
-                if (mInBuffer.size() < 4) {
-                    break;
-                }
-
-                length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2);
-
-                if (mInBuffer.size() < 4 + length) {
-                    break;
-                }
-
-                sp<AMessage> notify = mNotify->dup();
-                notify->setInt32("sessionID", mSessionID);
-                notify->setInt32("reason", kWhatBinaryData);
-                notify->setInt32("channel", mInBuffer.c_str()[1]);
-
-                sp<ABuffer> data = new ABuffer(length);
-                memcpy(data->data(), mInBuffer.c_str() + 4, length);
-
-                int64_t nowUs = ALooper::GetNowUs();
-                data->meta()->setInt64("arrivalTimeUs", nowUs);
-
-                notify->setBuffer("data", data);
-                notify->post();
-
-                mInBuffer.erase(0, 4 + length);
-                continue;
-            }
-
-            sp<ParsedMessage> msg =
-                ParsedMessage::Parse(
-                        mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
-
-            if (msg == NULL) {
-                break;
-            }
-
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("sessionID", mSessionID);
-            notify->setInt32("reason", kWhatData);
-            notify->setObject("data", msg);
-            notify->post();
-
-#if 1
-            // XXX The (old) dongle sends the wrong content length header on a
-            // SET_PARAMETER request that signals a "wfd_idr_request".
-            // (17 instead of 19).
-            const char *content = msg->getContent();
-            if (content
-                    && !memcmp(content, "wfd_idr_request\r\n", 17)
-                    && length >= 19
-                    && mInBuffer.c_str()[length] == '\r'
-                    && mInBuffer.c_str()[length + 1] == '\n') {
-                length += 2;
-            }
-#endif
-
-            mInBuffer.erase(0, length);
-
-            if (err != OK) {
-                break;
-            }
-        }
-    } else {
-        CHECK_EQ(mMode, MODE_WEBSOCKET);
-
-        const uint8_t *data = (const uint8_t *)mInBuffer.c_str();
-        // hexdump(data, mInBuffer.size());
-
-        while (mInBuffer.size() >= 2) {
-            size_t offset = 2;
-
-            uint64_t payloadLen = data[1] & 0x7f;
-            if (payloadLen == 126) {
-                if (offset + 2 > mInBuffer.size()) {
-                    break;
-                }
-
-                payloadLen = U16_AT(&data[offset]);
-                offset += 2;
-            } else if (payloadLen == 127) {
-                if (offset + 8 > mInBuffer.size()) {
-                    break;
-                }
-
-                payloadLen = U64_AT(&data[offset]);
-                offset += 8;
-            }
-
-            uint32_t mask = 0;
-            if (data[1] & 0x80) {
-                // MASK==1
-                if (offset + 4 > mInBuffer.size()) {
-                    break;
-                }
-
-                mask = U32_AT(&data[offset]);
-                offset += 4;
-            }
-
-            if (payloadLen > mInBuffer.size() || offset > mInBuffer.size() - payloadLen) {
-                break;
-            }
-
-            // We have the full message.
-
-            sp<ABuffer> packet = new ABuffer(payloadLen);
-            memcpy(packet->data(), &data[offset], payloadLen);
-
-            if (mask != 0) {
-                for (size_t i = 0; i < payloadLen; ++i) {
-                    packet->data()[i] =
-                        data[offset + i]
-                            ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
-                }
-            }
-
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("sessionID", mSessionID);
-            notify->setInt32("reason", kWhatWebSocketMessage);
-            notify->setBuffer("data", packet);
-            notify->setInt32("headerByte", data[0]);
-            notify->post();
-
-            mInBuffer.erase(0, offset + payloadLen);
-        }
-    }
-
-    if (err != OK) {
-        notifyError(false /* send */, err, "Recv failed.");
-        mSawReceiveFailure = true;
-    }
-
-    return err;
-}
-
-void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
-#if 0
-    int64_t nowUs = ALooper::GetNowUs();
-    int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
-
-    static const int64_t kMinDelayMs = 0;
-    static const int64_t kMaxDelayMs = 300;
-
-    const char *kPattern = "########################################";
-    size_t kPatternSize = strlen(kPattern);
-
-    int n = (kPatternSize * (delayMs - kMinDelayMs))
-                / (kMaxDelayMs - kMinDelayMs);
-
-    if (n < 0) {
-        n = 0;
-    } else if ((size_t)n > kPatternSize) {
-        n = kPatternSize;
-    }
-
-    ALOGI("[%lld]: (%4lld ms) %s\n",
-          frag.mTimeUs / 1000,
-          delayMs,
-          kPattern + kPatternSize - n);
-#endif
-}
-
-status_t ANetworkSession::Session::writeMore() {
-    if (mState == DATAGRAM) {
-        CHECK(!mOutFragments.empty());
-
-        status_t err;
-        do {
-            const Fragment &frag = *mOutFragments.begin();
-            const sp<ABuffer> &datagram = frag.mBuffer;
-
-            int n;
-            do {
-                n = send(mSocket, datagram->data(), datagram->size(), 0);
-            } while (n < 0 && errno == EINTR);
-
-            err = OK;
-
-            if (n > 0) {
-                if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
-                    dumpFragmentStats(frag);
-                }
-
-                mOutFragments.erase(mOutFragments.begin());
-            } else if (n < 0) {
-                err = -errno;
-            } else if (n == 0) {
-                err = -ECONNRESET;
-            }
-        } while (err == OK && !mOutFragments.empty());
-
-        if (err == -EAGAIN) {
-            if (!mOutFragments.empty()) {
-                ALOGI("%zu datagrams remain queued.", mOutFragments.size());
-            }
-            err = OK;
-        }
-
-        if (err != OK) {
-            if (!mUDPRetries) {
-                notifyError(true /* send */, err, "Send datagram failed.");
-                mSawSendFailure = true;
-            } else {
-                mUDPRetries--;
-                ALOGE("Send datagram failed, %d/%d retries left",
-                        mUDPRetries, kMaxUDPRetries);
-                err = OK;
-            }
-        } else {
-            mUDPRetries = kMaxUDPRetries;
-        }
-
-        return err;
-    }
-
-    if (mState == CONNECTING) {
-        int err;
-        socklen_t optionLen = sizeof(err);
-        CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
-        CHECK_EQ(optionLen, (socklen_t)sizeof(err));
-
-        if (err != 0) {
-            notifyError(kWhatError, -err, "Connection failed");
-            mSawSendFailure = true;
-
-            return -err;
-        }
-
-        mState = CONNECTED;
-        notify(kWhatConnected);
-
-        return OK;
-    }
-
-    CHECK_EQ(mState, CONNECTED);
-    CHECK(!mOutFragments.empty());
-
-    ssize_t n = -1;
-    while (!mOutFragments.empty()) {
-        const Fragment &frag = *mOutFragments.begin();
-
-        do {
-            n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0);
-        } while (n < 0 && errno == EINTR);
-
-        if (n <= 0) {
-            break;
-        }
-
-        frag.mBuffer->setRange(
-                frag.mBuffer->offset() + n, frag.mBuffer->size() - n);
-
-        if (frag.mBuffer->size() > 0) {
-            break;
-        }
-
-        if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
-            dumpFragmentStats(frag);
-        }
-
-        mOutFragments.erase(mOutFragments.begin());
-    }
-
-    status_t err = OK;
-
-    if (n < 0) {
-        err = -errno;
-    } else if (n == 0) {
-        err = -ECONNRESET;
-    }
-
-    if (err != OK) {
-        notifyError(true /* send */, err, "Send failed.");
-        mSawSendFailure = true;
-    }
-
-#if 0
-    int numBytesQueued;
-    int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued);
-    if (res == 0 && numBytesQueued > 50 * 1024) {
-        if (numBytesQueued > 409600) {
-            ALOGW("!!! numBytesQueued = %d", numBytesQueued);
-        }
-
-        int64_t nowUs = ALooper::GetNowUs();
-
-        if (mLastStallReportUs < 0ll
-                || nowUs > mLastStallReportUs + 100000ll) {
-            sp<AMessage> msg = mNotify->dup();
-            msg->setInt32("sessionID", mSessionID);
-            msg->setInt32("reason", kWhatNetworkStall);
-            msg->setSize("numBytesQueued", numBytesQueued);
-            msg->post();
-
-            mLastStallReportUs = nowUs;
-        }
-    }
-#endif
-
-    return err;
-}
-
-status_t ANetworkSession::Session::sendRequest(
-        const void *data, ssize_t size, bool timeValid, int64_t timeUs) {
-    CHECK(mState == CONNECTED || mState == DATAGRAM);
-
-    if (size < 0) {
-        size = strlen((const char *)data);
-    }
-
-    if (size == 0) {
-        return OK;
-    }
-
-    sp<ABuffer> buffer;
-
-    if (mState == CONNECTED && mMode == MODE_DATAGRAM) {
-        CHECK_LE(size, 65535);
-
-        buffer = new ABuffer(size + 2);
-        buffer->data()[0] = size >> 8;
-        buffer->data()[1] = size & 0xff;
-        memcpy(buffer->data() + 2, data, size);
-    } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) {
-        static const bool kUseMask = false;  // Chromium doesn't like it.
-
-        size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0);
-        if (size > 65535) {
-            numHeaderBytes += 8;
-        } else if (size > 125) {
-            numHeaderBytes += 2;
-        }
-
-        buffer = new ABuffer(numHeaderBytes + size);
-        buffer->data()[0] = 0x81;  // FIN==1 | opcode=1 (text)
-        buffer->data()[1] = kUseMask ? 0x80 : 0x00;
-
-        if (size > 65535) {
-            buffer->data()[1] |= 127;
-            buffer->data()[2] = 0x00;
-            buffer->data()[3] = 0x00;
-            buffer->data()[4] = 0x00;
-            buffer->data()[5] = 0x00;
-            buffer->data()[6] = (size >> 24) & 0xff;
-            buffer->data()[7] = (size >> 16) & 0xff;
-            buffer->data()[8] = (size >> 8) & 0xff;
-            buffer->data()[9] = size & 0xff;
-        } else if (size > 125) {
-            buffer->data()[1] |= 126;
-            buffer->data()[2] = (size >> 8) & 0xff;
-            buffer->data()[3] = size & 0xff;
-        } else {
-            buffer->data()[1] |= size;
-        }
-
-        if (kUseMask) {
-            uint32_t mask = rand();
-
-            buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff;
-            buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff;
-            buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff;
-            buffer->data()[numHeaderBytes - 1] = mask & 0xff;
-
-            for (size_t i = 0; i < (size_t)size; ++i) {
-                buffer->data()[numHeaderBytes + i] =
-                    ((const uint8_t *)data)[i]
-                        ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
-            }
-        } else {
-            memcpy(buffer->data() + numHeaderBytes, data, size);
-        }
-    } else {
-        buffer = new ABuffer(size);
-        memcpy(buffer->data(), data, size);
-    }
-
-    Fragment frag;
-
-    frag.mFlags = 0;
-    if (timeValid) {
-        frag.mFlags = FRAGMENT_FLAG_TIME_VALID;
-        frag.mTimeUs = timeUs;
-    }
-
-    frag.mBuffer = buffer;
-
-    mOutFragments.push_back(frag);
-
-    return OK;
-}
-
-void ANetworkSession::Session::notifyError(
-        bool send, status_t err, const char *detail) {
-    sp<AMessage> msg = mNotify->dup();
-    msg->setInt32("sessionID", mSessionID);
-    msg->setInt32("reason", kWhatError);
-    msg->setInt32("send", send);
-    msg->setInt32("err", err);
-    msg->setString("detail", detail);
-    msg->post();
-}
-
-void ANetworkSession::Session::notify(NotificationReason reason) {
-    sp<AMessage> msg = mNotify->dup();
-    msg->setInt32("sessionID", mSessionID);
-    msg->setInt32("reason", reason);
-    msg->post();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-ANetworkSession::ANetworkSession()
-    : mNextSessionID(1) {
-    mPipeFd[0] = mPipeFd[1] = -1;
-}
-
-ANetworkSession::~ANetworkSession() {
-    stop();
-}
-
-status_t ANetworkSession::start() {
-    if (mThread != NULL) {
-        return INVALID_OPERATION;
-    }
-
-    int res = pipe(mPipeFd);
-    if (res != 0) {
-        mPipeFd[0] = mPipeFd[1] = -1;
-        return -errno;
-    }
-
-    mThread = new NetworkThread(this);
-
-    status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);
-
-    if (err != OK) {
-        mThread.clear();
-
-        close(mPipeFd[0]);
-        close(mPipeFd[1]);
-        mPipeFd[0] = mPipeFd[1] = -1;
-
-        return err;
-    }
-
-    return OK;
-}
-
-status_t ANetworkSession::stop() {
-    if (mThread == NULL) {
-        return INVALID_OPERATION;
-    }
-
-    mThread->requestExit();
-    interrupt();
-    mThread->requestExitAndWait();
-
-    mThread.clear();
-
-    close(mPipeFd[0]);
-    close(mPipeFd[1]);
-    mPipeFd[0] = mPipeFd[1] = -1;
-
-    return OK;
-}
-
-status_t ANetworkSession::createRTSPClient(
-        const char *host, unsigned port, const sp<AMessage> &notify,
-        int32_t *sessionID) {
-    return createClientOrServer(
-            kModeCreateRTSPClient,
-            NULL /* addr */,
-            0 /* port */,
-            host,
-            port,
-            notify,
-            sessionID);
-}
-
-status_t ANetworkSession::createRTSPServer(
-        const struct in_addr &addr, unsigned port,
-        const sp<AMessage> &notify, int32_t *sessionID) {
-    return createClientOrServer(
-            kModeCreateRTSPServer,
-            &addr,
-            port,
-            NULL /* remoteHost */,
-            0 /* remotePort */,
-            notify,
-            sessionID);
-}
-
-status_t ANetworkSession::createUDPSession(
-        unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID) {
-    return createUDPSession(localPort, NULL, 0, notify, sessionID);
-}
-
-status_t ANetworkSession::createUDPSession(
-        unsigned localPort,
-        const char *remoteHost,
-        unsigned remotePort,
-        const sp<AMessage> &notify,
-        int32_t *sessionID) {
-    return createClientOrServer(
-            kModeCreateUDPSession,
-            NULL /* addr */,
-            localPort,
-            remoteHost,
-            remotePort,
-            notify,
-            sessionID);
-}
-
-status_t ANetworkSession::createTCPDatagramSession(
-        const struct in_addr &addr, unsigned port,
-        const sp<AMessage> &notify, int32_t *sessionID) {
-    return createClientOrServer(
-            kModeCreateTCPDatagramSessionPassive,
-            &addr,
-            port,
-            NULL /* remoteHost */,
-            0 /* remotePort */,
-            notify,
-            sessionID);
-}
-
-status_t ANetworkSession::createTCPDatagramSession(
-        unsigned localPort,
-        const char *remoteHost,
-        unsigned remotePort,
-        const sp<AMessage> &notify,
-        int32_t *sessionID) {
-    return createClientOrServer(
-            kModeCreateTCPDatagramSessionActive,
-            NULL /* addr */,
-            localPort,
-            remoteHost,
-            remotePort,
-            notify,
-            sessionID);
-}
-
-status_t ANetworkSession::destroySession(int32_t sessionID) {
-    Mutex::Autolock autoLock(mLock);
-
-    ssize_t index = mSessions.indexOfKey(sessionID);
-
-    if (index < 0) {
-        return -ENOENT;
-    }
-
-    mSessions.removeItemsAt(index);
-
-    interrupt();
-
-    return OK;
-}
-
-// static
-status_t ANetworkSession::MakeSocketNonBlocking(int s) {
-    int flags = fcntl(s, F_GETFL, 0);
-    if (flags < 0) {
-        flags = 0;
-    }
-
-    int res = fcntl(s, F_SETFL, flags | O_NONBLOCK);
-    if (res < 0) {
-        return -errno;
-    }
-
-    return OK;
-}
-
-status_t ANetworkSession::createClientOrServer(
-        Mode mode,
-        const struct in_addr *localAddr,
-        unsigned port,
-        const char *remoteHost,
-        unsigned remotePort,
-        const sp<AMessage> &notify,
-        int32_t *sessionID) {
-    Mutex::Autolock autoLock(mLock);
-
-    *sessionID = 0;
-    status_t err = OK;
-    int s, res;
-    sp<Session> session;
-
-    s = socket(
-            AF_INET,
-            (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
-            0);
-
-    if (s < 0) {
-        err = -errno;
-        goto bail;
-    }
-
-    if (mode == kModeCreateRTSPServer
-            || mode == kModeCreateTCPDatagramSessionPassive) {
-        const int yes = 1;
-        res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
-
-        if (res < 0) {
-            err = -errno;
-            goto bail2;
-        }
-    }
-
-    if (mode == kModeCreateUDPSession) {
-        int size = 256 * 1024;
-
-        res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
-
-        if (res < 0) {
-            err = -errno;
-            goto bail2;
-        }
-
-        res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
-
-        if (res < 0) {
-            err = -errno;
-            goto bail2;
-        }
-    } else if (mode == kModeCreateTCPDatagramSessionActive) {
-        int flag = 1;
-        res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
-
-        if (res < 0) {
-            err = -errno;
-            goto bail2;
-        }
-
-        int tos = 224;  // VOICE
-        res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
-
-        if (res < 0) {
-            err = -errno;
-            goto bail2;
-        }
-    }
-
-    err = MakeSocketNonBlocking(s);
-
-    if (err != OK) {
-        goto bail2;
-    }
-
-    struct sockaddr_in addr;
-    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
-    addr.sin_family = AF_INET;
-
-    if (mode == kModeCreateRTSPClient
-            || mode == kModeCreateTCPDatagramSessionActive) {
-        struct hostent *ent= gethostbyname(remoteHost);
-        if (ent == NULL) {
-            err = -h_errno;
-            goto bail2;
-        }
-
-        addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-        addr.sin_port = htons(remotePort);
-    } else if (localAddr != NULL) {
-        addr.sin_addr = *localAddr;
-        addr.sin_port = htons(port);
-    } else {
-        addr.sin_addr.s_addr = htonl(INADDR_ANY);
-        addr.sin_port = htons(port);
-    }
-
-    if (mode == kModeCreateRTSPClient
-            || mode == kModeCreateTCPDatagramSessionActive) {
-        in_addr_t x = ntohl(addr.sin_addr.s_addr);
-        ALOGI("connecting socket %d to %d.%d.%d.%d:%d",
-              s,
-              (x >> 24),
-              (x >> 16) & 0xff,
-              (x >> 8) & 0xff,
-              x & 0xff,
-              ntohs(addr.sin_port));
-
-        res = connect(s, (const struct sockaddr *)&addr, sizeof(addr));
-
-        CHECK_LT(res, 0);
-        if (errno == EINPROGRESS) {
-            res = 0;
-        }
-    } else {
-        res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
-
-        if (res == 0) {
-            if (mode == kModeCreateRTSPServer
-                    || mode == kModeCreateTCPDatagramSessionPassive) {
-                res = listen(s, 4);
-            } else {
-                CHECK_EQ(mode, kModeCreateUDPSession);
-
-                if (remoteHost != NULL) {
-                    struct sockaddr_in remoteAddr;
-                    memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
-                    remoteAddr.sin_family = AF_INET;
-                    remoteAddr.sin_port = htons(remotePort);
-
-                    struct hostent *ent= gethostbyname(remoteHost);
-                    if (ent == NULL) {
-                        err = -h_errno;
-                        goto bail2;
-                    }
-
-                    remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-
-                    res = connect(
-                            s,
-                            (const struct sockaddr *)&remoteAddr,
-                            sizeof(remoteAddr));
-                }
-            }
-        }
-    }
-
-    if (res < 0) {
-        err = -errno;
-        goto bail2;
-    }
-
-    Session::State state;
-    switch (mode) {
-        case kModeCreateRTSPClient:
-            state = Session::CONNECTING;
-            break;
-
-        case kModeCreateTCPDatagramSessionActive:
-            state = Session::CONNECTING;
-            break;
-
-        case kModeCreateTCPDatagramSessionPassive:
-            state = Session::LISTENING_TCP_DGRAMS;
-            break;
-
-        case kModeCreateRTSPServer:
-            state = Session::LISTENING_RTSP;
-            break;
-
-        default:
-            CHECK_EQ(mode, kModeCreateUDPSession);
-            state = Session::DATAGRAM;
-            break;
-    }
-
-    session = new Session(
-            mNextSessionID++,
-            state,
-            s,
-            notify);
-
-    if (mode == kModeCreateTCPDatagramSessionActive) {
-        session->setMode(Session::MODE_DATAGRAM);
-    } else if (mode == kModeCreateRTSPClient) {
-        session->setMode(Session::MODE_RTSP);
-    }
-
-    mSessions.add(session->sessionID(), session);
-
-    interrupt();
-
-    *sessionID = session->sessionID();
-
-    goto bail;
-
-bail2:
-    close(s);
-    s = -1;
-
-bail:
-    return err;
-}
-
-status_t ANetworkSession::connectUDPSession(
-        int32_t sessionID, const char *remoteHost, unsigned remotePort) {
-    Mutex::Autolock autoLock(mLock);
-
-    ssize_t index = mSessions.indexOfKey(sessionID);
-
-    if (index < 0) {
-        return -ENOENT;
-    }
-
-    const sp<Session> session = mSessions.valueAt(index);
-    int s = session->socket();
-
-    struct sockaddr_in remoteAddr;
-    memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
-    remoteAddr.sin_family = AF_INET;
-    remoteAddr.sin_port = htons(remotePort);
-
-    status_t err = OK;
-    struct hostent *ent = gethostbyname(remoteHost);
-    if (ent == NULL) {
-        err = -h_errno;
-    } else {
-        remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-
-        int res = connect(
-                s,
-                (const struct sockaddr *)&remoteAddr,
-                sizeof(remoteAddr));
-
-        if (res < 0) {
-            err = -errno;
-        }
-    }
-
-    return err;
-}
-
-status_t ANetworkSession::sendRequest(
-        int32_t sessionID, const void *data, ssize_t size,
-        bool timeValid, int64_t timeUs) {
-    Mutex::Autolock autoLock(mLock);
-
-    ssize_t index = mSessions.indexOfKey(sessionID);
-
-    if (index < 0) {
-        return -ENOENT;
-    }
-
-    const sp<Session> session = mSessions.valueAt(index);
-
-    status_t err = session->sendRequest(data, size, timeValid, timeUs);
-
-    interrupt();
-
-    return err;
-}
-
-status_t ANetworkSession::switchToWebSocketMode(int32_t sessionID) {
-    Mutex::Autolock autoLock(mLock);
-
-    ssize_t index = mSessions.indexOfKey(sessionID);
-
-    if (index < 0) {
-        return -ENOENT;
-    }
-
-    const sp<Session> session = mSessions.valueAt(index);
-    return session->switchToWebSocketMode();
-}
-
-void ANetworkSession::interrupt() {
-    static const char dummy = 0;
-
-    ssize_t n;
-    do {
-        n = write(mPipeFd[1], &dummy, 1);
-    } while (n < 0 && errno == EINTR);
-
-    if (n < 0) {
-        ALOGW("Error writing to pipe (%s)", strerror(errno));
-    }
-}
-
-void ANetworkSession::threadLoop() {
-    fd_set rs, ws;
-    FD_ZERO(&rs);
-    FD_ZERO(&ws);
-
-    FD_SET(mPipeFd[0], &rs);
-    int maxFd = mPipeFd[0];
-
-    {
-        Mutex::Autolock autoLock(mLock);
-
-        for (size_t i = 0; i < mSessions.size(); ++i) {
-            const sp<Session> &session = mSessions.valueAt(i);
-
-            int s = session->socket();
-
-            if (s < 0) {
-                continue;
-            }
-
-            if (session->wantsToRead()) {
-                FD_SET(s, &rs);
-                if (s > maxFd) {
-                    maxFd = s;
-                }
-            }
-
-            if (session->wantsToWrite()) {
-                FD_SET(s, &ws);
-                if (s > maxFd) {
-                    maxFd = s;
-                }
-            }
-        }
-    }
-
-    int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
-
-    if (res == 0) {
-        return;
-    }
-
-    if (res < 0) {
-        if (errno == EINTR) {
-            return;
-        }
-
-        ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
-        return;
-    }
-
-    if (FD_ISSET(mPipeFd[0], &rs)) {
-        char c;
-        ssize_t n;
-        do {
-            n = read(mPipeFd[0], &c, 1);
-        } while (n < 0 && errno == EINTR);
-
-        if (n < 0) {
-            ALOGW("Error reading from pipe (%s)", strerror(errno));
-        }
-
-        --res;
-    }
-
-    {
-        Mutex::Autolock autoLock(mLock);
-
-        List<sp<Session> > sessionsToAdd;
-
-        for (size_t i = mSessions.size(); res > 0 && i > 0;) {
-            i--;
-            const sp<Session> &session = mSessions.valueAt(i);
-
-            int s = session->socket();
-
-            if (s < 0) {
-                continue;
-            }
-
-            if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
-                --res;
-            }
-
-            if (FD_ISSET(s, &rs)) {
-                if (session->isRTSPServer() || session->isTCPDatagramServer()) {
-                    struct sockaddr_in remoteAddr;
-                    socklen_t remoteAddrLen = sizeof(remoteAddr);
-
-                    int clientSocket = accept(
-                            s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
-
-                    if (clientSocket >= 0) {
-                        status_t err = MakeSocketNonBlocking(clientSocket);
-
-                        if (err != OK) {
-                            ALOGE("Unable to make client socket non blocking, "
-                                  "failed w/ error %d (%s)",
-                                  err, strerror(-err));
-
-                            close(clientSocket);
-                            clientSocket = -1;
-                        } else {
-                            in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr);
-
-                            ALOGI("incoming connection from %d.%d.%d.%d:%d "
-                                  "(socket %d)",
-                                  (addr >> 24),
-                                  (addr >> 16) & 0xff,
-                                  (addr >> 8) & 0xff,
-                                  addr & 0xff,
-                                  ntohs(remoteAddr.sin_port),
-                                  clientSocket);
-
-                            sp<Session> clientSession =
-                                new Session(
-                                        mNextSessionID++,
-                                        Session::CONNECTED,
-                                        clientSocket,
-                                        session->getNotificationMessage());
-
-                            clientSession->setMode(
-                                    session->isRTSPServer()
-                                        ? Session::MODE_RTSP
-                                        : Session::MODE_DATAGRAM);
-
-                            sessionsToAdd.push_back(clientSession);
-                        }
-                    } else {
-                        ALOGE("accept returned error %d (%s)",
-                              errno, strerror(errno));
-                    }
-                } else {
-                    status_t err = session->readMore();
-                    if (err != OK) {
-                        ALOGE("readMore on socket %d failed w/ error %d (%s)",
-                              s, err, strerror(-err));
-                    }
-                }
-            }
-
-            if (FD_ISSET(s, &ws)) {
-                status_t err = session->writeMore();
-                if (err != OK) {
-                    ALOGE("writeMore on socket %d failed w/ error %d (%s)",
-                          s, err, strerror(-err));
-                }
-            }
-        }
-
-        while (!sessionsToAdd.empty()) {
-            sp<Session> session = *sessionsToAdd.begin();
-            sessionsToAdd.erase(sessionsToAdd.begin());
-
-            mSessions.add(session->sessionID(), session);
-
-            ALOGI("added clientSession %d", session->sessionID());
-        }
-    }
-}
-
-}  // namespace android
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 6b384c0..5b7961d 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -56,18 +56,15 @@
         "ABuffer.cpp",
         "ADebug.cpp",
         "AHandler.cpp",
-        "AHierarchicalStateMachine.cpp",
         "ALooper.cpp",
         "ALooperRoster.cpp",
         "AMessage.cpp",
-        "ANetworkSession.cpp",
         "AString.cpp",
         "AStringUtils.cpp",
         "ByteUtils.cpp",
         "ColorUtils.cpp",
         "MediaDefs.cpp",
         "MediaKeys.cpp",
-        "ParsedMessage.cpp",
         "avc_utils.cpp",
         "base64.cpp",
         "hexdump.cpp",
diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp
index a32cf08..28bb10a 100644
--- a/media/libstagefright/foundation/MediaDefs.cpp
+++ b/media/libstagefright/foundation/MediaDefs.cpp
@@ -55,7 +55,7 @@
 
 const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
 const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
-const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_OGG = "audio/ogg";
 const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
 const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
 const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
diff --git a/media/libstagefright/foundation/ParsedMessage.cpp b/media/libstagefright/foundation/ParsedMessage.cpp
deleted file mode 100644
index 049c9ad..0000000
--- a/media/libstagefright/foundation/ParsedMessage.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright 2012, 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 "ParsedMessage.h"
-
-#include <ctype.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/hexdump.h>
-
-namespace android {
-
-// static
-sp<ParsedMessage> ParsedMessage::Parse(
-        const char *data, size_t size, bool noMoreData, size_t *length) {
-    sp<ParsedMessage> msg = new ParsedMessage;
-    ssize_t res = msg->parse(data, size, noMoreData);
-
-    if (res < 0) {
-        *length = 0;
-        return NULL;
-    }
-
-    *length = res;
-    return msg;
-}
-
-ParsedMessage::ParsedMessage() {
-}
-
-ParsedMessage::~ParsedMessage() {
-}
-
-bool ParsedMessage::findString(const char *name, AString *value) const {
-    AString key = name;
-    key.tolower();
-
-    ssize_t index = mDict.indexOfKey(key);
-
-    if (index < 0) {
-        value->clear();
-
-        return false;
-    }
-
-    *value = mDict.valueAt(index);
-    return true;
-}
-
-bool ParsedMessage::findInt32(const char *name, int32_t *value) const {
-    AString stringValue;
-
-    if (!findString(name, &stringValue)) {
-        return false;
-    }
-
-    char *end;
-    *value = strtol(stringValue.c_str(), &end, 10);
-
-    if (end == stringValue.c_str() || *end != '\0') {
-        *value = 0;
-        return false;
-    }
-
-    return true;
-}
-
-const char *ParsedMessage::getContent() const {
-    return mContent.c_str();
-}
-
-ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) {
-    if (size == 0) {
-        return -1;
-    }
-
-    ssize_t lastDictIndex = -1;
-
-    size_t offset = 0;
-    bool headersComplete = false;
-    while (offset < size) {
-        size_t lineEndOffset = offset;
-        while (lineEndOffset + 1 < size
-                && (data[lineEndOffset] != '\r'
-                        || data[lineEndOffset + 1] != '\n')) {
-            ++lineEndOffset;
-        }
-
-        if (lineEndOffset + 1 >= size) {
-            return -1;
-        }
-
-        AString line(&data[offset], lineEndOffset - offset);
-
-        if (offset == 0) {
-            // Special handling for the request/status line.
-
-            mDict.add(AString("_"), line);
-            offset = lineEndOffset + 2;
-
-            continue;
-        }
-
-        if (lineEndOffset == offset) {
-            // An empty line separates headers from body.
-            headersComplete = true;
-            offset += 2;
-            break;
-        }
-
-        if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
-            // Support for folded header values.
-
-            if (lastDictIndex >= 0) {
-                // Otherwise it's malformed since the first header line
-                // cannot continue anything...
-
-                AString &value = mDict.editValueAt(lastDictIndex);
-                value.append(line);
-            }
-
-            offset = lineEndOffset + 2;
-            continue;
-        }
-
-        ssize_t colonPos = line.find(":");
-        if (colonPos >= 0) {
-            AString key(line, 0, colonPos);
-            key.trim();
-            key.tolower();
-
-            line.erase(0, colonPos + 1);
-
-            lastDictIndex = mDict.add(key, line);
-        }
-
-        offset = lineEndOffset + 2;
-    }
-
-    if (!headersComplete && (!noMoreData || offset == 0)) {
-        // We either saw the empty line separating headers from body
-        // or we saw at least the status line and know that no more data
-        // is going to follow.
-        return -1;
-    }
-
-    for (size_t i = 0; i < mDict.size(); ++i) {
-        mDict.editValueAt(i).trim();
-    }
-
-    int32_t contentLength;
-    if (!findInt32("content-length", &contentLength) || contentLength < 0) {
-        contentLength = 0;
-    }
-
-    size_t totalLength = offset + contentLength;
-
-    if (size < totalLength) {
-        return -1;
-    }
-
-    mContent.setTo(&data[offset], contentLength);
-
-    return totalLength;
-}
-
-bool ParsedMessage::getRequestField(size_t index, AString *field) const {
-    AString line;
-    CHECK(findString("_", &line));
-
-    size_t prevOffset = 0;
-    size_t offset = 0;
-    for (size_t i = 0; i <= index; ++i) {
-        if (offset >= line.size()) {
-            return false;
-        }
-
-        ssize_t spacePos = line.find(" ", offset);
-
-        if (spacePos < 0) {
-            spacePos = line.size();
-        }
-
-        prevOffset = offset;
-        offset = spacePos + 1;
-    }
-
-    field->setTo(line, prevOffset, offset - prevOffset - 1);
-
-    return true;
-}
-
-bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
-    AString statusCodeString;
-    if (!getRequestField(1, &statusCodeString)) {
-        *statusCode = 0;
-        return false;
-    }
-
-    char *end;
-    *statusCode = strtol(statusCodeString.c_str(), &end, 10);
-
-    if (*end != '\0' || end == statusCodeString.c_str()
-            || (*statusCode) < 100 || (*statusCode) > 999) {
-        *statusCode = 0;
-        return false;
-    }
-
-    return true;
-}
-
-AString ParsedMessage::debugString() const {
-    AString line;
-    CHECK(findString("_", &line));
-
-    line.append("\n");
-
-    for (size_t i = 0; i < mDict.size(); ++i) {
-        const AString &key = mDict.keyAt(i);
-        const AString &value = mDict.valueAt(i);
-
-        if (key == AString("_")) {
-            continue;
-        }
-
-        line.append(key);
-        line.append(": ");
-        line.append(value);
-        line.append("\n");
-    }
-
-    line.append("\n");
-    line.append(mContent);
-
-    return line;
-}
-
-// static
-bool ParsedMessage::GetAttribute(
-        const char *s, const char *key, AString *value) {
-    value->clear();
-
-    size_t keyLen = strlen(key);
-
-    for (;;) {
-        while (isspace(*s)) {
-            ++s;
-        }
-
-        const char *colonPos = strchr(s, ';');
-
-        size_t len =
-            (colonPos == NULL) ? strlen(s) : colonPos - s;
-
-        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
-            value->setTo(&s[keyLen + 1], len - keyLen - 1);
-            return true;
-        }
-
-        if (colonPos == NULL) {
-            return false;
-        }
-
-        s = colonPos + 1;
-    }
-}
-
-// static
-bool ParsedMessage::GetInt32Attribute(
-        const char *s, const char *key, int32_t *value) {
-    AString stringValue;
-    if (!GetAttribute(s, key, &stringValue)) {
-        *value = 0;
-        return false;
-    }
-
-    char *end;
-    *value = strtol(stringValue.c_str(), &end, 10);
-
-    if (end == stringValue.c_str() || *end != '\0') {
-        *value = 0;
-        return false;
-    }
-
-    return true;
-}
-
-}  // namespace android
-
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h
deleted file mode 100644
index fd3ebaa..0000000
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2012, 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 A_NETWORK_SESSION_H_
-
-#define A_NETWORK_SESSION_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/Thread.h>
-
-#include <netinet/in.h>
-
-namespace android {
-
-struct AMessage;
-
-// Helper class to manage a number of live sockets (datagram and stream-based)
-// on a single thread. Clients are notified about activity through AMessages.
-struct ANetworkSession : public RefBase {
-    ANetworkSession();
-
-    status_t start();
-    status_t stop();
-
-    status_t createRTSPClient(
-            const char *host, unsigned port, const sp<AMessage> &notify,
-            int32_t *sessionID);
-
-    status_t createRTSPServer(
-            const struct in_addr &addr, unsigned port,
-            const sp<AMessage> &notify, int32_t *sessionID);
-
-    status_t createUDPSession(
-            unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID);
-
-    status_t createUDPSession(
-            unsigned localPort,
-            const char *remoteHost,
-            unsigned remotePort,
-            const sp<AMessage> &notify,
-            int32_t *sessionID);
-
-    status_t connectUDPSession(
-            int32_t sessionID, const char *remoteHost, unsigned remotePort);
-
-    // passive
-    status_t createTCPDatagramSession(
-            const struct in_addr &addr, unsigned port,
-            const sp<AMessage> &notify, int32_t *sessionID);
-
-    // active
-    status_t createTCPDatagramSession(
-            unsigned localPort,
-            const char *remoteHost,
-            unsigned remotePort,
-            const sp<AMessage> &notify,
-            int32_t *sessionID);
-
-    status_t destroySession(int32_t sessionID);
-
-    status_t sendRequest(
-            int32_t sessionID, const void *data, ssize_t size = -1,
-            bool timeValid = false, int64_t timeUs = -1ll);
-
-    status_t switchToWebSocketMode(int32_t sessionID);
-
-    enum NotificationReason {
-        kWhatError,
-        kWhatConnected,
-        kWhatClientConnected,
-        kWhatData,
-        kWhatDatagram,
-        kWhatBinaryData,
-        kWhatWebSocketMessage,
-        kWhatNetworkStall,
-    };
-
-protected:
-    virtual ~ANetworkSession();
-
-private:
-    struct NetworkThread;
-    struct Session;
-
-    Mutex mLock;
-    sp<Thread> mThread;
-
-    int32_t mNextSessionID;
-
-    int mPipeFd[2];
-
-    KeyedVector<int32_t, sp<Session> > mSessions;
-
-    enum Mode {
-        kModeCreateUDPSession,
-        kModeCreateTCPDatagramSessionPassive,
-        kModeCreateTCPDatagramSessionActive,
-        kModeCreateRTSPServer,
-        kModeCreateRTSPClient,
-    };
-    status_t createClientOrServer(
-            Mode mode,
-            const struct in_addr *addr,
-            unsigned port,
-            const char *remoteHost,
-            unsigned remotePort,
-            const sp<AMessage> &notify,
-            int32_t *sessionID);
-
-    void threadLoop();
-    void interrupt();
-
-    static status_t MakeSocketNonBlocking(int s);
-
-    DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession);
-};
-
-}  // namespace android
-
-#endif  // A_NETWORK_SESSION_H_
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h
deleted file mode 100644
index 9d43a93..0000000
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2012, 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 <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-// Encapsulates an "HTTP/RTSP style" response, i.e. a status line,
-// key/value pairs making up the headers and an optional body/content.
-struct ParsedMessage : public RefBase {
-    static sp<ParsedMessage> Parse(
-            const char *data, size_t size, bool noMoreData, size_t *length);
-
-    bool findString(const char *name, AString *value) const;
-    bool findInt32(const char *name, int32_t *value) const;
-
-    const char *getContent() const;
-
-    bool getRequestField(size_t index, AString *field) const;
-    bool getStatusCode(int32_t *statusCode) const;
-
-    AString debugString() const;
-
-    static bool GetAttribute(const char *s, const char *key, AString *value);
-
-    static bool GetInt32Attribute(
-            const char *s, const char *key, int32_t *value);
-
-
-protected:
-    virtual ~ParsedMessage();
-
-private:
-    KeyedVector<AString, AString> mDict;
-    AString mContent;
-
-    ParsedMessage();
-
-    ssize_t parse(const char *data, size_t size, bool noMoreData);
-
-    DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage);
-};
-
-}  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 1137cf1..73f93d1 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -22,7 +22,7 @@
 #include <media/hardware/MetadataBufferType.h>
 #include <media/MediaCodecInfo.h>
 #include <media/IOMX.h>
-#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <media/stagefright/AHierarchicalStateMachine.h>
 #include <media/stagefright/CodecBase.h>
 #include <media/stagefright/FrameRenderTracker.h>
 #include <media/stagefright/MediaDefs.h>
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/media/libstagefright/include/media/stagefright/AHierarchicalStateMachine.h
similarity index 100%
rename from media/libstagefright/foundation/include/media/stagefright/foundation/AHierarchicalStateMachine.h
rename to media/libstagefright/include/media/stagefright/AHierarchicalStateMachine.h
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index a9fce55..0f229f7 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -190,7 +190,12 @@
  */
 // convert: Status -> status_t
 inline status_t toStatusT(Return<Status> const& t) {
-    return t.isOk() ? toStatusT(static_cast<Status>(t)) : UNKNOWN_ERROR;
+    if (t.isOk()) {
+        return toStatusT(static_cast<Status>(t));
+    } else if (t.isDeadObject()) {
+        return DEAD_OBJECT;
+    }
+    return UNKNOWN_ERROR;
 }
 
 /**
diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp
index a5ad6c6..7c51333 100644
--- a/media/libstagefright/timedtext/Android.bp
+++ b/media/libstagefright/timedtext/Android.bp
@@ -25,3 +25,36 @@
 
     shared_libs: ["libmedia"],
 }
+
+cc_library_static {
+    name: "libstagefright_timedtext2",
+
+    srcs: ["TextDescriptions2.cpp"],
+
+    static_libs: [
+        "libmediaplayer2-protos",
+        "libprotobuf-cpp-lite",
+    ],
+
+    cflags: [
+        "-Wno-multichar",
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+    ],
+
+    shared_libs: ["libmedia"],
+}
diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp
new file mode 100644
index 0000000..f48eacc
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 "TextDescriptions2.h"
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+TextDescriptions2::TextDescriptions2() {
+}
+
+status_t TextDescriptions2::getPlayerMessageOfDescriptions(
+        const uint8_t *data, ssize_t size,
+        uint32_t flags, int timeMs, PlayerMessage *playerMsg) {
+    if (flags & IN_BAND_TEXT_3GPP) {
+        if (flags & GLOBAL_DESCRIPTIONS) {
+            return extract3GPPGlobalDescriptions(data, size, playerMsg);
+        } else if (flags & LOCAL_DESCRIPTIONS) {
+            return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg);
+        }
+    } else if (flags & OUT_OF_BAND_TEXT_SRT) {
+        if (flags & LOCAL_DESCRIPTIONS) {
+            return extractSRTLocalDescriptions(data, size, timeMs, playerMsg);
+        }
+    }
+
+    return ERROR_UNSUPPORTED;
+}
+
+// Parse the SRT text sample, and store the timing and text sample in a PlayerMessage.
+// The PlayerMessage will be sent to MediaPlayer2.java through event, and will be
+// parsed in TimedText.java.
+status_t TextDescriptions2::extractSRTLocalDescriptions(
+        const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) {
+    playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+    playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+    playerMsg->add_values()->set_int32_value(timeMs);
+
+    playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+    playerMsg->add_values()->set_bytes_value(data, size);
+
+    return OK;
+}
+
+// Extract the local 3GPP display descriptions. 3GPP local descriptions
+// are appended to the text sample if any.
+status_t TextDescriptions2::extract3GPPLocalDescriptions(
+        const uint8_t *data, ssize_t size,
+        int timeMs, PlayerMessage *playerMsg) {
+
+    playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+
+    // write start time to display this text sample
+    playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+    playerMsg->add_values()->set_int32_value(timeMs);
+
+    if (size < 2) {
+        return OK;
+    }
+    ssize_t textLen = (*data) << 8 | (*(data + 1));
+
+    if (size < textLen + 2) {
+        return OK;
+    }
+
+    // write text sample length and text sample itself
+    playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+    playerMsg->add_values()->set_bytes_value(data + 2, textLen);
+
+    if (size > textLen + 2) {
+        data += (textLen + 2);
+        size -= (textLen + 2);
+    } else {
+        return OK;
+    }
+
+    while (size >= 8) {
+        const uint8_t *tmpData = data;
+        ssize_t chunkSize = U32_AT(tmpData);      // size includes size and type
+        uint32_t chunkType = U32_AT(tmpData + 4);
+
+        if (chunkSize <= 8 || chunkSize > size) {
+            return OK;
+        }
+
+        size_t remaining = chunkSize - 8;
+
+        tmpData += 8;
+
+        switch(chunkType) {
+            // 'tbox' box to indicate the position of the text with values
+            // of top, left, bottom and right
+            case FOURCC('t', 'b', 'o', 'x'):
+            {
+                if (remaining < 8) {
+                    return OK;
+                }
+                playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 8;
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage
+status_t TextDescriptions2::extract3GPPGlobalDescriptions(
+        const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) {
+
+    playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING);
+
+    while (size >= 8) {
+        ssize_t chunkSize = U32_AT(data);
+        uint32_t chunkType = U32_AT(data + 4);
+        const uint8_t *tmpData = data;
+        tmpData += 8;
+        size_t remaining = size - 8;
+
+        if (size < chunkSize) {
+            return OK;
+        }
+        switch(chunkType) {
+            case FOURCC('t', 'x', '3', 'g'):
+            {
+                if (remaining < 18) {
+                    return OK;
+                }
+                // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA
+                tmpData += 18;
+                remaining -= 18;
+
+                if (remaining < 8) {
+                    return OK;
+                }
+                playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 18;
+                // Ignore remaining data.
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/timedtext/TextDescriptions2.h b/media/libstagefright/timedtext/TextDescriptions2.h
new file mode 100644
index 0000000..7c7d2d0
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.h
@@ -0,0 +1,88 @@
+ /*
+ * 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 TEXT_DESCRIPTIONS2_H_
+
+#define TEXT_DESCRIPTIONS2_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
+namespace android {
+
+class TextDescriptions2 {
+public:
+    enum {
+        IN_BAND_TEXT_3GPP             = 0x01,
+        OUT_OF_BAND_TEXT_SRT          = 0x02,
+
+        GLOBAL_DESCRIPTIONS           = 0x100,
+        LOCAL_DESCRIPTIONS            = 0x200,
+    };
+
+    static status_t getPlayerMessageOfDescriptions(
+            const uint8_t *data, ssize_t size,
+            uint32_t flags, int timeMs, PlayerMessage *playerMsg);
+private:
+    TextDescriptions2();
+
+    enum {
+        // These keys must be in sync with the keys in TimedText.java
+        KEY_DISPLAY_FLAGS                 = 1, // int
+        KEY_STYLE_FLAGS                   = 2, // int
+        KEY_BACKGROUND_COLOR_RGBA         = 3, // int
+        KEY_HIGHLIGHT_COLOR_RGBA          = 4, // int
+        KEY_SCROLL_DELAY                  = 5, // int
+        KEY_WRAP_TEXT                     = 6, // int
+        KEY_START_TIME                    = 7, // int
+        KEY_STRUCT_BLINKING_TEXT_LIST     = 8, // List<CharPos>
+        KEY_STRUCT_FONT_LIST              = 9, // List<Font>
+        KEY_STRUCT_HIGHLIGHT_LIST         = 10, // List<CharPos>
+        KEY_STRUCT_HYPER_TEXT_LIST        = 11, // List<HyperText>
+        KEY_STRUCT_KARAOKE_LIST           = 12, // List<Karaoke>
+        KEY_STRUCT_STYLE_LIST             = 13, // List<Style>
+        KEY_STRUCT_TEXT_POS               = 14, // TextPos
+        KEY_STRUCT_JUSTIFICATION          = 15, // Justification
+        KEY_STRUCT_TEXT                   = 16, // Text
+
+        KEY_GLOBAL_SETTING                = 101,
+        KEY_LOCAL_SETTING                 = 102,
+        KEY_START_CHAR                    = 103,
+        KEY_END_CHAR                      = 104,
+        KEY_FONT_ID                       = 105,
+        KEY_FONT_SIZE                     = 106,
+        KEY_TEXT_COLOR_RGBA               = 107,
+    };
+
+    static status_t extractSRTLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, PlayerMessage *playerMsg);
+    static status_t extract3GPPGlobalDescriptions(
+            const uint8_t *data, ssize_t size,
+            PlayerMessage *playerMsg);
+    static status_t extract3GPPLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, PlayerMessage *playerMsg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions2);
+};
+
+}  // namespace android
+#endif  // TEXT_DESCRIPTIONS2_H_
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 5597488..2552073 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -17,6 +17,8 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaDrm"
 
+#include <inttypes.h>
+
 #include <media/NdkMediaDrm.h>
 
 #include <cutils/properties.h>
@@ -40,10 +42,32 @@
 {
 private:
     AMediaDrm *mObj;
-    AMediaDrmEventListener mListener;
+    AMediaDrmEventListener mEventListener;
+    AMediaDrmExpirationUpdateListener mExpirationUpdateListener;
+    AMediaDrmKeysChangeListener mKeysChangeListener;
 
 public:
-    DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), mListener(listener) {}
+    DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj),
+            mEventListener(listener), mExpirationUpdateListener(NULL), mKeysChangeListener(NULL) {}
+
+    DrmListener(AMediaDrm *obj, AMediaDrmExpirationUpdateListener listener) : mObj(obj),
+            mEventListener(NULL), mExpirationUpdateListener(listener), mKeysChangeListener(NULL) {}
+
+    DrmListener(AMediaDrm *obj, AMediaDrmKeysChangeListener listener) : mObj(obj),
+            mEventListener(NULL), mExpirationUpdateListener(NULL), mKeysChangeListener(listener) {}
+
+    void setEventListener(AMediaDrmEventListener listener) {
+        mEventListener = listener;
+    }
+
+    void setExpirationUpdateListener(AMediaDrmExpirationUpdateListener listener) {
+        mExpirationUpdateListener = listener;
+    }
+
+    void setKeysChangeListener(AMediaDrmKeysChangeListener listener) {
+        mKeysChangeListener = listener;
+    }
+
     void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj);
 };
 
@@ -62,27 +86,75 @@
 };
 
 void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
-    if (!mListener) {
+    if (!mEventListener && !mExpirationUpdateListener && !mKeysChangeListener) {
+        ALOGE("No listeners are specified");
         return;
     }
 
+    obj->setDataPosition(0);
+
     AMediaDrmSessionId sessionId = {NULL, 0};
     int32_t sessionIdSize = obj->readInt32();
-    if (sessionIdSize) {
-        uint8_t *sessionIdData = new uint8_t[sessionIdSize];
-        sessionId.ptr = sessionIdData;
-        sessionId.length = sessionIdSize;
-        obj->read(sessionIdData, sessionId.length);
+    if (sessionIdSize <= 0) {
+        ALOGE("Invalid session id size");
+        return;
     }
 
-    int32_t dataSize = obj->readInt32();
-    uint8_t *data = NULL;
-    if (dataSize) {
-        data = new uint8_t[dataSize];
-        obj->read(data, dataSize);
+    std::unique_ptr<uint8_t[]> sessionIdData(new uint8_t[sessionIdSize]);
+    sessionId.ptr = sessionIdData.get();
+    sessionId.length = sessionIdSize;
+    status_t err = obj->read(sessionIdData.get(), sessionId.length);
+    if (err != OK) {
+        ALOGE("Failed to read session id, error=%d", err);
+        return;
     }
 
-    // translate DrmPlugin event types into their NDK equivalents
+    if (DrmPlugin::kDrmPluginEventExpirationUpdate == eventType) {
+        int64_t expiryTimeInMS = obj->readInt64();
+        if (expiryTimeInMS >= 0) {
+            (*mExpirationUpdateListener)(mObj, &sessionId, expiryTimeInMS);
+        } else {
+            ALOGE("Failed to read expiry time, status=%" PRId64 "", expiryTimeInMS);
+        }
+        return;
+    } else if (DrmPlugin::kDrmPluginEventKeysChange == eventType) {
+        int32_t numKeys = 0;
+        err = obj->readInt32(&numKeys);
+        if (err != OK) {
+            ALOGE("Failed to read number of keys status, error=%d", err);
+            return;
+        }
+
+        Vector<AMediaDrmKeyStatus> keysStatus;
+        std::vector<std::unique_ptr<uint8_t[]> > dataPointers;
+        AMediaDrmKeyStatus keyStatus;
+
+        for (size_t i = 0; i < numKeys; ++i) {
+            keyStatus.keyId.ptr = nullptr;
+            keyStatus.keyId.length = 0;
+            int32_t idSize = obj->readInt32();
+            if (idSize > 0) {
+                std::unique_ptr<uint8_t[]> data(new uint8_t[idSize]);
+                err = obj->read(data.get(), idSize);
+                if (err != OK) {
+                    ALOGE("Failed to read key data, error=%d", err);
+                    return;
+                }
+                keyStatus.keyId.ptr = data.get();
+                keyStatus.keyId.length = idSize;
+                dataPointers.push_back(std::move(data));
+            }
+            keyStatus.keyType = static_cast<AMediaDrmKeyStatusType>(obj->readInt32());
+            keysStatus.push(keyStatus);
+        }
+
+        bool hasNewUsableKey = obj->readInt32();
+        (*mKeysChangeListener)(mObj, &sessionId, keysStatus.array(), numKeys, hasNewUsableKey);
+        return;
+    }
+
+    // Handles AMediaDrmEventListener below:
+    //  translates DrmPlugin event types into their NDK equivalents
     AMediaDrmEventType ndkEventType;
     switch(eventType) {
         case DrmPlugin::kDrmPluginEventProvisionRequired:
@@ -97,19 +169,30 @@
         case DrmPlugin::kDrmPluginEventVendorDefined:
             ndkEventType = EVENT_VENDOR_DEFINED;
             break;
+        case DrmPlugin::kDrmPluginEventSessionReclaimed:
+            ndkEventType = EVENT_SESSION_RECLAIMED;
+            break;
         default:
             ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
-            goto cleanup;
+            return;
     }
 
-    (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
-
- cleanup:
-    delete [] sessionId.ptr;
-    delete [] data;
+    int32_t dataSize = obj->readInt32();
+    uint8_t *data = NULL;
+    if (dataSize > 0) {
+        data = new uint8_t[dataSize];
+        err = obj->read(data, dataSize);
+        if (err == OK) {
+            (*mEventListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
+        } else {
+            ALOGE("Failed to read event data, error=%d", err);
+        }
+        delete [] data;
+    } else {
+        ALOGE("Error reading parcel: invalid event data size=%d", dataSize);
+    }
 }
 
-
 extern "C" {
 
 static media_status_t translateStatus(status_t status) {
@@ -198,6 +281,8 @@
 AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
     AMediaDrm *mObj = new AMediaDrm();
     mObj->mDrm = CreateDrmFromUUID(uuid);
+
+    mObj->mListener.clear();
     return mObj;
 }
 
@@ -216,11 +301,47 @@
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    mObj->mListener = new DrmListener(mObj, listener);
+
+    if (mObj->mListener.get()) {
+        mObj->mListener->setEventListener(listener);
+    } else {
+        mObj->mListener = new DrmListener(mObj, listener);
+    }
     mObj->mDrm->setListener(mObj->mListener);
     return AMEDIA_OK;
 }
 
+EXPORT
+media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *mObj,
+        AMediaDrmExpirationUpdateListener listener) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+
+    if (mObj->mListener.get()) {
+        mObj->mListener->setExpirationUpdateListener(listener);
+    } else {
+        mObj->mListener = new DrmListener(mObj, listener);
+    }
+    mObj->mDrm->setListener(mObj->mListener);
+    return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *mObj,
+        AMediaDrmKeysChangeListener listener) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+
+    if (mObj->mListener.get()) {
+        mObj->mListener->setKeysChangeListener(listener);
+    } else {
+        mObj->mListener = new DrmListener(mObj, listener);
+    }
+    mObj->mDrm->setListener(mObj->mListener);
+    return AMEDIA_OK;
+}
 
 static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
     for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) {
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 8c1ac59..c66cd50 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -84,8 +84,17 @@
 
 EXPORT
 media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
-    ALOGV("setDataSource(%s)", location);
-    // TODO: add header support
+    return AMediaExtractor_setDataSourceWithHeaders(mData, location, 0, NULL, NULL);
+}
+
+EXPORT
+media_status_t AMediaExtractor_setDataSourceWithHeaders(AMediaExtractor *mData,
+        const char *uri,
+        int numheaders,
+        const char * const *keys,
+        const char * const *values) {
+
+    ALOGV("setDataSource(%s)", uri);
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jobject service = NULL;
@@ -109,7 +118,7 @@
         return AMEDIA_ERROR_UNSUPPORTED;
     }
 
-    jstring jloc = env->NewStringUTF(location);
+    jstring jloc = env->NewStringUTF(uri);
 
     service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc);
     env->DeleteLocalRef(jloc);
@@ -120,7 +129,15 @@
         httpService = interface_cast<IMediaHTTPService>(binder);
     }
 
-    status_t err = mData->mImpl->setDataSource(httpService, location, NULL);
+    KeyedVector<String8, String8> headers;
+    for (int i = 0; i < numheaders; ++i) {
+        String8 key8(keys[i]);
+        String8 value8(values[i]);
+        headers.add(key8, value8);
+    }
+
+    status_t err;
+    err = mData->mImpl->setDataSource(httpService, uri, numheaders > 0 ? &headers : NULL);
     env->ExceptionClear();
     return translate_error(err);
 }
diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h
index 0209681..2e438d9 100644
--- a/media/ndk/include/media/NdkMediaDrm.h
+++ b/media/ndk/include/media/NdkMediaDrm.h
@@ -56,6 +56,7 @@
 typedef AMediaDrmByteArray AMediaDrmScope;
 typedef AMediaDrmByteArray AMediaDrmKeySetId;
 typedef AMediaDrmByteArray AMediaDrmSecureStop;
+typedef AMediaDrmByteArray AMediaDrmKeyId;
 
 typedef enum AMediaDrmEventType {
     /**
@@ -81,12 +82,89 @@
      * This event may indicate some specific vendor-defined condition, see your
      * DRM provider documentation for details
      */
-    EVENT_VENDOR_DEFINED = 4
+    EVENT_VENDOR_DEFINED = 4,
+
+    /**
+     * This event indicates that a session opened by the app has been reclaimed
+     * by the resource manager.
+     */
+    EVENT_SESSION_RECLAIMED = 5,
 } AMediaDrmEventType;
 
+typedef enum AMediaDrmKeyType {
+    /**
+     * This key request type specifies that the keys will be for online use, they will
+     * not be saved to the device for subsequent use when the device is not connected
+     * to a network.
+     */
+    KEY_TYPE_STREAMING = 1,
+
+    /**
+     * This key request type specifies that the keys will be for offline use, they
+     * will be saved to the device for use when the device is not connected to a network.
+     */
+    KEY_TYPE_OFFLINE = 2,
+
+    /**
+     * This key request type specifies that previously saved offline keys should be released.
+     */
+    KEY_TYPE_RELEASE = 3
+} AMediaDrmKeyType;
+
+/**
+ *  Data type containing {key, value} pair
+ */
+typedef struct AMediaDrmKeyValuePair {
+    const char *mKey;
+    const char *mValue;
+} AMediaDrmKeyValue;
+
+typedef enum AMediaKeyStatusType {
+    /**
+     * The key is currently usable to decrypt media data.
+     */
+    KEY_STATUS_TYPE_USABLE,
+
+    /**
+     * The key is no longer usable to decrypt media data because its expiration
+     * time has passed.
+     */
+    KEY_STATUS_TYPE_EXPIRED,
+
+    /**
+     * The key is not currently usable to decrypt media data because its output
+     * requirements cannot currently be met.
+     */
+    KEY_STATUS_TYPE_OUTPUTNOTALLOWED,
+
+    /**
+     * The status of the key is not yet known and is being determined.
+     */
+    KEY_STATUS_TYPE_STATUSPENDING,
+
+    /**
+     * The key is not currently usable to decrypt media data because of an
+     * internal error in processing unrelated to input parameters.
+     */
+    KEY_STATUS_TYPE_INTERNALERROR,
+
+} AMediaDrmKeyStatusType;
+
+typedef struct AMediaDrmKeyStatus {
+    AMediaDrmKeyId keyId;
+    AMediaDrmKeyStatusType keyType;
+} AMediaDrmKeyStatus;
+
 typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId,
         AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize);
 
+typedef void (*AMediaDrmExpirationUpdateListener)(AMediaDrm *,
+        const AMediaDrmSessionId *sessionId, int64_t expiryTimeInMS);
+
+typedef void (*AMediaDrmKeysChangeListener)(AMediaDrm *,
+        const AMediaDrmSessionId *sessionId, const AMediaDrmKeyStatus *keyStatus,
+        size_t numKeys, bool hasNewUsableKey);
+
 #if __ANDROID_API__ >= 21
 
 /**
@@ -120,6 +198,22 @@
         AMediaDrmEventListener listener) __INTRODUCED_IN(21);
 
 /**
+ * Register a callback to be invoked when an expiration update event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *,
+        AMediaDrmExpirationUpdateListener listener) __INTRODUCED_IN(29);
+
+/**
+ * Register a callback to be invoked when a key status change event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *,
+        AMediaDrmKeysChangeListener listener) __INTRODUCED_IN(29);
+
+/**
  * Open a new session with the MediaDrm object.  A session ID is returned.
  *
  * returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
@@ -135,34 +229,6 @@
 media_status_t AMediaDrm_closeSession(AMediaDrm *,
         const AMediaDrmSessionId *sessionId) __INTRODUCED_IN(21);
 
-typedef enum AMediaDrmKeyType {
-    /**
-     * This key request type species that the keys will be for online use, they will
-     * not be saved to the device for subsequent use when the device is not connected
-     * to a network.
-     */
-    KEY_TYPE_STREAMING = 1,
-
-    /**
-     * This key request type specifies that the keys will be for offline use, they
-     * will be saved to the device for use when the device is not connected to a network.
-     */
-    KEY_TYPE_OFFLINE = 2,
-
-    /**
-     * This key request type specifies that previously saved offline keys should be released.
-     */
-    KEY_TYPE_RELEASE = 3
-} AMediaDrmKeyType;
-
-/**
- *  Data type containing {key, value} pair
- */
-typedef struct AMediaDrmKeyValuePair {
-    const char *mKey;
-    const char *mValue;
-} AMediaDrmKeyValue;
-
 /**
  * A key request/response exchange occurs between the app and a license server
  * to obtain or release keys used to decrypt encrypted content.
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index 6a1796f..413bc1a 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -72,7 +72,27 @@
  */
 media_status_t AMediaExtractor_setDataSource(AMediaExtractor*,
         const char *location) __INTRODUCED_IN(21);
-        // TODO support headers
+
+#if __ANDROID_API__ >= 29
+/**
+ * Set the |uri| from which the extractor will read,
+ * plus additional http headers when initiating the request.
+ *
+ * Headers will contain corresponding items from |keys| & |values|
+ * from indices 0 (inclusive) to numheaders-1 (inclusive):
+ *
+ * keys[0]:values[0]
+ * ...
+ * keys[numheaders - 1]:values[numheaders - 1]
+ *
+ */
+media_status_t AMediaExtractor_setDataSourceWithHeaders(AMediaExtractor*,
+        const char *uri,
+        int numheaders,
+        const char * const *keys,
+        const char * const *values) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
 
 #if __ANDROID_API__ >= 28
 
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 0751a55..1fd69a2 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -184,6 +184,7 @@
     AMediaExtractor_setDataSource;
     AMediaExtractor_setDataSourceCustom; # introduced=28
     AMediaExtractor_setDataSourceFd;
+    AMediaExtractor_setDataSourceWithHeaders; # introduced=29
     AMediaExtractor_unselectTrack;
     AMediaFormat_clear; # introduced=29
     AMediaFormat_copy; # introduced=29
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index c4c742e..6cab441 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1157,7 +1157,8 @@
 {
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0 &&
-        (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT)) {
+        (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT) &&
+        !isNonOffloadableEnabled_l()) {
         PlaybackThread *t = (PlaybackThread *)thread.get();
         float vol_l = (float)left / (1 << 24);
         float vol_r = (float)right / (1 << 24);
@@ -2552,6 +2553,11 @@
 bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
 {
     Mutex::Autolock _l(mLock);
+    return isNonOffloadableEnabled_l();
+}
+
+bool AudioFlinger::EffectChain::isNonOffloadableEnabled_l()
+{
     size_t size = mEffects.size();
     for (size_t i = 0; i < size; i++) {
         if (mEffects[i]->isEnabled() && !mEffects[i]->isOffloadable()) {
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index e04ee8e..15a26ea 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -378,6 +378,7 @@
 
     // At least one non offloadable effect in the chain is enabled
     bool isNonOffloadableEnabled();
+    bool isNonOffloadableEnabled_l();
 
     void syncHalEffectsState();
 
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index a42d6b3..f650b66 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -195,6 +195,9 @@
             //       to avoid blocking here and to prevent possible priority inversion
             mMixer = new AudioMixer(frameCount, mSampleRate);
             // FIXME See the other FIXME at FastMixer::setNBLogWriter()
+            // TODO define an int to thread type mapping for the "2" below.
+            const NBLog::thread_info_t info = { 2 /*FastMixer*/, frameCount, mSampleRate };
+            LOG_THREAD_INFO(info);
             const size_t mixerFrameSize = mSinkChannelCount
                     * audio_bytes_per_sample(mMixerBufferFormat);
             mMixerBufferSize = mixerFrameSize * frameCount;
@@ -340,10 +343,19 @@
     FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
 
     if (mIsWarm) {
+        // Logging timestamps for FastMixer is currently disabled to make memory room for logging
+        // other statistics in FastMixer.
+        // To re-enable, delete the #ifdef FASTMIXER_LOG_HIST_TS lines (and the #endif lines).
+#ifdef FASTMIXER_LOG_HIST_TS
         LOG_HIST_TS();
+#endif
+        //ALOGD("Eric FastMixer::onWork() mIsWarm");
     } else {
         dumpState->mTimestampVerifier.discontinuity();
+        // See comment in if block.
+#ifdef FASTMIXER_LOG_HIST_TS
         LOG_AUDIO_STATE();
+#endif
     }
     const FastMixerState::Command command = mCommand;
     const size_t frameCount = current->mFrameCount;
@@ -498,8 +510,10 @@
                             timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
                     // We don't compensate for server - kernel time difference and
                     // only update latency if we have valid info.
-                    dumpState->mLatencyMs =
+                    const double latencyMs =
                             (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate;
+                    dumpState->mLatencyMs = latencyMs;
+                    LOG_LATENCY(latencyMs);
                 } else {
                     // HAL reported that more frames were presented than were written
                     mNativeFramesWrittenButNotPresented = 0;
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index e587026..09101d9 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -22,6 +22,7 @@
 #include "Configuration.h"
 #include <linux/futex.h>
 #include <sys/syscall.h>
+#include <audio_utils/clock.h>
 #include <cutils/atomic.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
@@ -260,6 +261,9 @@
                         mIsWarm = true;
                         mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs;
                         mDumpState->mWarmupCycles = mWarmupCycles;
+                        const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1e3) +
+                                (mMeasuredWarmupTs.tv_nsec * 1e-6);
+                        LOG_WARMUP_TIME(measuredWarmupMs);
                     }
                 }
                 mSleepNs = -1;
@@ -270,6 +274,7 @@
                         ALOGV("underrun: time since last cycle %d.%03ld sec",
                                 (int) sec, nsec / 1000000L);
                         mDumpState->mUnderruns++;
+                        LOG_UNDERRUN(audio_utils_ns_from_timespec(&newTs));
                         mIgnoreNextOverrun = true;
                     } else if (nsec < mOverrunNs) {
                         if (mIgnoreNextOverrun) {
@@ -279,6 +284,7 @@
                             ALOGV("overrun: time since last cycle %d.%03ld sec",
                                     (int) sec, nsec / 1000000L);
                             mDumpState->mOverruns++;
+                            LOG_OVERRUN(audio_utils_ns_from_timespec(&newTs));
                         }
                         // This forces a minimum cycle time. It:
                         //  - compensates for an audio HAL with jitter due to sample rate conversion
@@ -339,7 +345,7 @@
                     // these stores #1, #2, #3 are not atomic with respect to each other,
                     // or with respect to store #4 below
                     mDumpState->mMonotonicNs[i] = monotonicNs;
-                    LOG_MONOTONIC_CYCLE_TIME(monotonicNs);
+                    LOG_WORK_TIME(monotonicNs);
                     mDumpState->mLoadNs[i] = loadNs;
 #ifdef CPU_FREQUENCY_STATISTICS
                     mDumpState->mCpukHz[i] = kHz;
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 0fea42a..dd2e7c9 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -97,10 +97,32 @@
 #define LOG_AUDIO_STATE() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
         x->logEventHistTs(NBLog::EVENT_AUDIO_STATE, hash(__FILE__, __LINE__)); } while(0)
 
-// Record a typed entry that represents a thread's cycle time in nanoseconds.
+// Record a typed entry that represents a thread's work time in nanoseconds.
 // Parameter ns should be of type uint32_t.
-#define LOG_MONOTONIC_CYCLE_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
-        x->logMonotonicCycleTime(ns); } while (0)
+#define LOG_WORK_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_WORK_TIME>(ns); } while (0)
+
+// Log the difference bewteen frames presented by HAL and frames written to HAL output sink,
+// divided by the sample rate. Parameter ms is of type double.
+#define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_LATENCY>(ms); } while (0)
+
+// Record thread warmup time in milliseconds. Parameter ms is of type double.
+#define LOG_WARMUP_TIME(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_WARMUP_TIME>(ms); } while (0)
+
+// Record thread underrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_UNDERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_UNDERRUN>(ns); } while (0)
+
+// Record thread overrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_OVERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+        x->log<NBLog::EVENT_OVERRUN>(ns); } while (0)
+
+// Record thread info. This currently includes type, frameCount, and sampleRate.
+// Parameter type is thread_info_t as defined in NBLog.h.
+#define LOG_THREAD_INFO(info) do { NBLog::Writer *x = tlNBLogWriter; \
+        if (x != nullptr) x->log<NBLog::EVENT_THREAD_INFO>(info); } while (0)
 
 namespace android {
 extern "C" {
diff --git a/services/audiopolicy/common/include/RoutingStrategy.h b/services/audiopolicy/common/include/RoutingStrategy.h
index d38967e..f8a1cd6 100644
--- a/services/audiopolicy/common/include/RoutingStrategy.h
+++ b/services/audiopolicy/common/include/RoutingStrategy.h
@@ -23,6 +23,7 @@
 #define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000
 
 enum routing_strategy {
+    STRATEGY_NONE = -1,
     STRATEGY_MEDIA,
     STRATEGY_PHONE,
     STRATEGY_SONIFICATION,
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 9b8f095..bacb780 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -17,10 +17,8 @@
     src/AudioCollections.cpp \
     src/EffectDescriptor.cpp \
     src/SoundTriggerSession.cpp \
-    src/SessionRoute.cpp \
     src/VolumeCurve.cpp \
     src/TypeConverter.cpp \
-    src/AudioSession.cpp \
     src/ClientDescriptor.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 44662e5..72d5a8c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -16,19 +16,19 @@
 
 #pragma once
 
-#include "AudioIODescriptorInterface.h"
-#include "AudioPort.h"
-#include "AudioSession.h"
-#include "ClientDescriptor.h"
-#include <utils/Errors.h>
 #include <system/audio.h>
+#include <utils/Errors.h>
 #include <utils/SortedVector.h>
 #include <utils/KeyedVector.h>
+#include "AudioIODescriptorInterface.h"
+#include "AudioPort.h"
+#include "ClientDescriptor.h"
 
 namespace android {
 
 class IOProfile;
 class AudioMix;
+class AudioPolicyClientInterface;
 
 // descriptor for audio inputs. Used to maintain current configuration of each opened audio input
 // and keep track of the usage of this input.
@@ -39,7 +39,6 @@
                                   AudioPolicyClientInterface *clientInterface);
     audio_port_handle_t getId() const;
     audio_module_handle_t getModuleHandle() const;
-    uint32_t getOpenRefCount() const;
 
     status_t    dump(int fd);
 
@@ -56,19 +55,13 @@
     SortedVector<audio_session_t> getPreemptedSessions() const;
     bool hasPreemptedSession(audio_session_t session) const;
     void clearPreemptedSessions();
-    bool isActive() const;
+    bool isActive() const { return mGlobalActiveCount > 0; }
     bool isSourceActive(audio_source_t source) const;
     audio_source_t inputSource(bool activeOnly = false) const;
     bool isSoundTrigger() const;
-    status_t addAudioSession(audio_session_t session,
-                             const sp<AudioSession>& audioSession);
-    status_t removeAudioSession(audio_session_t session);
-    sp<AudioSession> getAudioSession(audio_session_t session) const;
-    AudioSessionCollection getAudioSessions(bool activeOnly) const;
-    size_t getAudioSessionCount(bool activeOnly) const;
     audio_source_t getHighestPrioritySource(bool activeOnly) const;
-    void changeRefCount(audio_session_t session, int delta);
-
+    void setClientActive(const sp<RecordClientDescriptor>& client, bool active);
+    int32_t activeCount() { return mGlobalActiveCount; }
 
     // implementation of AudioIODescriptorInterface
     audio_config_base_t getConfig() const override;
@@ -82,24 +75,24 @@
                   audio_input_flags_t flags,
                   audio_io_handle_t *input);
     // Called when a stream is about to be started.
-    // Note: called after changeRefCount(session, 1)
+    // Note: called after setClientActive(client, true)
     status_t start();
     // Called after a stream is stopped
-    // Note: called after changeRefCount(session, -1)
+    // Note: called after setClientActive(client, false)
     void stop();
     void close();
 
-    RecordClientMap& clients() { return mClients; }
+    RecordClientMap& clientsMap() { return mClients; }
     RecordClientVector getClientsForSession(audio_session_t session);
+    RecordClientVector clientsList(bool activeOnly = false,
+        audio_source_t source = AUDIO_SOURCE_DEFAULT, bool preferredDeviceOnly = false) const;
 
  private:
 
-    void updateSessionRecordingConfiguration(int event, const sp<AudioSession>& audioSession);
+    void updateClientRecordingConfiguration(int event, const sp<RecordClientDescriptor>& client);
 
     audio_patch_handle_t          mPatchHandle;
     audio_port_handle_t           mId;
-    // audio sessions attached to this input
-    AudioSessionCollection        mSessions;
     // Because a preemptible capture session can preempt another one, we end up in an endless loop
     // situation were each session is allowed to restart after being preempted,
     // thus preempting the other one which restarts and so on.
@@ -108,7 +101,7 @@
     // We also inherit sessions from the preempted input to avoid a 3 way preemption loop etc...
     SortedVector<audio_session_t> mPreemptedSessions;
     AudioPolicyClientInterface *mClientInterface;
-    uint32_t mGlobalRefCount;  // non-session-specific ref count
+    int32_t mGlobalActiveCount;  // non-client-specific activity ref count
 
     RecordClientMap mClients;
 };
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index ff0201a..27b1c93 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -59,7 +59,10 @@
                            audio_devices_t device,
                            uint32_t delayMs,
                            bool force);
-    virtual void changeRefCount(audio_stream_type_t stream, int delta);
+    virtual void changeStreamActiveCount(audio_stream_type_t stream, int delta);
+            uint32_t streamActiveCount(audio_stream_type_t stream) const
+                            { return mActiveCount[stream]; }
+            void setClientActive(const sp<TrackClientDescriptor>& client, bool active);
 
     bool isActive(uint32_t inPastMs = 0) const;
     bool isStreamActive(audio_stream_type_t stream,
@@ -78,19 +81,23 @@
     audio_patch_handle_t getPatchHandle() const override;
     void setPatchHandle(audio_patch_handle_t handle) override;
 
-    TrackClientMap& clients() { return mClients; }
+    TrackClientMap& clientsMap() { return mClients; }
+    TrackClientVector clientsList(bool activeOnly = false,
+        routing_strategy strategy = STRATEGY_NONE, bool preferredDeviceOnly = false) const;
 
     sp<AudioPort> mPort;
     audio_devices_t mDevice;                   // current device this output is routed to
-    uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output
     nsecs_t mStopTime[AUDIO_STREAM_CNT];
     float mCurVolume[AUDIO_STREAM_CNT];   // current stream volume in dB
     int mMuteCount[AUDIO_STREAM_CNT];     // mute request counter
     bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
                                         // device selection. See checkDeviceMuteStrategies()
     AudioPolicyClientInterface *mClientInterface;
+    AudioMix *mPolicyMix;             // non NULL when used by a dynamic policy
 
 protected:
+    uint32_t mActiveCount[AUDIO_STREAM_CNT]; // number of streams of each type active on this output
+    uint32_t mGlobalActiveCount;  // non-client-specific active count
     audio_patch_handle_t mPatchHandle;
     audio_port_handle_t mId;
     TrackClientMap mClients;
@@ -114,7 +121,7 @@
     virtual bool isFixedVolume(audio_devices_t device);
     virtual sp<AudioOutputDescriptor> subOutput1() { return mOutput1; }
     virtual sp<AudioOutputDescriptor> subOutput2() { return mOutput2; }
-    virtual void changeRefCount(audio_stream_type_t stream, int delta);
+    virtual void changeStreamActiveCount(audio_stream_type_t stream, int delta);
     virtual bool setVolume(float volume,
                            audio_stream_type_t stream,
                            audio_devices_t device,
@@ -132,10 +139,10 @@
                           audio_output_flags_t flags,
                           audio_io_handle_t *output);
             // Called when a stream is about to be started
-            // Note: called before changeRefCount(1);
+            // Note: called before setClientActive(true);
             status_t start();
             // Called after a stream is stopped.
-            // Note: called after changeRefCount(-1);
+            // Note: called after setClientActive(false);
             void stop();
             void close();
             status_t openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
@@ -146,12 +153,10 @@
     audio_io_handle_t mIoHandle;           // output handle
     uint32_t mLatency;                  //
     audio_output_flags_t mFlags;   //
-    AudioMix *mPolicyMix;             // non NULL when used by a dynamic policy
     sp<SwAudioOutputDescriptor> mOutput1;    // used by duplicated outputs: first output
     sp<SwAudioOutputDescriptor> mOutput2;    // used by duplicated outputs: second output
     uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
     audio_session_t mDirectClientSession; // session id of the direct output client
-    uint32_t mGlobalRefCount;  // non-stream-specific ref count
 };
 
 // Audio output driven by an input device directly.
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index f861b95..78e7ec9 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -28,7 +28,6 @@
 #include <AudioPolicyMix.h>
 #include <EffectDescriptor.h>
 #include <SoundTriggerSession.h>
-#include <SessionRoute.h>
 
 namespace android {
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
deleted file mode 100644
index 1636d3a..0000000
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#pragma once
-
-#include <system/audio.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <media/AudioPolicy.h>
-#include <media/IAudioPolicyServiceClient.h>
-#include "AudioIODescriptorInterface.h"
-
-namespace android {
-
-class AudioPolicyClientInterface;
-
-class AudioSession : public RefBase
-{
-public:
-    AudioSession(audio_session_t session,
-                 audio_source_t inputSource,
-                 audio_format_t format,
-                 uint32_t sampleRate,
-                 audio_channel_mask_t channelMask,
-                 audio_input_flags_t flags,
-                 uid_t uid,
-                 bool isSoundTrigger);
-
-    status_t dump(int fd, int spaces, int index) const;
-
-    audio_session_t session() const { return mRecordClientInfo.session; }
-    audio_source_t inputSource()const { return mRecordClientInfo.source; }
-    audio_format_t format() const { return mConfig.format; }
-    uint32_t sampleRate() const { return mConfig.sample_rate; }
-    audio_channel_mask_t channelMask() const { return mConfig.channel_mask; }
-    audio_config_base config() const { return mConfig; }
-    record_client_info_t recordClientInfo() const { return mRecordClientInfo; }
-    audio_input_flags_t flags() const { return mFlags; }
-    uid_t uid() const { return mRecordClientInfo.uid; }
-    void setUid(uid_t uid) { mRecordClientInfo.uid = uid; }
-    bool matches(const sp<AudioSession> &other) const;
-    bool isSoundTrigger() const { return mIsSoundTrigger; }
-    void setSilenced(bool silenced) { mSilenced = silenced; }
-    bool isSilenced() const { return mSilenced; }
-    uint32_t openCount() const { return mOpenCount; } ;
-    uint32_t activeCount() const { return mActiveCount; } ;
-
-    uint32_t changeOpenCount(int delta);
-    uint32_t changeActiveCount(int delta);
-
-private:
-    record_client_info_t mRecordClientInfo;
-    const struct audio_config_base mConfig;
-    const audio_input_flags_t mFlags;
-    bool  mIsSoundTrigger;
-    bool mSilenced;
-    uint32_t  mOpenCount;
-    uint32_t  mActiveCount;
-};
-
-class AudioSessionCollection :
-    public DefaultKeyedVector<audio_session_t, sp<AudioSession> >
-{
-public:
-    status_t addSession(audio_session_t session,
-                             const sp<AudioSession>& audioSession);
-
-    status_t removeSession(audio_session_t session);
-
-    uint32_t getOpenCount() const;
-
-    AudioSessionCollection getActiveSessions() const;
-    size_t getActiveSessionCount() const;
-    bool hasActiveSession() const;
-    bool isSourceActive(audio_source_t source) const;
-    audio_source_t getHighestPrioritySource(bool activeOnly) const;
-
-    status_t dump(int fd, int spaces) const;
-};
-
-} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 9efe57f..1a3300d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -27,6 +27,7 @@
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 #include "AudioPatch.h"
+#include "RoutingStrategy.h"
 
 namespace android {
 
@@ -53,8 +54,14 @@
     audio_attributes_t attributes() const { return mAttributes; }
     audio_config_base_t config() const { return mConfig; }
     audio_port_handle_t preferredDeviceId() const { return mPreferredDeviceId; };
+    void setPreferredDeviceId(audio_port_handle_t preferredDeviceId) {
+        mPreferredDeviceId = preferredDeviceId;
+    };
     void setActive(bool active) { mActive = active; }
     bool active() const { return mActive; }
+    bool hasPreferredDevice(bool activeOnly = false) const {
+        return mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive);
+    }
 
 private:
     const audio_port_handle_t mPortId;  // unique Id for this client
@@ -62,7 +69,7 @@
     const audio_session_t mSessionId;       // audio session ID
     const audio_attributes_t mAttributes; // usage...
     const audio_config_base_t mConfig;
-    const audio_port_handle_t mPreferredDeviceId;  // selected input device port ID
+          audio_port_handle_t mPreferredDeviceId;  // selected input device port ID
           bool mActive;
 
 protected:
@@ -75,10 +82,10 @@
 public:
     TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId,
                    audio_attributes_t attributes, audio_config_base_t config,
-                   audio_port_handle_t preferredDeviceId,
-                   audio_stream_type_t stream, audio_output_flags_t flags) :
+                   audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
+                          routing_strategy strategy, audio_output_flags_t flags) :
         ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId),
-        mStream(stream), mFlags(flags) {}
+        mStream(stream), mStrategy(strategy), mFlags(flags) {}
     ~TrackClientDescriptor() override = default;
 
     using ClientDescriptor::dump;
@@ -86,9 +93,11 @@
 
     audio_output_flags_t flags() const { return mFlags; }
     audio_stream_type_t stream() const { return mStream; }
+    routing_strategy strategy() const { return mStrategy; }
 
 private:
     const audio_stream_type_t mStream;
+    const routing_strategy mStrategy;
     const audio_output_flags_t mFlags;
 };
 
@@ -98,9 +107,9 @@
     RecordClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId,
                         audio_attributes_t attributes, audio_config_base_t config,
                         audio_port_handle_t preferredDeviceId,
-                        audio_source_t source, audio_input_flags_t flags) :
+                        audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) :
         ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId),
-        mSource(source), mFlags(flags) {}
+        mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mSilenced(false) {}
     ~RecordClientDescriptor() override = default;
 
     using ClientDescriptor::dump;
@@ -108,10 +117,15 @@
 
     audio_source_t source() const { return mSource; }
     audio_input_flags_t flags() const { return mFlags; }
+    bool isSoundTrigger() const { return mIsSoundTrigger; }
+    void setSilenced(bool silenced) { mSilenced = silenced; }
+    bool isSilenced() const { return mSilenced; }
 
 private:
     const audio_source_t mSource;
     const audio_input_flags_t mFlags;
+    const bool mIsSoundTrigger;
+          bool mSilenced;
 };
 
 class SourceClientDescriptor: public TrackClientDescriptor
@@ -119,7 +133,7 @@
 public:
     SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes,
                            const sp<AudioPatch>& patchDesc, const sp<DeviceDescriptor>& srcDevice,
-                           audio_stream_type_t stream);
+                           audio_stream_type_t stream, routing_strategy strategy);
     ~SourceClientDescriptor() override = default;
 
     sp<AudioPatch> patchDesc() const { return mPatchDesc; }
diff --git a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
deleted file mode 100644
index 32b4440..0000000
--- a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#pragma once
-
-#include <system/audio.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-class DeviceDescriptor;
-class DeviceVector;
-
-class SessionRoute : public RefBase
-{
-public:
-    // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the
-    // streamType argument
-    static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT;
-
-    // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the
-    // source argument
-
-    static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT;
-
-    SessionRoute(audio_session_t session,
-                 audio_stream_type_t streamType,
-                 audio_source_t source,
-                 sp<DeviceDescriptor> deviceDescriptor,
-                 uid_t uid)
-        : mUid(uid),
-          mSession(session),
-          mDeviceDescriptor(deviceDescriptor),
-          mRefCount(0),
-          mActivityCount(0),
-          mChanged(false),
-          mStreamType(streamType),
-          mSource(source)
-    {}
-
-    void log(const char* prefix);
-
-    bool isActiveOrChanged() {
-        return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0));
-    }
-
-    uid_t                       mUid;
-    audio_session_t             mSession;
-    sp<DeviceDescriptor>        mDeviceDescriptor;
-
-    // "reference" counting
-    int                         mRefCount;      // +/- on references
-    int                         mActivityCount; // +/- on start/stop
-    bool                        mChanged;
-    // for outputs
-    const audio_stream_type_t   mStreamType;
-    // for inputs
-    const audio_source_t        mSource;
-};
-
-class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute> >
-{
-public:
-    // These constants identify the SessionRoutMap as holding EITHER input routes,
-    // or output routes.  An error will occur if an attempt is made to add a SessionRoute
-    // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a
-    // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT)
-    // and similarly  for output SessionRoutes and Input SessionRouteMaps.
-    typedef enum
-    {
-        MAPTYPE_INPUT = 0,
-        MAPTYPE_OUTPUT = 1
-    } session_route_map_type_t;
-
-    explicit SessionRouteMap(session_route_map_type_t mapType) :
-        mMapType(mapType)
-    {}
-
-    bool hasRoute(audio_session_t session);
-
-    void removeRoute(audio_session_t session);
-
-    int incRouteActivity(audio_session_t session);
-    int decRouteActivity(audio_session_t session);
-    bool getAndClearRouteChanged(audio_session_t session); // also clears the changed flag
-    void log(const char* caption);
-    audio_devices_t getActiveDeviceForStream(audio_stream_type_t streamType,
-                                             const DeviceVector& availableDevices);
-    // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the
-    // source argument.
-    // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT
-    // in the streamType argument.
-    void addRoute(audio_session_t session,
-                  audio_stream_type_t streamType,
-                  audio_source_t source,
-                  const sp<DeviceDescriptor>& deviceDescriptor,
-                  uid_t uid);
-
-private:
-    // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input)
-    // or outputs (mMapType == kSessionRouteMap_Output)
-    const session_route_map_type_t mMapType;
-};
-
-} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 2770e74..e25b0fe 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -17,13 +17,13 @@
 #define LOG_TAG "APM::AudioInputDescriptor"
 //#define LOG_NDEBUG 0
 
+#include <media/AudioPolicy.h>
+#include <policy.h>
 #include <AudioPolicyInterface.h>
 #include "AudioInputDescriptor.h"
 #include "IOProfile.h"
 #include "AudioGain.h"
 #include "HwModule.h"
-#include <media/AudioPolicy.h>
-#include <policy.h>
 
 namespace android {
 
@@ -32,7 +32,7 @@
     : mIoHandle(0),
       mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL),
       mProfile(profile), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0),
-      mClientInterface(clientInterface), mGlobalRefCount(0)
+      mClientInterface(clientInterface), mGlobalActiveCount(0)
 {
     if (profile != NULL) {
         profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
@@ -50,11 +50,6 @@
     return mProfile->getModuleHandle();
 }
 
-uint32_t AudioInputDescriptor::getOpenRefCount() const
-{
-    return mSessions.getOpenCount();
-}
-
 audio_port_handle_t AudioInputDescriptor::getId() const
 {
     return mId;
@@ -118,57 +113,45 @@
     mPreemptedSessions.clear();
 }
 
-bool AudioInputDescriptor::isActive() const {
-    return mSessions.hasActiveSession();
-}
-
 bool AudioInputDescriptor::isSourceActive(audio_source_t source) const
 {
-    return mSessions.isSourceActive(source);
+    for (const auto &client : mClients) {
+        if (client.second->active() &&
+            ((client.second->source() == source) ||
+                ((source == AUDIO_SOURCE_VOICE_RECOGNITION) &&
+                    (client.second->source() == AUDIO_SOURCE_HOTWORD) &&
+                    client.second->isSoundTrigger()))) {
+            return true;
+        }
+    }
+    return false;
 }
 
 audio_source_t AudioInputDescriptor::getHighestPrioritySource(bool activeOnly) const
 {
+    audio_source_t source = AUDIO_SOURCE_DEFAULT;
+    int32_t priority = -1;
 
-    return mSessions.getHighestPrioritySource(activeOnly);
+    for (const auto &client : mClients) {
+        if (activeOnly && !client.second->active() ) {
+            continue;
+        }
+        int32_t curPriority = source_priority(client.second->source());
+        if (curPriority > priority) {
+            priority = curPriority;
+            source = client.second->source();
+        }
+    }
+    return source;
 }
 
 bool AudioInputDescriptor::isSoundTrigger() const {
-    // sound trigger and non sound trigger sessions are not mixed
-    // on a given input
-    return mSessions.valueAt(0)->isSoundTrigger();
-}
-
-sp<AudioSession> AudioInputDescriptor::getAudioSession(
-                                              audio_session_t session) const {
-    return mSessions.valueFor(session);
-}
-
-AudioSessionCollection AudioInputDescriptor::getAudioSessions(bool activeOnly) const
-{
-    if (activeOnly) {
-        return mSessions.getActiveSessions();
-    } else {
-        return mSessions;
+    // sound trigger and non sound trigger clients are not mixed on a given input
+    // so check only first client
+    if (mClients.size() == 0) {
+        return false;
     }
-}
-
-size_t AudioInputDescriptor::getAudioSessionCount(bool activeOnly) const
-{
-    if (activeOnly) {
-        return mSessions.getActiveSessionCount();
-    } else {
-        return mSessions.size();
-    }
-}
-
-status_t AudioInputDescriptor::addAudioSession(audio_session_t session,
-                         const sp<AudioSession>& audioSession) {
-    return mSessions.addSession(session, audioSession);
-}
-
-status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) {
-    return mSessions.removeSession(session);
+    return mClients.cbegin()->second->isSoundTrigger();
 }
 
 audio_patch_handle_t AudioInputDescriptor::getPatchHandle() const
@@ -179,9 +162,9 @@
 void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle)
 {
     mPatchHandle = handle;
-    for (size_t i = 0; i < mSessions.size(); i++) {
-        if (mSessions[i]->activeCount() > 0) {
-            updateSessionRecordingConfiguration(RECORD_CONFIG_EVENT_START, mSessions[i]);
+    for (const auto &client : mClients) {
+        if (client.second->active()) {
+            updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client.second);
         }
     }
 }
@@ -243,7 +226,7 @@
 
 status_t AudioInputDescriptor::start()
 {
-    if (getAudioSessionCount(true/*activeOnly*/) == 1) {
+    if (mGlobalActiveCount == 1) {
         if (!mProfile->canStartNewIo()) {
             ALOGI("%s mProfile->curActiveCount %d", __func__, mProfile->curActiveCount);
             return INVALID_OPERATION;
@@ -270,7 +253,7 @@
         LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u",
                             __FUNCTION__, mProfile->curOpenCount);
         // do not call stop() here as stop() is supposed to be called after
-        //  changeRefCount(session, -1) and we don't know how many sessions
+        //  setClientActive(client, false) and we don't know how many clients
         // are still active at this time
         if (isActive()) {
             mProfile->curActiveCount--;
@@ -280,27 +263,28 @@
     }
 }
 
-void AudioInputDescriptor::changeRefCount(audio_session_t session, int delta)
+void AudioInputDescriptor::setClientActive(const sp<RecordClientDescriptor>& client, bool active)
 {
-    sp<AudioSession> audioSession = mSessions.valueFor(session);
-    if (audioSession == 0) {
+    if (mClients.find(client->portId()) == mClients.end()
+         || active == client->active()) {
         return;
     }
-    // handle session-independent ref count
-    uint32_t oldGlobalRefCount = mGlobalRefCount;
-    if ((delta + (int)mGlobalRefCount) < 0) {
-        ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount);
-        delta = -((int)mGlobalRefCount);
+
+    // Handle non-client-specific activity ref count
+    int32_t oldGlobalActiveCount = mGlobalActiveCount;
+    if (!active && mGlobalActiveCount < 1) {
+        ALOGW("%s invalid deactivation with globalRefCount %d", __FUNCTION__, mGlobalActiveCount);
+        mGlobalActiveCount = 1;
     }
-    mGlobalRefCount += delta;
-    if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) {
+    mGlobalActiveCount += active ? 1 : -1;
+
+    if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) {
         if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
         {
             mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
                                                             MIX_STATE_MIXING);
         }
-
-    } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) {
+    } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) {
         if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
         {
             mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
@@ -308,32 +292,18 @@
         }
     }
 
-    uint32_t oldActiveCount = audioSession->activeCount();
-    if ((delta + (int)oldActiveCount) < 0) {
-        ALOGW("changeRefCount() invalid delta %d for sesion %d active count %d",
-              delta, session, oldActiveCount);
-        delta = -((int)oldActiveCount);
-    }
+    client->setActive(active);
 
-    audioSession->changeActiveCount(delta);
-
-    int event = RECORD_CONFIG_EVENT_NONE;
-    if ((oldActiveCount == 0) && (audioSession->activeCount() > 0)) {
-        event = RECORD_CONFIG_EVENT_START;
-    } else if ((oldActiveCount > 0) && (audioSession->activeCount() == 0)) {
-        event = RECORD_CONFIG_EVENT_STOP;
-    }
-    if (event != RECORD_CONFIG_EVENT_NONE) {
-        updateSessionRecordingConfiguration(event, audioSession);
-    }
+    int event = active ? RECORD_CONFIG_EVENT_START : RECORD_CONFIG_EVENT_STOP;
+    updateClientRecordingConfiguration(event, client);
 
 }
 
-void AudioInputDescriptor::updateSessionRecordingConfiguration(
-    int event, const sp<AudioSession>& audioSession) {
-
-    const audio_config_base_t sessionConfig = audioSession->config();
-    const record_client_info_t recordClientInfo = audioSession->recordClientInfo();
+void AudioInputDescriptor::updateClientRecordingConfiguration(
+    int event, const sp<RecordClientDescriptor>& client)
+{
+    const audio_config_base_t sessionConfig = client->config();
+    const record_client_info_t recordClientInfo{client->uid(), client->session(), client->source()};
     const audio_config_base_t config = getConfig();
     mClientInterface->onRecordingConfigurationUpdate(event,
                                                      &recordClientInfo, &sessionConfig,
@@ -352,6 +322,20 @@
     return clients;
 }
 
+RecordClientVector AudioInputDescriptor::clientsList(bool activeOnly, audio_source_t source,
+                                                     bool preferredDeviceOnly) const
+{
+    RecordClientVector clients;
+    for (const auto &client : mClients) {
+        if ((!activeOnly || client.second->active())
+            && (source == AUDIO_SOURCE_DEFAULT || source == client.second->source())
+            && (!preferredDeviceOnly || client.second->hasPreferredDevice())) {
+            clients.push_back(client.second);
+        }
+    }
+    return clients;
+}
+
 status_t AudioInputDescriptor::dump(int fd)
 {
     const size_t SIZE = 256;
@@ -371,8 +355,6 @@
 
     write(fd, result.string(), result.size());
 
-    mSessions.dump(fd, 1);
-
     size_t index = 0;
     result = " AudioRecord clients:\n";
     for (const auto& client: mClients) {
@@ -446,7 +428,7 @@
 {
     for (size_t i = 0; i < size(); i++) {
         sp<AudioInputDescriptor> inputDesc = valueAt(i);
-        for (const auto& client : inputDesc->clients()) {
+        for (const auto& client : inputDesc->clientsMap()) {
             if (client.second->portId() == portId) {
                 return inputDesc;
             }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 39fce4d..b6ff6ea 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -34,12 +34,12 @@
 
 AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port,
                                              AudioPolicyClientInterface *clientInterface)
-    : mPort(port), mDevice(AUDIO_DEVICE_NONE),
-      mClientInterface(clientInterface), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0)
+    : mPort(port), mDevice(AUDIO_DEVICE_NONE), mClientInterface(clientInterface),
+      mPolicyMix(NULL), mGlobalActiveCount(0), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0)
 {
     // clear usage count for all stream types
     for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
-        mRefCount[i] = 0;
+        mActiveCount[i] = 0;
         mCurVolume[i] = -1.0;
         mMuteCount[i] = 0;
         mStopTime[i] = 0;
@@ -103,17 +103,51 @@
     }
 }
 
-void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+void AudioOutputDescriptor::changeStreamActiveCount(audio_stream_type_t stream,
                                                                    int delta)
 {
-    if ((delta + (int)mRefCount[stream]) < 0) {
-        ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d",
-              delta, stream, mRefCount[stream]);
-        mRefCount[stream] = 0;
+    if ((delta + (int)mActiveCount[stream]) < 0) {
+        ALOGW("%s invalid delta %d for stream %d, active count %d",
+              __FUNCTION__, delta, stream, mActiveCount[stream]);
+        mActiveCount[stream] = 0;
         return;
     }
-    mRefCount[stream] += delta;
-    ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
+    mActiveCount[stream] += delta;
+    ALOGV("%s stream %d, count %d", __FUNCTION__, stream, mActiveCount[stream]);
+}
+
+void AudioOutputDescriptor::setClientActive(const sp<TrackClientDescriptor>& client, bool active)
+{
+    if (mClients.find(client->portId()) == mClients.end()
+        || active == client->active()) {
+        return;
+    }
+
+    changeStreamActiveCount(client->stream(), active ? 1 : -1);
+
+    // Handle non-client-specific activity ref count
+    int32_t oldGlobalActiveCount = mGlobalActiveCount;
+    if (!active && mGlobalActiveCount < 1) {
+        ALOGW("%s invalid deactivation with globalRefCount %d", __FUNCTION__, mGlobalActiveCount);
+        mGlobalActiveCount = 1;
+    }
+    mGlobalActiveCount += active ? 1 : -1;
+
+    if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) {
+        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+        {
+            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
+                                                            MIX_STATE_MIXING);
+        }
+    } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) {
+        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+        {
+            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
+                                                            MIX_STATE_IDLE);
+        }
+    }
+
+    client->setActive(active);
 }
 
 bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const
@@ -137,7 +171,7 @@
                                            uint32_t inPastMs,
                                            nsecs_t sysTime) const
 {
-    if (mRefCount[stream] != 0) {
+    if (mActiveCount[stream] != 0) {
         return true;
     }
     if (inPastMs == 0) {
@@ -201,6 +235,20 @@
     port->ext.mix.hw_module = getModuleHandle();
 }
 
+TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, routing_strategy strategy,
+                                                     bool preferredDeviceOnly) const
+{
+    TrackClientVector clients;
+    for (const auto &client : mClients) {
+        if ((!activeOnly || client.second->active())
+            && (strategy == STRATEGY_NONE || strategy == client.second->strategy())
+            && (!preferredDeviceOnly || client.second->hasPreferredDevice())) {
+            clients.push_back(client.second);
+        }
+    }
+    return clients;
+}
+
 status_t AudioOutputDescriptor::dump(int fd)
 {
     const size_t SIZE = 256;
@@ -217,11 +265,11 @@
     result.append(buffer);
     snprintf(buffer, SIZE, " Devices %08x\n", device());
     result.append(buffer);
-    snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
+    snprintf(buffer, SIZE, " Stream volume activeCount muteCount\n");
     result.append(buffer);
     for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) {
-        snprintf(buffer, SIZE, " %02d     %.03f     %02d       %02d\n",
-                 i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
+        snprintf(buffer, SIZE, " %02d     %.03f     %02d          %02d\n",
+                 i, mCurVolume[i], streamActiveCount((audio_stream_type_t)i), mMuteCount[i]);
         result.append(buffer);
     }
 
@@ -247,9 +295,9 @@
                                                  AudioPolicyClientInterface *clientInterface)
     : AudioOutputDescriptor(profile, clientInterface),
     mProfile(profile), mIoHandle(AUDIO_IO_HANDLE_NONE), mLatency(0),
-    mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
+    mFlags((audio_output_flags_t)0),
     mOutput1(0), mOutput2(0), mDirectOpenCount(0),
-    mDirectClientSession(AUDIO_SESSION_NONE), mGlobalRefCount(0)
+    mDirectClientSession(AUDIO_SESSION_NONE)
 {
     if (profile != NULL) {
         mFlags = (audio_output_flags_t)profile->getFlags();
@@ -313,41 +361,17 @@
     }
 }
 
-void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+void SwAudioOutputDescriptor::changeStreamActiveCount(audio_stream_type_t stream,
                                                                    int delta)
 {
     // forward usage count change to attached outputs
     if (isDuplicated()) {
-        mOutput1->changeRefCount(stream, delta);
-        mOutput2->changeRefCount(stream, delta);
+        mOutput1->changeStreamActiveCount(stream, delta);
+        mOutput2->changeStreamActiveCount(stream, delta);
     }
-    AudioOutputDescriptor::changeRefCount(stream, delta);
-
-    // handle stream-independent ref count
-    uint32_t oldGlobalRefCount = mGlobalRefCount;
-    if ((delta + (int)mGlobalRefCount) < 0) {
-        ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount);
-        mGlobalRefCount = 0;
-    } else {
-        mGlobalRefCount += delta;
-    }
-    if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) {
-        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
-        {
-            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
-                    MIX_STATE_MIXING);
-        }
-
-    } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) {
-        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
-        {
-            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
-                    MIX_STATE_IDLE);
-        }
-    }
+    AudioOutputDescriptor::changeStreamActiveCount(stream, delta);
 }
 
-
 bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device)
 {
     // unit gain if rerouting to external policy
@@ -523,7 +547,7 @@
 
         LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u",
                             __FUNCTION__, mProfile->curOpenCount);
-        // do not call stop() here as stop() is supposed to be called after changeRefCount(-1)
+        // do not call stop() here as stop() is supposed to be called after setClientActive(false)
         // and we don't know how many streams are still active at this time
         if (isActive()) {
             mProfile->curActiveCount--;
@@ -723,7 +747,7 @@
         }
         for (size_t i = 0; i < size(); i++) {
             const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
-            if (outputDesc->mRefCount[s] != 0) {
+            if (outputDesc->streamActiveCount((audio_stream_type_t)s)!= 0) {
                 return true;
             }
         }
@@ -742,7 +766,7 @@
 {
     for (size_t i = 0; i < size(); i++) {
         sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
-        for (const auto& client : outputDesc->clients()) {
+        for (const auto& client : outputDesc->clientsMap()) {
             if (client.second->portId() == portId) {
                 return outputDesc;
             }
@@ -788,7 +812,7 @@
         }
         for (size_t i = 0; i < size(); i++) {
             const sp<HwAudioOutputDescriptor> outputDesc = valueAt(i);
-            if (outputDesc->mRefCount[s] != 0) {
+            if (outputDesc->streamActiveCount((audio_stream_type_t)s) != 0) {
                 return true;
             }
         }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
deleted file mode 100644
index 5ea4c92..0000000
--- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2015 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_TAG "APM::AudioSession"
-//#define LOG_NDEBUG 0
-
-#include <AudioPolicyInterface.h>
-#include "policy.h"
-#include "AudioSession.h"
-#include "AudioGain.h"
-#include "TypeConverter.h"
-
-#include <log/log.h>
-#include <utils/String8.h>
-
-namespace android {
-
-AudioSession::AudioSession(audio_session_t session,
-                           audio_source_t inputSource,
-                           audio_format_t format,
-                           uint32_t sampleRate,
-                           audio_channel_mask_t channelMask,
-                           audio_input_flags_t flags,
-                           uid_t uid,
-                           bool isSoundTrigger) :
-    mRecordClientInfo({ .uid = uid, .session = session, .source = inputSource}),
-    mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}),
-    mFlags(flags), mIsSoundTrigger(isSoundTrigger),
-    mOpenCount(1), mActiveCount(0)
-{
-}
-
-uint32_t AudioSession::changeOpenCount(int delta)
-{
-    if ((delta + (int)mOpenCount) < 0) {
-        ALOGW("%s invalid delta %d, open count %d",
-              __FUNCTION__, delta, mOpenCount);
-        mOpenCount = (uint32_t)(-delta);
-    }
-    mOpenCount += delta;
-    ALOGV("%s open count %d", __FUNCTION__, mOpenCount);
-    return mOpenCount;
-}
-
-uint32_t AudioSession::changeActiveCount(int delta)
-{
-    if ((delta + (int)mActiveCount) < 0) {
-        ALOGW("%s invalid delta %d, active count %d",
-              __FUNCTION__, delta, mActiveCount);
-        mActiveCount = (uint32_t)(-delta);
-    }
-    mActiveCount += delta;
-    ALOGV("%s active count %d", __FUNCTION__, mActiveCount);
-
-    return mActiveCount;
-}
-
-bool AudioSession::matches(const sp<AudioSession> &other) const
-{
-    if (other->session() == mRecordClientInfo.session &&
-        other->inputSource() == mRecordClientInfo.source &&
-        other->format() == mConfig.format &&
-        other->sampleRate() == mConfig.sample_rate &&
-        other->channelMask() == mConfig.channel_mask &&
-        other->flags() == mFlags &&
-        other->uid() == mRecordClientInfo.uid) {
-        return true;
-    }
-    return false;
-}
-
-status_t AudioSession::dump(int fd, int spaces, int index) const
-{
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    String8 result;
-
-    snprintf(buffer, SIZE, "%*sAudio session %d:\n", spaces, "", index+1);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- session: %2d\n", spaces, "", mRecordClientInfo.session);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mRecordClientInfo.uid);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- input source: %d\n", spaces, "", mRecordClientInfo.source);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- format: %08x\n", spaces, "", mConfig.format);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- sample: %d\n", spaces, "", mConfig.sample_rate);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- channel mask: %08x\n",
-             spaces, "", mConfig.channel_mask);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- is soundtrigger: %s\n",
-             spaces, "", mIsSoundTrigger ? "true" : "false");
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- open count: %d\n", spaces, "", mOpenCount);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- active count: %d\n", spaces, "", mActiveCount);
-    result.append(buffer);
-
-    write(fd, result.string(), result.size());
-    return NO_ERROR;
-}
-
-status_t AudioSessionCollection::addSession(audio_session_t session,
-                                         const sp<AudioSession>& audioSession)
-{
-    ssize_t index = indexOfKey(session);
-
-    if (index >= 0) {
-        ALOGW("addSession() session %d already in", session);
-        return ALREADY_EXISTS;
-    }
-    add(session, audioSession);
-    ALOGV("addSession() session %d  client %d source %d",
-            session, audioSession->uid(), audioSession->inputSource());
-    return NO_ERROR;
-}
-
-status_t AudioSessionCollection::removeSession(audio_session_t session)
-{
-    ssize_t index = indexOfKey(session);
-
-    if (index < 0) {
-        ALOGW("removeSession() session %d not in", session);
-        return ALREADY_EXISTS;
-    }
-    ALOGV("removeSession() session %d", session);
-    removeItemsAt(index);
-    return NO_ERROR;
-}
-
-uint32_t AudioSessionCollection::getOpenCount() const
-{
-    uint32_t openCount = 0;
-    for (size_t i = 0; i < size(); i++) {
-        openCount += valueAt(i)->openCount();
-    }
-    return openCount;
-}
-
-AudioSessionCollection AudioSessionCollection::getActiveSessions() const
-{
-    AudioSessionCollection activeSessions;
-    for (size_t i = 0; i < size(); i++) {
-        if (valueAt(i)->activeCount() != 0) {
-            activeSessions.add(valueAt(i)->session(), valueAt(i));
-        }
-    }
-    return activeSessions;
-}
-
-size_t AudioSessionCollection::getActiveSessionCount() const
-{
-    size_t activeCount = 0;
-    for (size_t i = 0; i < size(); i++) {
-        if (valueAt(i)->activeCount() != 0) {
-            activeCount++;
-        }
-    }
-    return activeCount;
-}
-
-bool AudioSessionCollection::hasActiveSession() const
-{
-    return getActiveSessionCount() != 0;
-}
-
-bool AudioSessionCollection::isSourceActive(audio_source_t source) const
-{
-    for (size_t i = 0; i < size(); i++) {
-        const sp<AudioSession>  audioSession = valueAt(i);
-        // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it
-        // corresponds to an active capture triggered by a hardware hotword recognition
-        if (audioSession->activeCount() > 0 &&
-                ((audioSession->inputSource() == source) ||
-                ((source == AUDIO_SOURCE_VOICE_RECOGNITION) &&
-                 (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) &&
-                 audioSession->isSoundTrigger()))) {
-            return true;
-        }
-    }
-    return false;
-}
-
-audio_source_t AudioSessionCollection::getHighestPrioritySource(bool activeOnly) const
-{
-    audio_source_t source = AUDIO_SOURCE_DEFAULT;
-    int32_t priority = -1;
-
-    for (size_t i = 0; i < size(); i++) {
-        const sp<AudioSession>  audioSession = valueAt(i);
-        if (activeOnly && audioSession->activeCount() == 0) {
-            continue;
-        }
-        int32_t curPriority = source_priority(audioSession->inputSource());
-        if (curPriority > priority) {
-            priority = curPriority;
-            source = audioSession->inputSource();
-        }
-    }
-    return source;
-}
-
-status_t AudioSessionCollection::dump(int fd, int spaces) const
-{
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    snprintf(buffer, SIZE, "%*sAudio Sessions:\n", spaces, "");
-    write(fd, buffer, strlen(buffer));
-    for (size_t i = 0; i < size(); i++) {
-        valueAt(i)->dump(fd, spaces + 2, i);
-    }
-    return NO_ERROR;
-}
-
-} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 5aca3cc..0d65a31 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -76,9 +76,11 @@
 
 SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid,
          audio_attributes_t attributes, const sp<AudioPatch>& patchDesc,
-         const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream) :
+         const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream,
+         routing_strategy strategy) :
     TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
-        AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, stream, AUDIO_OUTPUT_FLAG_NONE),
+        AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
+        stream, strategy, AUDIO_OUTPUT_FLAG_NONE),
         mPatchDesc(patchDesc), mSrcDevice(srcDevice)
 {
 }
diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
deleted file mode 100644
index 440a4e7..0000000
--- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2015 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_TAG "APM_SessionRoute"
-//#define LOG_NDEBUG 0
-
-#include "SessionRoute.h"
-#include "HwModule.h"
-#include "AudioGain.h"
-#include "DeviceDescriptor.h"
-#include <utils/Log.h>
-
-namespace android {
-
-// --- SessionRoute class implementation
-void SessionRoute::log(const char* prefix)
-{
-    ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d",
-          prefix, mStreamType, mSource, mSession,
-          mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
-          mRefCount, mActivityCount);
-}
-
-// --- SessionRouteMap class implementation
-bool SessionRouteMap::hasRoute(audio_session_t session)
-{
-    return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
-}
-
-bool SessionRouteMap::getAndClearRouteChanged(audio_session_t session)
-{
-    if (indexOfKey(session) >= 0) {
-        if (valueFor(session)->mChanged) {
-            valueFor(session)->mChanged = false;
-            return true;
-        }
-    }
-    return false;
-}
-
-void SessionRouteMap::removeRoute(audio_session_t session)
-{
-    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
-    if (route != 0) {
-        ALOG_ASSERT(route->mRefCount > 0);
-        --route->mRefCount;
-        if (route->mRefCount <= 0) {
-            removeItem(session);
-        }
-    }
-}
-
-int SessionRouteMap::incRouteActivity(audio_session_t session)
-{
-    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
-    return route != 0 ? ++(route->mActivityCount) : -1;
-}
-
-int SessionRouteMap::decRouteActivity(audio_session_t session)
-{
-    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
-    if (route != 0 && route->mActivityCount > 0) {
-        return --(route->mActivityCount);
-    } else {
-        return -1;
-    }
-}
-
-void SessionRouteMap::log(const char* caption)
-{
-    ALOGI("%s ----", caption);
-    for (size_t index = 0; index < size(); index++) {
-        valueAt(index)->log("  ");
-    }
-}
-
-void SessionRouteMap::addRoute(audio_session_t session,
-                               audio_stream_type_t streamType,
-                               audio_source_t source,
-                               const sp<DeviceDescriptor>& descriptor,
-                               uid_t uid)
-{
-    if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) {
-        ALOGE("Adding Output Route to InputRouteMap");
-        return;
-    } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) {
-        ALOGE("Adding Input Route to OutputRouteMap");
-        return;
-    }
-
-    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
-
-    if (route != 0) {
-        if (descriptor != 0 || route->mDeviceDescriptor != 0) {
-            route->mChanged = true;
-        }
-        route->mRefCount++;
-        route->mDeviceDescriptor = descriptor;
-    } else {
-        route = new SessionRoute(session, streamType, source, descriptor, uid);
-        route->mRefCount++;
-        if (descriptor != 0) {
-            route->mChanged = true;
-        }
-        add(session, route);
-    }
-}
-
-audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType,
-                                                          const DeviceVector& availableDevices)
-{
-    for (size_t index = 0; index < size(); index++) {
-        sp<SessionRoute> route = valueAt(index);
-        if (streamType == route->mStreamType && route->isActiveOrChanged()
-                && route->mDeviceDescriptor != 0) {
-            audio_devices_t device = route->mDeviceDescriptor->type();
-            if (!availableDevices.getDevicesFromTypeMask(device).isEmpty()) {
-                return device;
-            }
-        }
-    }
-    return AUDIO_DEVICE_NONE;
-}
-
-} // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2a8b397..60bce9c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -517,7 +517,7 @@
         // symmetric to the one in startInput()
         for (const auto& activeDesc : mInputs.getActiveInputs()) {
             if (activeDesc->hasSameHwModuleAs(txSourceDeviceDesc)) {
-                closeSessions(activeDesc, true  /*activeOnly*/);
+                closeActiveClients(activeDesc);
             }
         }
     }
@@ -788,9 +788,14 @@
     DeviceVector outputDevices;
     routing_strategy strategy;
     audio_devices_t device;
-    audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+    const audio_port_handle_t requestedDeviceId = *selectedDeviceId;
     audio_devices_t msdDevice = getMsdAudioOutDeviceTypes();
 
+    // The supplied portId must be AUDIO_PORT_HANDLE_NONE
+    if (*portId != AUDIO_PORT_HANDLE_NONE) {
+        return INVALID_OPERATION;
+    }
+
     if (attr != NULL) {
         if (!isValidAttributes(attr)) {
             ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]",
@@ -808,19 +813,19 @@
     }
 
     ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
-            " session %d selectedDeviceId %d",
-            attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
-            session, *selectedDeviceId);
+          " session %d selectedDeviceId %d",
+          attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
+          session, requestedDeviceId);
 
-    // TODO: check for existing client for this port ID
-    if (*portId == AUDIO_PORT_HANDLE_NONE) {
-        *portId = AudioPort::getNextUniqueId();
-    }
+    *stream = streamTypefromAttributesInt(&attributes);
+
+    strategy = getStrategyForAttr(&attributes);
 
     // First check for explicit routing (eg. setPreferredDevice)
-    sp<DeviceDescriptor> deviceDesc;
-    if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
-        deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId);
+    if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+        sp<DeviceDescriptor> deviceDesc =
+            mAvailableOutputDevices.getDeviceFromId(requestedDeviceId);
+        device = deviceDesc->type();
     } else {
         // If no explict route, is there a matching dynamic policy that applies?
         sp<SwAudioOutputDescriptor> desc;
@@ -831,6 +836,10 @@
             }
             *stream = streamTypefromAttributesInt(&attributes);
             *output = desc->mIoHandle;
+            AudioMix *mix = desc->mPolicyMix;
+            sp<DeviceDescriptor> deviceDesc =
+                mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress);
+            *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
             ALOGV("getOutputForAttr() returns output %d", *output);
             goto exit;
         }
@@ -840,25 +849,9 @@
             ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
             return BAD_VALUE;
         }
+        device = getDeviceForStrategy(strategy, false /*fromCache*/);
     }
 
-    // Virtual sources must always be dynamicaly or explicitly routed
-    if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
-        ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
-        return BAD_VALUE;
-    }
-
-    *stream = streamTypefromAttributesInt(&attributes);
-
-    // TODO:  Should this happen only if an explicit route is active?
-    // the previous code structure meant that this would always happen which
-    // would appear to result in adding a null deviceDesc when not using an
-    // explicit route.  Is that the intended and necessary behavior?
-    mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
-
-    strategy = (routing_strategy) getStrategyForAttr(&attributes);
-    device = getDeviceForStrategy(strategy, false /*fromCache*/);
-
     if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
         *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
     }
@@ -871,9 +864,10 @@
         *stream == AUDIO_STREAM_MUSIC &&
         audio_is_linear_pcm(config->format) &&
         isInCall()) {
-        if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+        if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
             *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
         } else {
+            // Get the devce type directly from the engine to bypass preferred route logic
             device = mEngine->getDeviceForStrategy(strategy);
         }
     }
@@ -897,7 +891,6 @@
         *output = getOutputForDevice(device, session, *stream, config, flags);
     }
     if (*output == AUDIO_IO_HANDLE_NONE) {
-        mOutputRoutes.removeRoute(session);
         return INVALID_OPERATION;
     }
 
@@ -909,11 +902,15 @@
     audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
         .format = config->format,
         .channel_mask = config->channel_mask };
+    *portId = AudioPort::getNextUniqueId();
+
     sp<TrackClientDescriptor> clientDesc =
-        new TrackClientDescriptor(*portId, uid, session,
-                                  attributes, clientConfig, requestedDeviceId, *stream, *flags);
+        new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig,
+                                  requestedDeviceId, *stream,
+                                  getStrategyForAttr(&attributes),
+                                  *flags);
     sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
-    outputDesc->clients().emplace(*portId, clientDesc);
+    outputDesc->clientsMap().emplace(*portId, clientDesc);
 
     ALOGV("  getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d",
           *output, *selectedDeviceId, *portId);
@@ -1037,8 +1034,6 @@
             }
             return AUDIO_IO_HANDLE_NONE;
         }
-        outputDesc->mRefCount[stream] = 0;
-        outputDesc->mStopTime[stream] = 0;
         outputDesc->mDirectOpenCount = 1;
         outputDesc->mDirectClientSession = session;
 
@@ -1325,60 +1320,23 @@
         ALOGW("startOutput() no output for client %d", portId);
         return BAD_VALUE;
     }
-    sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
-    audio_stream_type_t stream = client->stream();
-    audio_session_t session = client->session();
+    sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
 
     ALOGV("startOutput() output %d, stream %d, session %d",
-          outputDesc->mIoHandle, stream, session);
+          outputDesc->mIoHandle, client->stream(), client->session());
 
     status_t status = outputDesc->start();
     if (status != NO_ERROR) {
         return status;
     }
 
-    // Routing?
-    mOutputRoutes.incRouteActivity(session);
-
-    audio_devices_t newDevice;
-    AudioMix *policyMix = NULL;
-    const char *address = NULL;
-    if (outputDesc->mPolicyMix != NULL) {
-        policyMix = outputDesc->mPolicyMix;
-        address = policyMix->mDeviceAddress.string();
-        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
-            newDevice = policyMix->mDeviceType;
-        } else {
-            newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
-        }
-    } else if (mOutputRoutes.getAndClearRouteChanged(session)) {
-        newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
-        if (newDevice != outputDesc->device()) {
-            checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle);
-        }
-    } else {
-        newDevice = AUDIO_DEVICE_NONE;
-    }
-
-    uint32_t delayMs = 0;
-
-    status = startSource(outputDesc, stream, newDevice, address, &delayMs);
+    uint32_t delayMs;
+    status = startSource(outputDesc, client, &delayMs);
 
     if (status != NO_ERROR) {
-        mOutputRoutes.decRouteActivity(session);
         outputDesc->stop();
         return status;
     }
-    // Automatically enable the remote submix input when output is started on a re routing mix
-    // of type MIX_TYPE_RECORDERS
-    if (audio_is_remote_submix_device(newDevice) && policyMix != NULL &&
-            policyMix->mMixType == MIX_TYPE_RECORDERS) {
-            setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                    address,
-                    "remote-submix");
-    }
-
     if (delayMs != 0) {
         usleep(delayMs * 1000);
     }
@@ -1386,16 +1344,15 @@
     return status;
 }
 
-status_t AudioPolicyManager::startSource(const sp<AudioOutputDescriptor>& outputDesc,
-                                             audio_stream_type_t stream,
-                                             audio_devices_t device,
-                                             const char *address,
-                                             uint32_t *delayMs)
+status_t AudioPolicyManager::startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+                                         const sp<TrackClientDescriptor>& client,
+                                         uint32_t *delayMs)
 {
     // cannot start playback of STREAM_TTS if any other output is being used
     uint32_t beaconMuteLatency = 0;
 
     *delayMs = 0;
+    audio_stream_type_t stream = client->stream();
     if (stream == AUDIO_STREAM_TTS) {
         ALOGV("\t found BEACON stream");
         if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -1413,6 +1370,19 @@
     bool force = !outputDesc->isActive() &&
             (outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE);
 
+    audio_devices_t device = AUDIO_DEVICE_NONE;
+    AudioMix *policyMix = NULL;
+    const char *address = NULL;
+    if (outputDesc->mPolicyMix != NULL) {
+        policyMix = outputDesc->mPolicyMix;
+        address = policyMix->mDeviceAddress.string();
+        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+            device = policyMix->mDeviceType;
+        } else {
+            device = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+        }
+    }
+
     // requiresMuteCheck is false when we can bypass mute strategy.
     // It covers a common case when there is no materially active audio
     // and muting would result in unnecessary delay and dropped audio.
@@ -1422,13 +1392,20 @@
     // increment usage count for this stream on the requested output:
     // NOTE that the usage count is the same for duplicated output and hardware output which is
     // necessary for a correct control of hardware output routing by startOutput() and stopOutput()
-    outputDesc->changeRefCount(stream, 1);
+    outputDesc->setClientActive(client, true);
+
+    if (client->hasPreferredDevice(true)) {
+        device = getNewOutputDevice(outputDesc, false /*fromCache*/);
+        if (device != outputDesc->device()) {
+            checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle);
+        }
+    }
 
     if (stream == AUDIO_STREAM_MUSIC) {
         selectOutputForMusicEffects();
     }
 
-    if (outputDesc->mRefCount[stream] == 1 || device != AUDIO_DEVICE_NONE) {
+    if (outputDesc->streamActiveCount(stream) == 1 || device != AUDIO_DEVICE_NONE) {
         // starting an output being rerouted?
         if (device == AUDIO_DEVICE_NONE) {
             device = getNewOutputDevice(outputDesc, false /*fromCache*/);
@@ -1514,6 +1491,16 @@
         setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc);
     }
 
+    // Automatically enable the remote submix input when output is started on a re routing mix
+    // of type MIX_TYPE_RECORDERS
+    if (audio_is_remote_submix_device(device) && policyMix != NULL &&
+        policyMix->mMixType == MIX_TYPE_RECORDERS) {
+        setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                                    address,
+                                    "remote-submix");
+    }
+
     return NO_ERROR;
 }
 
@@ -1526,37 +1513,12 @@
         ALOGW("stopOutput() no output for client %d", portId);
         return BAD_VALUE;
     }
-    sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
-    audio_stream_type_t stream = client->stream();
-    audio_session_t session = client->session();
+    sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
 
-    ALOGV("stopOutput() output %d, stream %d, session %d", outputDesc->mIoHandle, stream, session);
+    ALOGV("stopOutput() output %d, stream %d, session %d",
+          outputDesc->mIoHandle, client->stream(), client->session());
 
-    if (outputDesc->mRefCount[stream] == 1) {
-        // Automatically disable the remote submix input when output is stopped on a
-        // re routing mix of type MIX_TYPE_RECORDERS
-        if (audio_is_remote_submix_device(outputDesc->mDevice) &&
-                outputDesc->mPolicyMix != NULL &&
-                outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
-            setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
-                    outputDesc->mPolicyMix->mDeviceAddress,
-                    "remote-submix");
-        }
-    }
-
-    // Routing?
-    bool forceDeviceUpdate = false;
-    if (outputDesc->mRefCount[stream] > 0) {
-        int activityCount = mOutputRoutes.decRouteActivity(session);
-        forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0));
-
-        if (forceDeviceUpdate) {
-            checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
-        }
-    }
-
-    status_t status = stopSource(outputDesc, stream, forceDeviceUpdate);
+    status_t status = stopSource(outputDesc, client);
 
     if (status == NO_ERROR ) {
         outputDesc->stop();
@@ -1564,19 +1526,38 @@
     return status;
 }
 
-status_t AudioPolicyManager::stopSource(const sp<AudioOutputDescriptor>& outputDesc,
-                                            audio_stream_type_t stream,
-                                            bool forceDeviceUpdate)
+status_t AudioPolicyManager::stopSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+                                        const sp<TrackClientDescriptor>& client)
 {
     // always handle stream stop, check which stream type is stopping
+    audio_stream_type_t stream = client->stream();
+
     handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
 
-    if (outputDesc->mRefCount[stream] > 0) {
+    if (outputDesc->streamActiveCount(stream) > 0) {
+        if (outputDesc->streamActiveCount(stream) == 1) {
+            // Automatically disable the remote submix input when output is stopped on a
+            // re routing mix of type MIX_TYPE_RECORDERS
+            if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+                outputDesc->mPolicyMix != NULL &&
+                outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                                            AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                            outputDesc->mPolicyMix->mDeviceAddress,
+                                            "remote-submix");
+            }
+        }
+        bool forceDeviceUpdate = false;
+        if (client->hasPreferredDevice(true)) {
+            checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
+            forceDeviceUpdate = true;
+        }
+
         // decrement usage count of this stream on the output
-        outputDesc->changeRefCount(stream, -1);
+        outputDesc->setClientActive(client, false);
 
         // store time at which the stream was stopped - see isStreamActive()
-        if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) {
+        if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) {
             outputDesc->mStopTime[stream] = systemTime();
             audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
             // delay the device switch by twice the latency because stopOutput() is executed when
@@ -1636,14 +1617,10 @@
         ALOGW("releaseOutput() no output for client %d", portId);
         return;
     }
-    sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
-    audio_session_t session = client->session();
+    sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
 
     ALOGV("releaseOutput() %d", outputDesc->mIoHandle);
 
-    // Routing
-    mOutputRoutes.removeRoute(session);
-
     if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
         if (outputDesc->mDirectOpenCount <= 0) {
             ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1655,7 +1632,7 @@
             mpClientInterface->onAudioPortListUpdate();
         }
     }
-    outputDesc->clients().erase(portId);
+    outputDesc->clientsMap().erase(portId);
 }
 
 
@@ -1683,6 +1660,13 @@
     sp<AudioInputDescriptor> inputDesc;
     sp<RecordClientDescriptor> clientDesc;
     audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+    bool isSoundTrigger;
+    audio_devices_t device;
+
+    // The supplied portId must be AUDIO_PORT_HANDLE_NONE
+    if (*portId != AUDIO_PORT_HANDLE_NONE) {
+        return INVALID_OPERATION;
+    }
 
     if (inputSource == AUDIO_SOURCE_DEFAULT) {
         inputSource = AUDIO_SOURCE_MIC;
@@ -1693,7 +1677,6 @@
     if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
         deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId);
     }
-    mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
 
     // special case for mmap capture: if an input IO handle is specified, we reuse this input if
     // possible
@@ -1706,8 +1689,8 @@
             goto error;
         }
         sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
-        sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
-        if (audioSession == 0) {
+        RecordClientVector clients = inputDesc->getClientsForSession(session);
+        if (clients.size() == 0) {
             ALOGW("getInputForAttr() unknown session %d on input %d", session, *input);
             status = BAD_VALUE;
             goto error;
@@ -1716,28 +1699,27 @@
         // The second call is for the first active client and sets the UID. Any further call
         // corresponds to a new client and is only permitted from the same UID.
         // If the first UID is silenced, allow a new UID connection and replace with new UID
-        if (audioSession->openCount() == 1) {
-            audioSession->setUid(uid);
-        } else if (audioSession->uid() != uid) {
-            if (!audioSession->isSilenced()) {
-                ALOGW("getInputForAttr() bad uid %d for session %d uid %d",
-                      uid, session, audioSession->uid());
-                status = INVALID_OPERATION;
-                goto error;
+        if (clients.size() > 1) {
+            for (const auto& client : clients) {
+                // The client map is ordered by key values (portId) and portIds are allocated
+                // incrementaly. So the first client in this list is the one opened by audio flinger
+                // when the mmap stream is created and should be ignored as it does not correspond
+                // to an actual client
+                if (client == *clients.cbegin()) {
+                    continue;
+                }
+                if (uid != client->uid() && !client->isSilenced()) {
+                    ALOGW("getInputForAttr() bad uid %d for client %d uid %d",
+                          uid, client->portId(), client->uid());
+                    status = INVALID_OPERATION;
+                    goto error;
+                }
             }
-            audioSession->setUid(uid);
-            audioSession->setSilenced(false);
         }
-        audioSession->changeOpenCount(1);
         *inputType = API_INPUT_LEGACY;
-        if (*portId == AUDIO_PORT_HANDLE_NONE) {
-            *portId = AudioPort::getNextUniqueId();
-        }
-        inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(inputDesc->mDevice);
-        *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
-                : AUDIO_PORT_HANDLE_NONE;
-        ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
+        device = inputDesc->mDevice;
 
+        ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
         goto exit;
     }
 
@@ -1746,13 +1728,6 @@
 
     halInputSource = inputSource;
 
-    // TODO: check for existing client for this port ID
-    if (*portId == AUDIO_PORT_HANDLE_NONE) {
-        *portId = AudioPort::getNextUniqueId();
-    }
-
-    audio_devices_t device;
-
     if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
             strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
         status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
@@ -1763,7 +1738,11 @@
         device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
         address = String8(attr->tags + strlen("addr="));
     } else {
-        device = getDeviceAndMixForInputSource(inputSource, &policyMix);
+        if (deviceDesc != 0) {
+            device = deviceDesc->type();
+        } else {
+            device = getDeviceAndMixForInputSource(inputSource, &policyMix);
+        }
         if (device == AUDIO_DEVICE_NONE) {
             ALOGW("getInputForAttr() could not find device for source %d", inputSource);
             status = BAD_VALUE;
@@ -1792,7 +1771,7 @@
 
     }
 
-    *input = getInputForDevice(device, address, session, uid, inputSource,
+    *input = getInputForDevice(device, address, session, inputSource,
                                config, flags,
                                policyMix);
     if (*input == AUDIO_IO_HANDLE_NONE) {
@@ -1800,15 +1779,21 @@
         goto error;
     }
 
+exit:
+
     inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device);
     *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
-            : AUDIO_PORT_HANDLE_NONE;
+                                                : AUDIO_PORT_HANDLE_NONE;
 
-exit:
+    isSoundTrigger = inputSource == AUDIO_SOURCE_HOTWORD &&
+        mSoundTriggerSessions.indexOfKey(session) > 0;
+    *portId = AudioPort::getNextUniqueId();
+
     clientDesc = new RecordClientDescriptor(*portId, uid, session,
-                                  *attr, *config, requestedDeviceId, inputSource, flags);
+                                  *attr, *config, requestedDeviceId,
+                                  inputSource,flags, isSoundTrigger);
     inputDesc = mInputs.valueFor(*input);
-    inputDesc->clients().emplace(*portId, clientDesc);
+    inputDesc->clientsMap().emplace(*portId, clientDesc);
 
     ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d",
             *input, *inputType, *selectedDeviceId, *portId);
@@ -1816,7 +1801,6 @@
     return NO_ERROR;
 
 error:
-    mInputRoutes.removeRoute(session);
     return status;
 }
 
@@ -1824,7 +1808,6 @@
 audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device,
                                                         String8 address,
                                                         audio_session_t session,
-                                                        uid_t uid,
                                                         audio_source_t inputSource,
                                                         const audio_config_base_t *config,
                                                         audio_input_flags_t flags,
@@ -1886,15 +1869,6 @@
         return input;
     }
 
-    sp<AudioSession> audioSession = new AudioSession(session,
-                                                     inputSource,
-                                                     config->format,
-                                                     samplingRate,
-                                                     config->channel_mask,
-                                                     flags,
-                                                     uid,
-                                                     isSoundTrigger);
-
     if (!profile->canOpenNewIo()) {
         return AUDIO_IO_HANDLE_NONE;
     }
@@ -1930,7 +1904,6 @@
     }
 
     inputDesc->mPolicyMix = policyMix;
-    inputDesc->addAudioSession(session, audioSession);
 
     addInput(input, inputDesc);
     mpClientInterface->onAudioPortListUpdate();
@@ -1946,39 +1919,6 @@
             (source == AUDIO_SOURCE_FM_TUNER);
 }
 
-bool AudioPolicyManager::isConcurentCaptureAllowed(const sp<AudioInputDescriptor>& inputDesc,
-        const sp<AudioSession>& audioSession)
-{
-    // Do not allow capture if an active voice call is using a software patch and
-    // the call TX source device is on the same HW module.
-    // FIXME: would be better to refine to only inputs whose profile connects to the
-    // call TX device but this information is not in the audio patch
-    if (mCallTxPatch != 0 &&
-        inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) {
-        return false;
-    }
-
-    // starting concurrent capture is enabled if:
-    // 1) capturing for re-routing
-    // 2) capturing for HOTWORD source
-    // 3) capturing for FM TUNER source
-    // 3) All other active captures are either for re-routing or HOTWORD
-
-    if (is_virtual_input_device(inputDesc->mDevice) ||
-            isConcurrentSource(audioSession->inputSource())) {
-        return true;
-    }
-
-    for (const auto& activeInput : mInputs.getActiveInputs()) {
-        if (!isConcurrentSource(activeInput->inputSource(true)) &&
-                !is_virtual_input_device(activeInput->mDevice)) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
 // FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537.
 bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() {
     if (!mHasComputedSoundTriggerSupportsConcurrentCapture) {
@@ -2024,22 +1964,21 @@
 
     sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
     if (inputDesc == 0) {
-        ALOGW("startInput() no input for client %d", portId);
+        ALOGW("%s no input for client %d", __FUNCTION__, portId);
         return BAD_VALUE;
     }
-    sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
-    audio_session_t session = client->session();
     audio_io_handle_t input = inputDesc->mIoHandle;
-
-    ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)",
-            input, session, silenced, *concurrency);
-
-    sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
-    if (audioSession == 0) {
-        ALOGW("startInput() unknown session %d on input %d", session, input);
-        return BAD_VALUE;
+    sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
+    if (client->active()) {
+        ALOGW("%s input %d client %d already started", __FUNCTION__, input, client->portId());
+        return INVALID_OPERATION;
     }
 
+    audio_session_t session = client->session();
+
+    ALOGV("%s input:%d, session:%d, silenced:%d, concurrency:%d)",
+        __FUNCTION__, input, session, silenced, *concurrency);
+
     if (!is_virtual_input_device(inputDesc->mDevice)) {
         if (mCallTxPatch != 0 &&
             inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) {
@@ -2055,47 +1994,48 @@
         // favor of the new one - "There can be only one" TM
         if (!silenced) {
             for (const auto& activeDesc : activeInputs) {
-                if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+                if ((activeDesc->getAudioPort()->getFlags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
                         activeDesc->getId() == inputDesc->getId()) {
                      continue;
                 }
 
-                AudioSessionCollection activeSessions = activeDesc->getAudioSessions(
-                        true /*activeOnly*/);
-                sp<AudioSession> activeSession = activeSessions.valueAt(0);
-                if (activeSession->isSilenced()) {
-                    closeSession(activeDesc, activeSession);
-                    ALOGV("startInput() session %d stopping silenced session %d", session, activeSession->session());
-                    activeInputs = mInputs.getActiveInputs();
+                RecordClientVector activeClients = activeDesc->clientsList(true /*activeOnly*/);
+                for (const auto& activeClient : activeClients) {
+                    if (activeClient->isSilenced()) {
+                        closeClient(activeClient->portId());
+                        ALOGV("%s client %d stopping silenced client %d", __FUNCTION__,
+                              portId, activeClient->portId());
+                        activeInputs = mInputs.getActiveInputs();
+                    }
                 }
             }
         }
 
         for (const auto& activeDesc : activeInputs) {
-            if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+            if ((client->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
                     activeDesc->getId() == inputDesc->getId()) {
                 continue;
             }
 
             audio_source_t activeSource = activeDesc->inputSource(true);
-            if (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) {
+            if (client->source() == AUDIO_SOURCE_HOTWORD) {
                 if (activeSource == AUDIO_SOURCE_HOTWORD) {
                     if (activeDesc->hasPreemptedSession(session)) {
-                        ALOGW("startInput(%d) failed for HOTWORD: "
-                                "other input %d already started for HOTWORD",
+                        ALOGW("%s input %d failed for HOTWORD: "
+                                "other input %d already started for HOTWORD", __FUNCTION__,
                               input, activeDesc->mIoHandle);
                         *concurrency |= API_INPUT_CONCURRENCY_HOTWORD;
                         return INVALID_OPERATION;
                     }
                 } else {
-                    ALOGV("startInput(%d) failed for HOTWORD: other input %d already started",
-                          input, activeDesc->mIoHandle);
+                    ALOGV("%s input %d failed for HOTWORD: other input %d already started",
+                        __FUNCTION__, input, activeDesc->mIoHandle);
                     *concurrency |= API_INPUT_CONCURRENCY_CAPTURE;
                     return INVALID_OPERATION;
                 }
             } else {
                 if (activeSource != AUDIO_SOURCE_HOTWORD) {
-                    ALOGW("startInput(%d) failed: other input %d already started",
+                    ALOGW("%s input %d failed: other input %d already started", __FUNCTION__,
                           input, activeDesc->mIoHandle);
                     *concurrency |= API_INPUT_CONCURRENCY_CAPTURE;
                     return INVALID_OPERATION;
@@ -2114,80 +2054,75 @@
             if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) {
                 continue;
             }
-
-            audio_source_t activeSource = activeDesc->inputSource(true);
-            if (activeSource == AUDIO_SOURCE_HOTWORD) {
-                AudioSessionCollection activeSessions =
-                        activeDesc->getAudioSessions(true /*activeOnly*/);
-                sp<AudioSession> activeSession = activeSessions[0];
+            RecordClientVector activeHotwordClients =
+                activeDesc->clientsList(true, AUDIO_SOURCE_HOTWORD);
+            if (activeHotwordClients.size() > 0) {
                 SortedVector<audio_session_t> sessions = activeDesc->getPreemptedSessions();
-                *concurrency |= API_INPUT_CONCURRENCY_PREEMPT;
-                sessions.add(activeSession->session());
+
+                for (const auto& activeClient : activeHotwordClients) {
+                    *concurrency |= API_INPUT_CONCURRENCY_PREEMPT;
+                    sessions.add(activeClient->session());
+                    closeClient(activeClient->portId());
+                    ALOGV("%s input %d for HOTWORD preempting HOTWORD input %d", __FUNCTION__,
+                          input, activeDesc->mIoHandle);
+                }
+
                 inputDesc->setPreemptedSessions(sessions);
-                closeSession(inputDesc, activeSession);
-                ALOGV("startInput(%d) for HOTWORD preempting HOTWORD input %d",
-                      input, activeDesc->mIoHandle);
             }
         }
     }
 
     // Make sure we start with the correct silence state
-    audioSession->setSilenced(silenced);
+    client->setSilenced(silenced);
 
     // increment activity count before calling getNewInputDevice() below as only active sessions
     // are considered for device selection
-    inputDesc->changeRefCount(session, 1);
+    inputDesc->setClientActive(client, true);
 
-    // Routing?
-    mInputRoutes.incRouteActivity(session);
+    // indicate active capture to sound trigger service if starting capture from a mic on
+    // primary HW module
+    audio_devices_t device = getNewInputDevice(inputDesc);
+    setInputDevice(input, device, true /* force */);
 
-    if (audioSession->activeCount() == 1 || mInputRoutes.getAndClearRouteChanged(session)) {
-        // indicate active capture to sound trigger service if starting capture from a mic on
-        // primary HW module
-        audio_devices_t device = getNewInputDevice(inputDesc);
-        setInputDevice(input, device, true /* force */);
+    status_t status = inputDesc->start();
+    if (status != NO_ERROR) {
+        inputDesc->setClientActive(client, false);
+        return status;
+    }
 
-        status_t status = inputDesc->start();
-        if (status != NO_ERROR) {
-            mInputRoutes.decRouteActivity(session);
-            inputDesc->changeRefCount(session, -1);
-            return status;
+    if (inputDesc->activeCount()  == 1) {
+        // if input maps to a dynamic policy with an activity listener, notify of state change
+        if ((inputDesc->mPolicyMix != NULL)
+                && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+            mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
+                    MIX_STATE_MIXING);
         }
 
-        if (inputDesc->getAudioSessionCount(true/*activeOnly*/) == 1) {
-            // if input maps to a dynamic policy with an activity listener, notify of state change
-            if ((inputDesc->mPolicyMix != NULL)
-                    && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
-                mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
-                        MIX_STATE_MIXING);
-            }
+        audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
+        if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
+                mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) {
+            SoundTrigger::setCaptureState(true);
+        }
 
-            audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
-            if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
-                    mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) {
-                SoundTrigger::setCaptureState(true);
+        // automatically enable the remote submix output when input is started if not
+        // used by a policy mix of type MIX_TYPE_RECORDERS
+        // For remote submix (a virtual device), we open only one input per capture request.
+        if (audio_is_remote_submix_device(inputDesc->mDevice)) {
+            String8 address = String8("");
+            if (inputDesc->mPolicyMix == NULL) {
+                address = String8("0");
+            } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
+                address = inputDesc->mPolicyMix->mDeviceAddress;
             }
-
-            // automatically enable the remote submix output when input is started if not
-            // used by a policy mix of type MIX_TYPE_RECORDERS
-            // For remote submix (a virtual device), we open only one input per capture request.
-            if (audio_is_remote_submix_device(inputDesc->mDevice)) {
-                String8 address = String8("");
-                if (inputDesc->mPolicyMix == NULL) {
-                    address = String8("0");
-                } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
-                    address = inputDesc->mPolicyMix->mDeviceAddress;
-                }
-                if (address != "") {
-                    setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
-                            AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                            address, "remote-submix");
-                }
+            if (address != "") {
+                setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                        address, "remote-submix");
             }
         }
     }
 
-    ALOGV("AudioPolicyManager::startInput() input source = %d", audioSession->inputSource());
+    ALOGV("%s input %d source = %d exit", __FUNCTION__, input, client->source());
 
     return NO_ERROR;
 }
@@ -2198,67 +2133,56 @@
 
     sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
     if (inputDesc == 0) {
-        ALOGW("stopInput() no input for client %d", portId);
+        ALOGW("%s no input for client %d", __FUNCTION__, portId);
         return BAD_VALUE;
     }
-    sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
-    audio_session_t session = client->session();
     audio_io_handle_t input = inputDesc->mIoHandle;
-
-    ALOGV("stopInput() input %d", input);
-
-    sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
-
-    if (audioSession->activeCount() == 0) {
-        ALOGW("stopInput() input %d already stopped", input);
+    sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
+    if (!client->active()) {
+        ALOGW("%s input %d client %d already stopped", __FUNCTION__, input, client->portId());
         return INVALID_OPERATION;
     }
 
-    inputDesc->changeRefCount(session, -1);
+    inputDesc->setClientActive(client, false);
 
-    // Routing?
-    mInputRoutes.decRouteActivity(session);
-
-    if (audioSession->activeCount() == 0) {
-        inputDesc->stop();
-        if (inputDesc->isActive()) {
-            setInputDevice(input, getNewInputDevice(inputDesc), false /* force */);
-        } else {
-            // if input maps to a dynamic policy with an activity listener, notify of state change
-            if ((inputDesc->mPolicyMix != NULL)
-                    && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
-                mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
-                        MIX_STATE_IDLE);
-            }
-
-            // automatically disable the remote submix output when input is stopped if not
-            // used by a policy mix of type MIX_TYPE_RECORDERS
-            if (audio_is_remote_submix_device(inputDesc->mDevice)) {
-                String8 address = String8("");
-                if (inputDesc->mPolicyMix == NULL) {
-                    address = String8("0");
-                } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
-                    address = inputDesc->mPolicyMix->mDeviceAddress;
-                }
-                if (address != "") {
-                    setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
-                                             AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
-                                             address, "remote-submix");
-                }
-            }
-
-            audio_devices_t device = inputDesc->mDevice;
-            resetInputDevice(input);
-
-            // indicate inactive capture to sound trigger service if stopping capture from a mic on
-            // primary HW module
-            audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
-            if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
-                    mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
-                SoundTrigger::setCaptureState(false);
-            }
-            inputDesc->clearPreemptedSessions();
+    inputDesc->stop();
+    if (inputDesc->isActive()) {
+        setInputDevice(input, getNewInputDevice(inputDesc), false /* force */);
+    } else {
+        // if input maps to a dynamic policy with an activity listener, notify of state change
+        if ((inputDesc->mPolicyMix != NULL)
+                && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+            mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
+                    MIX_STATE_IDLE);
         }
+
+        // automatically disable the remote submix output when input is stopped if not
+        // used by a policy mix of type MIX_TYPE_RECORDERS
+        if (audio_is_remote_submix_device(inputDesc->mDevice)) {
+            String8 address = String8("");
+            if (inputDesc->mPolicyMix == NULL) {
+                address = String8("0");
+            } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
+                address = inputDesc->mPolicyMix->mDeviceAddress;
+            }
+            if (address != "") {
+                setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+                                         AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                         address, "remote-submix");
+            }
+        }
+
+        audio_devices_t device = inputDesc->mDevice;
+        resetInputDevice(input);
+
+        // indicate inactive capture to sound trigger service if stopping capture from a mic on
+        // primary HW module
+        audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
+        if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
+                mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
+            SoundTrigger::setCaptureState(false);
+        }
+        inputDesc->clearPreemptedSessions();
     }
     return NO_ERROR;
 }
@@ -2269,64 +2193,40 @@
 
     sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
     if (inputDesc == 0) {
-        ALOGW("releaseInput() no input for client %d", portId);
+        ALOGW("%s no input for client %d", __FUNCTION__, portId);
         return;
     }
-    sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
-    audio_session_t session = client->session();
+    sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
     audio_io_handle_t input = inputDesc->mIoHandle;
 
-    ALOGV("releaseInput() %d", input);
+    ALOGV("%s %d", __FUNCTION__, input);
 
-    // Routing
-    mInputRoutes.removeRoute(session);
+    inputDesc->clientsMap().erase(portId);
 
-    sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
-    if (audioSession == 0) {
-        ALOGW("releaseInput() unknown session %d on input %d", session, input);
-        return;
-    }
-
-    if (audioSession->openCount() == 0) {
-        ALOGW("releaseInput() invalid open count %d on session %d",
-              audioSession->openCount(), session);
-        return;
-    }
-
-    if (audioSession->changeOpenCount(-1) == 0) {
-        inputDesc->removeAudioSession(session);
-    }
-
-    if (inputDesc->getOpenRefCount() > 0) {
-        ALOGV("releaseInput() exit > 0");
+    if (inputDesc->clientsMap().size() > 0) {
+        ALOGV("%s %zu clients remaining", __FUNCTION__, inputDesc->clientsMap().size());
         return;
     }
 
     closeInput(input);
-    inputDesc->clients().erase(portId);
     mpClientInterface->onAudioPortListUpdate();
-    ALOGV("releaseInput() exit");
+    ALOGV("%s exit", __FUNCTION__);
 }
 
-void AudioPolicyManager::closeSessions(const sp<AudioInputDescriptor>& input, bool activeOnly)
+void AudioPolicyManager::closeActiveClients(const sp<AudioInputDescriptor>& input)
 {
-    AudioSessionCollection sessions = input->getAudioSessions(activeOnly /*activeOnly*/);
-    for (size_t i = 0; i < sessions.size(); i++) {
-        closeSession(input, sessions[i]);
-    }
-}
-
-void AudioPolicyManager::closeSession(const sp<AudioInputDescriptor>& input,
-                                      const sp<AudioSession>& session)
-{
-    RecordClientVector clients = input->getClientsForSession(session->session());
+    RecordClientVector clients = input->clientsList(true);
 
     for (const auto& client : clients) {
-        stopInput(client->portId());
-        releaseInput(client->portId());
+        closeClient(client->portId());
     }
 }
 
+void AudioPolicyManager::closeClient(audio_port_handle_t portId)
+{
+    stopInput(portId);
+    releaseInput(portId);
+}
 
 void AudioPolicyManager::closeAllInputs() {
     bool patchRemoved = false;
@@ -2342,7 +2242,6 @@
         }
         inputDesc->close();
     }
-    mInputRoutes.clear();
     mInputs.clear();
     SoundTrigger::setCaptureState(false);
     nextAudioPortGeneration();
@@ -2619,12 +2518,13 @@
     sp<HwModule> rSubmixModule;
     // examine each mix's route type
     for (size_t i = 0; i < mixes.size(); i++) {
+        AudioMix mix = mixes[i];
         // we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination
-        if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
+        if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
             res = INVALID_OPERATION;
             break;
         }
-        if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
+        if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
             ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size());
             if (rSubmixModule == 0) {
                 rSubmixModule = mHwModules.getModuleFromName(
@@ -2637,15 +2537,20 @@
                 }
             }
 
-            String8 address = mixes[i].mDeviceAddress;
+            String8 address = mix.mDeviceAddress;
+            if (mix.mMixType == MIX_TYPE_PLAYERS) {
+                mix.mDeviceType = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+            } else {
+                mix.mDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+            }
 
-            if (mPolicyMixes.registerMix(address, mixes[i], 0 /*output desc*/) != NO_ERROR) {
+            if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
                 ALOGE(" Error registering mix %zu for address %s", i, address.string());
                 res = INVALID_OPERATION;
                 break;
             }
-            audio_config_t outputConfig = mixes[i].mFormat;
-            audio_config_t inputConfig = mixes[i].mFormat;
+            audio_config_t outputConfig = mix.mFormat;
+            audio_config_t inputConfig = mix.mFormat;
             // NOTE: audio flinger mixer does not support mono output: configure remote submix HAL in
             // stereo and let audio flinger do the channel conversion if needed.
             outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
@@ -2655,7 +2560,7 @@
             rSubmixModule->addInputProfile(address, &inputConfig,
                     AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
 
-            if (mixes[i].mMixType == MIX_TYPE_PLAYERS) {
+            if (mix.mMixType == MIX_TYPE_PLAYERS) {
                 setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
                         AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
                         address.string(), "remote-submix");
@@ -2664,9 +2569,9 @@
                         AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
                         address.string(), "remote-submix");
             }
-        } else if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
-            String8 address = mixes[i].mDeviceAddress;
-            audio_devices_t device = mixes[i].mDeviceType;
+        } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+            String8 address = mix.mDeviceAddress;
+            audio_devices_t device = mix.mDeviceType;
             ALOGV(" registerPolicyMixes() mix %zu of %zu is RENDER, dev=0x%X addr=%s",
                     i, mixes.size(), device, address.string());
 
@@ -2679,7 +2584,7 @@
                         && (patch->mPatch.sinks[0].ext.device.type == device)
                         && (strncmp(patch->mPatch.sinks[0].ext.device.address, address.string(),
                                 AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
-                    if (mPolicyMixes.registerMix(address, mixes[i], desc) != NO_ERROR) {
+                    if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
                         res = INVALID_OPERATION;
                     } else {
                         foundOutput = true;
@@ -2811,12 +2716,9 @@
     }
 
     // Check if offload has been disabled
-    char propValue[PROPERTY_VALUE_MAX];
-    if (property_get("audio.offload.disable", propValue, "0")) {
-        if (atoi(propValue) != 0) {
-            ALOGV("offload disabled by audio.offload.disable=%s", propValue );
-            return false;
-        }
+    if (property_get_bool("audio.offload.disable", false /* default_value */)) {
+        ALOGV("offload disabled by audio.offload.disable");
+        return false;
     }
 
     // Check if stream type is music, then only allow offload as of now.
@@ -2835,9 +2737,12 @@
     }
 
     //If duration is less than minimum value defined in property, return false
-    if (property_get("audio.offload.min.duration.secs", propValue, NULL)) {
-        if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) {
-            ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue);
+    const int min_duration_secs = property_get_int32(
+            "audio.offload.min.duration.secs", -1 /* default_value */);
+    if (min_duration_secs >= 0) {
+        if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
+            ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%d)",
+                    min_duration_secs);
             return false;
         }
     } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
@@ -3391,12 +3296,13 @@
 {
     // remove output routes associated with this uid
     SortedVector<routing_strategy> affectedStrategies;
-    for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--)  {
-        sp<SessionRoute> route = mOutputRoutes.valueAt(i);
-        if (route->mUid == uid) {
-            mOutputRoutes.removeItemsAt(i);
-            if (route->mDeviceDescriptor != 0) {
-                affectedStrategies.add(getStrategy(route->mStreamType));
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+        TrackClientMap clients = outputDesc->clientsMap();
+        for (const auto& client : clients) {
+            if (client.second->hasPreferredDevice() && client.second->uid() == uid) {
+                client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+                affectedStrategies.add(getStrategy(client.second->stream()));
             }
         }
     }
@@ -3407,12 +3313,13 @@
 
     // remove input routes associated with this uid
     SortedVector<audio_source_t> affectedSources;
-    for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--)  {
-        sp<SessionRoute> route = mInputRoutes.valueAt(i);
-        if (route->mUid == uid) {
-            mInputRoutes.removeItemsAt(i);
-            if (route->mDeviceDescriptor != 0) {
-                affectedSources.add(route->mSource);
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
+        RecordClientMap clients = inputDesc->clientsMap();
+        for (const auto& client : clients) {
+            if (client.second->hasPreferredDevice() && client.second->uid() == uid) {
+                client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+                affectedSources.add(client.second->source());
             }
         }
     }
@@ -3486,7 +3393,8 @@
 
     sp<SourceClientDescriptor> sourceDesc =
         new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDeviceDesc,
-                                   streamTypefromAttributesInt(attributes));
+                                   streamTypefromAttributesInt(attributes),
+                                   getStrategyForAttr(attributes));
 
     status_t status = connectAudioSource(sourceDesc);
     if (status == NO_ERROR) {
@@ -3503,7 +3411,7 @@
     disconnectAudioSource(sourceDesc);
 
     audio_attributes_t attributes = sourceDesc->attributes();
-    routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
+    routing_strategy strategy = getStrategyForAttr(&attributes);
     audio_stream_type_t stream = sourceDesc->stream();
     sp<DeviceDescriptor> srcDeviceDesc = sourceDesc->srcDevice();
 
@@ -3557,7 +3465,7 @@
             return INVALID_OPERATION;
         }
         uint32_t delayMs = 0;
-        status = startSource(outputDesc, stream, sinkDevice, NULL, &delayMs);
+        status = startSource(outputDesc, sourceDesc, &delayMs);
 
         if (status != NO_ERROR) {
             mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0);
@@ -3909,11 +3817,10 @@
     Vector<sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();
     for (size_t i = 0; i < activeInputs.size(); i++) {
         sp<AudioInputDescriptor> activeDesc = activeInputs[i];
-        AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true);
-        for (size_t j = 0; j < activeSessions.size(); j++) {
-            sp<AudioSession> activeSession = activeSessions.valueAt(j);
-            if (activeSession->uid() == uid) {
-                activeSession->setSilenced(silenced);
+        RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/);
+        for (const auto& client : clients) {
+            if (uid == client->uid()) {
+                client->setSilenced(silenced);
             }
         }
     }
@@ -3931,10 +3838,9 @@
     }
     removeAudioPatch(sourceDesc->patchDesc()->mHandle);
 
-    audio_stream_type_t stream = sourceDesc->stream();
     sp<SwAudioOutputDescriptor> swOutputDesc = sourceDesc->swOutput().promote();
     if (swOutputDesc != 0) {
-        status_t status = stopSource(swOutputDesc, stream, false);
+        status_t status = stopSource(swOutputDesc, sourceDesc);
         if (status == NO_ERROR) {
             swOutputDesc->stop();
         }
@@ -3958,8 +3864,7 @@
     for (size_t i = 0; i < mAudioSources.size(); i++)  {
         sp<SourceClientDescriptor> sourceDesc = mAudioSources.valueAt(i);
         audio_attributes_t attributes = sourceDesc->attributes();
-        routing_strategy sourceStrategy =
-                (routing_strategy) getStrategyForAttr(&attributes);
+        routing_strategy sourceStrategy = getStrategyForAttr(&attributes);
         sp<SwAudioOutputDescriptor> outputDesc = sourceDesc->swOutput().promote();
         if (sourceStrategy == strategy && outputDesc != 0 && outputDesc->mIoHandle == output) {
             source = sourceDesc;
@@ -4712,8 +4617,8 @@
             // the other output.
             bool wasActive = outputDesc2->isActive();
             for (int j = 0; j < AUDIO_STREAM_CNT; j++) {
-                int refCount = dupOutputDesc->mRefCount[j];
-                outputDesc2->changeRefCount((audio_stream_type_t)j,-refCount);
+                int activeCount = dupOutputDesc->streamActiveCount((audio_stream_type_t)j);
+                outputDesc2->changeStreamActiveCount((audio_stream_type_t)j,-activeCount);
             }
             // stop() will be a no op if the output is still active but is needed in case all
             // active streams refcounts where cleared above
@@ -4936,11 +4841,41 @@
     }
 }
 
+template <class IoDescriptor, class Filter>
+sp<DeviceDescriptor> AudioPolicyManager::findPreferredDevice(
+        IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices)
+{
+    auto activeClients = desc->clientsList(true /*activeOnly*/);
+    auto activeClientsWithRoute =
+        desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/);
+    active = activeClients.size() > 0;
+    if (active && activeClients.size() == activeClientsWithRoute.size()) {
+        return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId());
+    }
+    return nullptr;
+}
+
+template <class IoCollection, class Filter>
+sp<DeviceDescriptor> AudioPolicyManager::findPreferredDevice(
+        IoCollection& ioCollection, Filter filter, const DeviceVector& devices)
+{
+    sp<DeviceDescriptor> device;
+    for (size_t i = 0; i < ioCollection.size(); i++) {
+        auto desc = ioCollection.valueAt(i);
+        bool active;
+        sp<DeviceDescriptor> curDevice = findPreferredDevice(desc, filter, active, devices);
+        if (active && curDevice == nullptr) {
+            return nullptr;
+        } else if (curDevice != nullptr) {
+            device = curDevice;
+        }
+    }
+    return device;
+}
+
 audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                                        bool fromCache)
 {
-    audio_devices_t device = AUDIO_DEVICE_NONE;
-
     ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
     if (index >= 0) {
         sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -4951,18 +4886,13 @@
         }
     }
 
-    // Check if an explicit routing request exists for an active stream on this output and
-    // use it in priority before any other rule
-    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
-        if (outputDesc->isStreamActive((audio_stream_type_t)stream)) {
-            audio_devices_t forcedDevice =
-                    mOutputRoutes.getActiveDeviceForStream(
-                            (audio_stream_type_t)stream, mAvailableOutputDevices);
-
-            if (forcedDevice != AUDIO_DEVICE_NONE) {
-                return forcedDevice;
-            }
-        }
+    // Honor explicit routing requests only if no client using default routing is active on this
+    // input: a specific app can not force routing for other apps by setting a preferred device.
+    bool active; // unused
+    sp<DeviceDescriptor> deviceDesc =
+        findPreferredDevice(outputDesc, STRATEGY_NONE, active, mAvailableOutputDevices);
+    if (deviceDesc != nullptr) {
+        return deviceDesc->type();
     }
 
     // check the following by order of priority to request a routing change if necessary:
@@ -4988,6 +4918,7 @@
     // FIXME: extend use of isStrategyActiveOnSameModule() to all strategies
     // with a refined rule considering mutually exclusive devices (using same backend)
     // as opposed to all streams on the same audio HAL module.
+    audio_devices_t device = AUDIO_DEVICE_NONE;
     if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE) &&
         mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
         device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
@@ -5030,6 +4961,15 @@
         }
     }
 
+    // Honor explicit routing requests only if no client using default routing is active on this
+    // input: a specific app can not force routing for other apps by setting a preferred device.
+    bool active;
+    sp<DeviceDescriptor> deviceDesc =
+        findPreferredDevice(inputDesc, AUDIO_SOURCE_DEFAULT, active, mAvailableInputDevices);
+    if (deviceDesc != nullptr) {
+        return deviceDesc->type();
+    }
+
     // If we are not in call and no client is active on this input, this methods returns
     // AUDIO_DEVICE_NONE, causing the patch on the input stream to be released.
     audio_source_t source = inputDesc->getHighestPrioritySource(true /*activeOnly*/);
@@ -5059,6 +4999,7 @@
     if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) {
         return AUDIO_DEVICE_NONE;
     }
+    audio_devices_t activeDevices = AUDIO_DEVICE_NONE;
     audio_devices_t devices = AUDIO_DEVICE_NONE;
     for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
         if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
@@ -5067,15 +5008,20 @@
         routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
         audio_devices_t curDevices =
                 getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/);
+        devices |= curDevices;
         for (audio_io_handle_t output : getOutputsForDevice(curDevices, mOutputs)) {
             sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
             if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) {
-                curDevices |= outputDesc->device();
+                activeDevices |= outputDesc->device();
             }
         }
-        devices |= curDevices;
     }
 
+    // Favor devices selected on active streams if any to report correct device in case of
+    // explicit device selection
+    if (activeDevices != AUDIO_DEVICE_NONE) {
+        devices = activeDevices;
+    }
     /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it
       and doesn't really need to.*/
     if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
@@ -5091,16 +5037,16 @@
     return mEngine->getStrategyForStream(stream);
 }
 
-uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
+routing_strategy AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
     // flags to strategy mapping
     if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {
-        return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
+        return STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
     }
     if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
-        return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;
+        return STRATEGY_ENFORCED_AUDIBLE;
     }
     // usage to strategy mapping
-    return static_cast<uint32_t>(mEngine->getStrategyForUsage(attr->usage));
+    return mEngine->getStrategyForUsage(attr->usage);
 }
 
 void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) {
@@ -5176,17 +5122,11 @@
 audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                          bool fromCache)
 {
-    // Check if an explicit routing request exists for a stream type corresponding to the
-    // specified strategy and use it in priority over default routing rules.
-    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
-        if (getStrategy((audio_stream_type_t)stream) == strategy) {
-            audio_devices_t forcedDevice =
-                    mOutputRoutes.getActiveDeviceForStream(
-                            (audio_stream_type_t)stream, mAvailableOutputDevices);
-            if (forcedDevice != AUDIO_DEVICE_NONE) {
-                return forcedDevice;
-            }
-        }
+    // Honor explicit routing requests only if all active clients have a preferred route in which
+    // case the last active client route is used
+    sp<DeviceDescriptor> deviceDesc = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices);
+    if (deviceDesc != nullptr) {
+        return deviceDesc->type();
     }
 
     if (fromCache) {
@@ -5530,6 +5470,15 @@
 audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource,
                                                                   AudioMix **policyMix)
 {
+    // Honor explicit routing requests only if all active clients have a preferred route in which
+    // case the last active client route is used
+    sp<DeviceDescriptor> deviceDesc =
+        findPreferredDevice(mInputs, inputSource, mAvailableInputDevices);
+    if (deviceDesc != nullptr) {
+        return deviceDesc->type();
+    }
+
+
     audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
     audio_devices_t selectedDeviceFromMix =
            mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix);
@@ -5542,20 +5491,7 @@
 
 audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
 {
-    // Routing
-    // Scan the whole RouteMap to see if we have an explicit route:
-    // if the input source in the RouteMap is the same as the argument above,
-    // and activity count is non-zero and the device in the route descriptor is available
-    // then select this device.
-    for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) {
-         sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex);
-         if ((inputSource == route->mSource) && route->isActiveOrChanged() &&
-                 (mAvailableInputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
-             return route->mDeviceDescriptor->type();
-         }
-     }
-
-     return mEngine->getDeviceForInputSource(inputSource);
+    return mEngine->getDeviceForInputSource(inputSource);
 }
 
 float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
@@ -5860,7 +5796,7 @@
     }
     for (int i = 0; i < (int)AUDIO_STREAM_FOR_POLICY_CNT; i++) {
         if (((getStrategy((audio_stream_type_t)i) == strategy) ||
-                (NUM_STRATEGIES == strategy)) &&
+                (STRATEGY_NONE == strategy)) &&
                 outputDesc->isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) {
             return true;
         }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 9436767..6f4cce1 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -49,7 +49,6 @@
 #include <AudioPolicyMix.h>
 #include <EffectDescriptor.h>
 #include <SoundTriggerSession.h>
-#include <SessionRoute.h>
 #include <VolumeCurve.h>
 
 namespace android {
@@ -156,7 +155,7 @@
         // return the strategy corresponding to a given stream type
         virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
         // return the strategy corresponding to the given audio attributes
-        virtual uint32_t getStrategyForAttr(const audio_attributes_t *attr);
+        virtual routing_strategy getStrategyForAttr(const audio_attributes_t *attr);
 
         // return the enabled output devices for the given stream type
         virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream);
@@ -366,7 +365,7 @@
                              bool on,
                              const sp<AudioOutputDescriptor>& outputDesc,
                              int delayMs = 0,
-                             audio_devices_t device = (audio_devices_t)0);
+                             audio_devices_t device = AUDIO_DEVICE_NONE);
 
         // Mute or unmute the stream on the specified output
         void setStreamMute(audio_stream_type_t stream,
@@ -422,6 +421,14 @@
         // manages A2DP output suspend/restore according to phone state and BT SCO usage
         void checkA2dpSuspend();
 
+        template <class IoDescriptor, class Filter>
+        sp<DeviceDescriptor> findPreferredDevice(IoDescriptor& desc, Filter filter,
+                                                bool& active, const DeviceVector& devices);
+
+        template <class IoCollection, class Filter>
+        sp<DeviceDescriptor> findPreferredDevice(IoCollection& ioCollection, Filter filter,
+                                                const DeviceVector& devices);
+
         // selects the most appropriate device on output for current state
         // must be called every time a condition that affects the device choice for a given output is
         // changed: connected device, phone state, force use, output start, output stop..
@@ -508,16 +515,11 @@
         sp<DeviceDescriptor> findDevice(
                 const DeviceVector& devices, audio_devices_t device) const;
 
-        // if argument "device" is different from AUDIO_DEVICE_NONE,  startSource() will force
-        // the re-evaluation of the output device.
-        status_t startSource(const sp<AudioOutputDescriptor>& outputDesc,
-                             audio_stream_type_t stream,
-                             audio_devices_t device,
-                             const char *address,
+        status_t startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+                             const sp<TrackClientDescriptor>& client,
                              uint32_t *delayMs);
-        status_t stopSource(const sp<AudioOutputDescriptor>& outputDesc,
-                            audio_stream_type_t stream,
-                            bool forceDeviceUpdate);
+        status_t stopSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+                            const sp<TrackClientDescriptor>& client);
 
         void clearAudioPatches(uid_t uid);
         void clearSessionRoutes(uid_t uid);
@@ -537,15 +539,11 @@
 
         static bool isConcurrentSource(audio_source_t source);
 
-        bool isConcurentCaptureAllowed(const sp<AudioInputDescriptor>& inputDesc,
-                const sp<AudioSession>& audioSession);
-
         static bool streamsMatchForvolume(audio_stream_type_t stream1,
                                           audio_stream_type_t stream2);
 
-        void closeSessions(const sp<AudioInputDescriptor>& input, bool activeOnly);
-        void closeSession(const sp<AudioInputDescriptor>& input,
-                          const sp<AudioSession>& session);
+        void closeActiveClients(const sp<AudioInputDescriptor>& input);
+        void closeClient(audio_port_handle_t portId);
 
         const uid_t mUidCached;                         // AID_AUDIOSERVER
         AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
@@ -561,9 +559,6 @@
         DeviceVector  mAvailableOutputDevices; // all available output devices
         DeviceVector  mAvailableInputDevices;  // all available input devices
 
-        SessionRouteMap mOutputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_OUTPUT);
-        SessionRouteMap mInputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_INPUT);
-
         bool    mLimitRingtoneVolume;        // limit ringtone volume to music volume if headset connected
         audio_devices_t mDeviceForStrategy[NUM_STRATEGIES];
         float   mLastVoiceVolume;            // last voice volume value sent to audio HAL
@@ -668,7 +663,6 @@
         audio_io_handle_t getInputForDevice(audio_devices_t device,
                 String8 address,
                 audio_session_t session,
-                uid_t uid,
                 audio_source_t inputSource,
                 const audio_config_base_t *config,
                 audio_input_flags_t flags,
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index b3d564a..29b0561 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -656,12 +656,13 @@
         len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
         if (*curSize + len + 1 > *totSize) {
             *totSize = *curSize + len + 1;
-            *param = (char *)realloc(*param, *totSize);
-            if (*param == NULL) {
+            char *newParam = (char *)realloc(*param, *totSize);
+            if (newParam == NULL) {
                 len = 0;
                 ALOGE("%s realloc error for string len %zu", __func__, *totSize);
                 goto exit;
             }
+            *param = newParam;
         }
         strncpy(*param + *curSize, node->value, len);
         *curSize += len;
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 471c77d..e629cdd 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -125,7 +125,7 @@
     status_t res;
 
     std::vector<String8> cameraIds;
-    std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+    std::vector<std::string> ids = mProviderManager->getCameraDeviceIds();
     int numberOfCameras = static_cast<int>(ids.size());
     cameraIds.resize(numberOfCameras);
     // No module, must be provider
@@ -217,7 +217,7 @@
 
         if (mOpenedCameraIds.size() == 0) {
             // notify torch unavailable for all cameras with a flash
-            std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+            std::vector<std::string> ids = mProviderManager->getCameraDeviceIds();
             int numCameras = static_cast<int>(ids.size());
             for (int i = 0; i < numCameras; i++) {
                 String8 id8(ids[i].c_str());
@@ -263,7 +263,7 @@
 
     if (isBackwardCompatibleMode(cameraId)) {
         // notify torch available for all cameras with a flash
-        std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+        std::vector<std::string> ids = mProviderManager->getCameraDeviceIds();
         int numCameras = static_cast<int>(ids.size());
         for (int i = 0; i < numCameras; i++) {
             String8 id8(ids[i].c_str());
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 7656407..cbcc85b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2742,13 +2742,14 @@
 status_t Camera3Device::registerInFlight(uint32_t frameNumber,
         int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
         bool hasAppCallback, nsecs_t maxExpectedDuration,
-        std::set<String8>& physicalCameraIds, bool isStillCapture) {
+        std::set<String8>& physicalCameraIds, bool isStillCapture,
+        bool isZslCapture) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
     res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
-            hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture));
+            hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture));
     if (res < 0) return res;
 
     if (mInFlightMap.size() == 1) {
@@ -2766,11 +2767,12 @@
 
 void Camera3Device::returnOutputBuffers(
         const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
-        nsecs_t timestamp) {
+        nsecs_t timestamp, bool timestampIncreasing) {
+
     for (size_t i = 0; i < numBuffers; i++)
     {
         Camera3Stream *stream = Camera3Stream::cast(outputBuffers[i].stream);
-        status_t res = stream->returnBuffer(outputBuffers[i], timestamp);
+        status_t res = stream->returnBuffer(outputBuffers[i], timestamp, timestampIncreasing);
         // Note: stream may be deallocated at this point, if this buffer was
         // the last reference to it.
         if (res != OK) {
@@ -3214,8 +3216,9 @@
             request.pendingOutputBuffers.appendArray(result->output_buffers,
                 result->num_output_buffers);
         } else {
+            bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
             returnOutputBuffers(result->output_buffers,
-                result->num_output_buffers, shutterTimestamp);
+                result->num_output_buffers, shutterTimestamp, timestampIncreasing);
         }
 
         if (result->result != NULL && !isPartialResult) {
@@ -3421,8 +3424,9 @@
                     r.collectedPartialResult, msg.frame_number,
                     r.hasInputBuffer, r.physicalMetadatas);
             }
+            bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
             returnOutputBuffers(r.pendingOutputBuffers.array(),
-                r.pendingOutputBuffers.size(), r.shutterTimestamp);
+                r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing);
             r.pendingOutputBuffers.clear();
 
             removeInFlightRequestIfReadyLocked(idx);
@@ -4948,6 +4952,7 @@
             hasCallback = false;
         }
         bool isStillCapture = false;
+        bool isZslCapture = false;
         if (!mNextRequests[0].captureRequest->mSettingsList.begin()->metadata.isEmpty()) {
             camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
             find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_CAPTURE_INTENT, &e);
@@ -4955,13 +4960,18 @@
                 isStillCapture = true;
                 ATRACE_ASYNC_BEGIN("still capture", mNextRequests[i].halRequest.frame_number);
             }
+
+            find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_ENABLE_ZSL, &e);
+            if ((e.count > 0) && (e.data.u8[0] == ANDROID_CONTROL_ENABLE_ZSL_TRUE)) {
+                isZslCapture = true;
+            }
         }
         res = parent->registerInFlight(halRequest->frame_number,
                 totalNumBuffers, captureRequest->mResultExtras,
                 /*hasInput*/halRequest->input_buffer != NULL,
                 hasCallback,
                 calculateMaxExpectedDuration(halRequest->settings),
-                requestedPhysicalCameras, isStillCapture);
+                requestedPhysicalCameras, isStillCapture, isZslCapture);
         ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
                ", burstId = %" PRId32 ".",
                 __FUNCTION__,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 85f9614..159f2ca 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -999,6 +999,9 @@
         // Indicates a still capture request.
         bool stillCapture;
 
+        // Indicates a ZSL capture request
+        bool zslCapture;
+
         // Default constructor needed by KeyedVector
         InFlightRequest() :
                 shutterTimestamp(0),
@@ -1010,12 +1013,14 @@
                 hasCallback(true),
                 maxExpectedDuration(kDefaultExpectedDuration),
                 skipResultMetadata(false),
-                stillCapture(false) {
+                stillCapture(false),
+                zslCapture(false) {
         }
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
                 bool hasAppCallback, nsecs_t maxDuration,
-                const std::set<String8>& physicalCameraIdSet, bool isStillCapture) :
+                const std::set<String8>& physicalCameraIdSet, bool isStillCapture, 
+                bool isZslCapture) :
                 shutterTimestamp(0),
                 sensorTimestamp(0),
                 requestStatus(OK),
@@ -1027,7 +1032,8 @@
                 maxExpectedDuration(maxDuration),
                 skipResultMetadata(false),
                 physicalCameraIds(physicalCameraIdSet),
-                stillCapture(isStillCapture) {
+                stillCapture(isStillCapture),
+                zslCapture(isZslCapture) {
         }
     };
 
@@ -1044,7 +1050,7 @@
     status_t registerInFlight(uint32_t frameNumber,
             int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
             bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds,
-            bool isStillCapture);
+            bool isStillCapture, bool isZslCapture);
 
     /**
      * Returns the maximum expected time it'll take for all currently in-flight
@@ -1154,7 +1160,7 @@
 
     // helper function to return the output buffers to the streams.
     void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers,
-            size_t numBuffers, nsecs_t timestamp);
+            size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true);
 
     // Send a partial capture result.
     void sendPartialCaptureResult(const camera_metadata_t * partialResult,
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 2c020a2..ecbcf76 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -237,12 +237,14 @@
     /**
      * Return buffer back to ANativeWindow
      */
-    if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers) {
+    if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers || timestamp == 0) {
         // Cancel buffer
         if (mDropBuffers) {
             ALOGV("%s: Dropping a frame for stream %d.", __FUNCTION__, mId);
-        } else {
+        } else if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) {
             ALOGW("%s: A frame is dropped for stream %d due to buffer error.", __FUNCTION__, mId);
+        } else {
+            ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId);
         }
 
         res = currentConsumer->cancelBuffer(currentConsumer.get(),
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 2bb9ff7..fb3ce4c 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -60,9 +60,8 @@
         }
     }
 
-    android::PixelFormat format = isFormatOverridden() ? getOriginalFormat() : getFormat();
     res = mStreamSplitter->connect(initialSurfaces, usage, mUsage, camera3_stream::max_buffers,
-            getWidth(), getHeight(), format, &mConsumer);
+            getWidth(), getHeight(), getFormat(), &mConsumer);
     if (res != OK) {
         ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
                 __FUNCTION__, strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 6030d15..9238590 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -66,7 +66,8 @@
     mBufferLimitLatency(kBufferLimitLatencyBinSize),
     mFormatOverridden(false),
     mOriginalFormat(-1),
-    mPhysicalCameraId(physicalCameraId) {
+    mPhysicalCameraId(physicalCameraId),
+    mLastTimestamp(0) {
 
     camera3_stream::stream_type = type;
     camera3_stream::width = width;
@@ -649,7 +650,7 @@
 }
 
 status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer,
-        nsecs_t timestamp) {
+        nsecs_t timestamp, bool timestampIncreasing) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
 
@@ -661,6 +662,15 @@
 
     removeOutstandingBuffer(buffer);
 
+    // Buffer status may be changed, so make a copy of the stream_buffer struct.
+    camera3_stream_buffer b = buffer;
+    if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) {
+        ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64,
+                __FUNCTION__, mId, timestamp, mLastTimestamp);
+        b.status = CAMERA3_BUFFER_STATUS_ERROR;
+    }
+    mLastTimestamp = timestamp;
+
     /**
      * TODO: Check that the state is valid first.
      *
@@ -669,9 +679,9 @@
      *
      * Do this for getBuffer as well.
      */
-    status_t res = returnBufferLocked(buffer, timestamp);
+    status_t res = returnBufferLocked(b, timestamp);
     if (res == OK) {
-        fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/true);
+        fireBufferListenersLocked(b, /*acquired*/false, /*output*/true);
     }
 
     // Even if returning the buffer failed, we still want to signal whoever is waiting for the
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 4ddcf1a..e30fc59 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -320,7 +320,7 @@
      * For bidirectional streams, this method applies to the output-side buffers
      */
     status_t         returnBuffer(const camera3_stream_buffer &buffer,
-            nsecs_t timestamp);
+            nsecs_t timestamp, bool timestampIncreasing);
 
     /**
      * Fill in the camera3_stream_buffer with the next valid buffer for this
@@ -559,6 +559,7 @@
     android_dataspace mOriginalDataSpace;
 
     String8 mPhysicalCameraId;
+    nsecs_t mLastTimestamp;
 }; // class Camera3Stream
 
 }; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 9ed7184..bddd2e7 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -246,7 +246,7 @@
      * For bidirectional streams, this method applies to the output-side buffers
      */
     virtual status_t returnBuffer(const camera3_stream_buffer &buffer,
-            nsecs_t timestamp) = 0;
+            nsecs_t timestamp, bool timestampIncreasing = true) = 0;
 
     /**
      * Fill in the camera3_stream_buffer with the next valid buffer for this
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 8a9402e..8bc208d 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -182,12 +182,19 @@
         return BAD_VALUE;
     }
 
-  status_t res = native_window_set_buffers_dimensions(outputQueue.get(),
+    status_t res = native_window_set_buffers_dimensions(outputQueue.get(),
             mWidth, mHeight);
     if (res != NO_ERROR) {
         SP_LOGE("addOutput: failed to set buffer dimensions (%d)", res);
         return res;
     }
+    res = native_window_set_buffers_format(outputQueue.get(),
+            mFormat);
+    if (res != OK) {
+        ALOGE("%s: Unable to configure stream buffer format %#x for surfaceId %zu",
+                __FUNCTION__, mFormat, surfaceId);
+        return res;
+    }
 
     sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
     // Connect to the buffer producer
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 4b05395..45da56a 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -483,6 +483,7 @@
 {
     "audiopolicy",
     "audiorecord",
+    "audiothread",
     "audiotrack",
     "codec",
     "extractor",
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index 4031b11..966e214 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -49,6 +49,8 @@
 set_tid_address: 1
 write: 1
 nanosleep: 1
+sched_setscheduler: 1
+uname: 1
 
 # Required by AddressSanitizer
 gettid: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 04fee13..b1854bf 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -140,9 +140,8 @@
 }
 
 sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
-                                        const aaudio::AAudioStreamRequest &request,
-                                        aaudio_sharing_mode_t sharingMode) {
-    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+                                        const aaudio::AAudioStreamRequest &request) {
+    if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
         return openExclusiveEndpoint(audioService, request);
     } else {
         return openSharedEndpoint(audioService, request);
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 193bdee..ba17853 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -56,8 +56,7 @@
      * @return endpoint or null
      */
     android::sp<AAudioServiceEndpoint> openEndpoint(android::AAudioService &audioService,
-                                        const aaudio::AAudioStreamRequest &request,
-                                        aaudio_sharing_mode_t sharingMode);
+                                        const aaudio::AAudioStreamRequest &request);
 
     void closeEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
 
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 94440b1..53ee145 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -118,11 +118,16 @@
         }
     }
 
-    // if SHARED requested or if EXCLUSIVE failed
-    if (sharingMode == AAUDIO_SHARING_MODE_SHARED
-         || (serviceStream.get() == nullptr && !sharingModeMatchRequired)) {
+    // If SHARED requested or if EXCLUSIVE failed.
+    if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
         serviceStream =  new AAudioServiceStreamShared(*this);
         result = serviceStream->open(request);
+    } else if (serviceStream.get() == nullptr && !sharingModeMatchRequired) {
+        aaudio::AAudioStreamRequest modifiedRequest = request;
+        // Overwrite the original EXCLUSIVE mode with SHARED.
+        modifiedRequest.getConfiguration().setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+        serviceStream =  new AAudioServiceStreamShared(*this);
+        result = serviceStream->open(modifiedRequest);
     }
 
     if (result != AAUDIO_OK) {
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 0349034..24ab65e 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -59,6 +59,7 @@
     result << "    Device Id:            " << getDeviceId() << "\n";
     result << "    Sample Rate:          " << getSampleRate() << "\n";
     result << "    Channel Count:        " << getSamplesPerFrame() << "\n";
+    result << "    Format:               " << getFormat() << "\n";
     result << "    Frames Per Burst:     " << mFramesPerBurst << "\n";
     result << "    Usage:                " << getUsage() << "\n";
     result << "    ContentType:          " << getContentType() << "\n";
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index f30f9bb..18fcd35 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -111,11 +111,11 @@
     mRequestedDeviceId = deviceId = getDeviceId();
 
     // Fill in config
-    aaudio_format_t aaudioFormat = getFormat();
-    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
-        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+    audio_format_t audioFormat = getFormat();
+    if (audioFormat == AUDIO_FORMAT_DEFAULT || audioFormat == AUDIO_FORMAT_PCM_FLOAT) {
+        audioFormat = AUDIO_FORMAT_PCM_16_BIT;
     }
-    config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
+    config.format = audioFormat;
 
     int32_t aaudioSampleRate = getSampleRate();
     if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
@@ -210,9 +210,6 @@
         uid_t audioServiceUid = getuid();
         if ((mMmapClient.clientUid != audioServiceUid) &&
             getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
-            // Fallback is handled by caller but indicate what is possible in case
-            // this is used in the future
-            setSharingMode(AAUDIO_SHARING_MODE_SHARED);
             ALOGW("%s() - exclusive FD cannot be used by client", __func__);
             result = AAUDIO_ERROR_UNAVAILABLE;
             goto error;
@@ -233,7 +230,7 @@
         goto error;
     }
     mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
-    setFormat(AAudioConvert_androidToAAudioDataFormat(config.format));
+    setFormat(config.format);
     setSampleRate(config.sample_rate);
 
     // Scale up the burst size to meet the minimum equivalent in microseconds.
@@ -253,6 +250,9 @@
           ", deviceId = %d, capacity = %d\n",
           __func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
 
+    ALOGD("%s() format = =x%08x, frame size = %d",
+          __func__, getFormat(), calculateBytesPerFrame());
+
     return result;
 
 error:
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 63b9983..a7cd128 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -78,6 +78,7 @@
     setSamplesPerFrame(mStreamInternal->getSamplesPerFrame());
     setDeviceId(mStreamInternal->getDeviceId());
     setSessionId(mStreamInternal->getSessionId());
+    setFormat(AUDIO_FORMAT_PCM_FLOAT); // force for mixer
     mFramesPerBurst = mStreamInternal->getFramesPerBurst();
 
     return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 9af8af3..12be4a3 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -81,8 +81,7 @@
     return result.str();
 }
 
-aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
-                                              aaudio_sharing_mode_t sharingMode) {
+aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request) {
     AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
     aaudio_result_t result = AAUDIO_OK;
 
@@ -109,8 +108,7 @@
         // referenced until the service returns a handle to the client.
         // So only one thread can open a stream.
         mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
-                                                         request,
-                                                         sharingMode);
+                                                         request);
         if (mServiceEndpoint == nullptr) {
             ALOGE("%s() openEndpoint() failed", __func__);
             result = AAUDIO_ERROR_UNAVAILABLE;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index c845309..9377945 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -64,8 +64,13 @@
 
     sp<AAudioServiceStreamMMAP> keep(this);
 
-    aaudio_result_t result = AAudioServiceStreamBase::open(request,
-                                                           AAUDIO_SHARING_MODE_EXCLUSIVE);
+    if (request.getConstantConfiguration().getSharingMode() != AAUDIO_SHARING_MODE_EXCLUSIVE) {
+        ALOGE("%s() sharingMode mismatch %d", __func__,
+              request.getConstantConfiguration().getSharingMode());
+        return AAUDIO_ERROR_INTERNAL;
+    }
+
+    aaudio_result_t result = AAudioServiceStreamBase::open(request);
     if (result != AAUDIO_OK) {
         return result;
     }
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 05c5735..dbc2c2e 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -120,7 +120,13 @@
 
     sp<AAudioServiceStreamShared> keep(this);
 
-    aaudio_result_t result = AAudioServiceStreamBase::open(request, AAUDIO_SHARING_MODE_SHARED);
+    if (request.getConstantConfiguration().getSharingMode() != AAUDIO_SHARING_MODE_SHARED) {
+        ALOGE("%s() sharingMode mismatch %d", __func__,
+              request.getConstantConfiguration().getSharingMode());
+        return AAUDIO_ERROR_INTERNAL;
+    }
+
+    aaudio_result_t result = AAudioServiceStreamBase::open(request);
     if (result != AAUDIO_OK) {
         ALOGE("%s() returned %d", __func__, result);
         return result;
@@ -136,10 +142,10 @@
 
     // Is the request compatible with the shared endpoint?
     setFormat(configurationInput.getFormat());
-    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
-        setFormat(AAUDIO_FORMAT_PCM_FLOAT);
-    } else if (getFormat() != AAUDIO_FORMAT_PCM_FLOAT) {
-        ALOGD("%s() mAudioFormat = %d, need FLOAT", __func__, getFormat());
+    if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+        setFormat(AUDIO_FORMAT_PCM_FLOAT);
+    } else if (getFormat() != AUDIO_FORMAT_PCM_FLOAT) {
+        ALOGE("%s() audio_format_t mAudioFormat = %d, need FLOAT", __func__, getFormat());
         result = AAUDIO_ERROR_INVALID_FORMAT;
         goto error;
     }
@@ -148,7 +154,7 @@
     if (getSampleRate() == AAUDIO_UNSPECIFIED) {
         setSampleRate(endpoint->getSampleRate());
     } else if (getSampleRate() != endpoint->getSampleRate()) {
-        ALOGD("%s() mSampleRate = %d, need %d",
+        ALOGE("%s() mSampleRate = %d, need %d",
               __func__, getSampleRate(), endpoint->getSampleRate());
         result = AAUDIO_ERROR_INVALID_RATE;
         goto error;
@@ -158,7 +164,7 @@
     if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
         setSamplesPerFrame(endpoint->getSamplesPerFrame());
     } else if (getSamplesPerFrame() != endpoint->getSamplesPerFrame()) {
-        ALOGD("%s() mSamplesPerFrame = %d, need %d",
+        ALOGE("%s() mSamplesPerFrame = %d, need %d",
               __func__, getSamplesPerFrame(), endpoint->getSamplesPerFrame());
         result = AAUDIO_ERROR_OUT_OF_RANGE;
         goto error;
@@ -185,8 +191,8 @@
         }
     }
 
-    ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
-          getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId());
+    ALOGD("%s() actual rate = %d, channels = %d, deviceId = %d",
+          __func__, getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId());
 
     result = endpoint->registerStream(keep);
     if (result != AAUDIO_OK) {