Merge changes I24640ffb,I5c0ede96 into pi-dev am: 29d1cc0fe6
am: d42afb97fa

Change-Id: I30fabb967f245b8efebbd939877df62ebd8d3f6a
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 61fc897..2c088e6 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -579,12 +579,12 @@
                 break;
             }
 
+            CHECK(buffer != NULL);
+
             if (buffer->range_length() > 0) {
                 break;
             }
 
-            CHECK(buffer != NULL);
-
             buffer->release();
             buffer = NULL;
         }
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ca119d5..5f19f74 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -538,6 +538,10 @@
         mTimestampMutator.push(timestamp);
     }
 
+    virtual ExtendedTimestamp getTimestamp() const {
+        return mTimestampMutator.last();
+    }
+
     // Flushes the shared ring buffer if the client had requested it using mStreaming.mFlush.
     // If flush occurs then:
     //   cblk->u.mStreaming.mFront, ServerProxy::mFlush and ServerProxy::mFlushed will be modified
@@ -551,6 +555,9 @@
     // Total count of the number of flushed frames since creation (never reset).
     virtual int64_t     framesFlushed() const { return mFlushed; }
 
+    // Safe frames ready query with no side effects.
+    virtual size_t      framesReadySafe() const = 0;
+
     // Get dynamic buffer size from the shared control block.
     uint32_t            getBufferSizeInFrames() const {
         return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames);
@@ -588,8 +595,7 @@
     // which may include non-contiguous frames
     virtual size_t      framesReady();
 
-    // Safe frames ready query used by dump() - this has no side effects.
-    virtual size_t      framesReadySafe() const;
+    size_t              framesReadySafe() const override; // frames available to read by server.
 
     // Currently AudioFlinger will call framesReady() for a fast track from two threads:
     // FastMixer thread, and normal mixer thread.  This is dangerous, as the proxy is intended
@@ -693,6 +699,8 @@
         return mCblk->u.mStreaming.mRear; // For completeness only; mRear written by server.
     }
 
+    size_t framesReadySafe() const override; // frames available to read by client.
+
 protected:
     virtual ~AudioRecordServerProxy() { }
 };
diff --git a/media/OWNERS b/media/OWNERS
index 1f687a2..1e2d123 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -2,8 +2,10 @@
 dwkang@google.com
 elaurent@google.com
 essick@google.com
+gkasten@google.com
 hkuang@google.com
 hunga@google.com
+jiabin@google.com
 jmtrivi@google.com
 krocard@google.com
 lajos@google.com
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 1f2e82f..f1e815b 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -7,6 +7,7 @@
     ioprio rt 4
     writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
     onrestart restart vendor.audio-hal-2-0
+    onrestart restart vendor.audio-hal-4-0-msd
     # Keep the original service name for backward compatibility when upgrading
     # O-MR1 devices with framework-only.
     onrestart restart audio-hal-2-0
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 2df37a8..6146c0e 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -49,6 +49,7 @@
         "libaudiomanager",
         "libmedia_helper",
         "libmediametrics",
+        "libmediautils",
     ],
     export_shared_lib_headers: ["libbinder"],
 
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index dced3c4..a018b22 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -1231,6 +1231,21 @@
     return 0;
 }
 
+__attribute__((no_sanitize("integer")))
+size_t AudioRecordServerProxy::framesReadySafe() const
+{
+    if (mIsShutdown) {
+        return 0;
+    }
+    const int32_t front = android_atomic_acquire_load(&mCblk->u.mStreaming.mFront);
+    const int32_t rear = mCblk->u.mStreaming.mRear;
+    const ssize_t filled = rear - front;
+    if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+        return 0; // error condition, silently return 0.
+    }
+    return filled;
+}
+
 // ---------------------------------------------------------------------------
 
 }   // namespace android
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 00af7e8..37c62a8 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,10 +24,8 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
-#include <cutils/multiuser.h>
 #include <media/TimeCheck.h>
-#include <private/android_filesystem_config.h>
-
+#include <mediautils/ServiceUtilities.h>
 #include "IAudioFlinger.h"
 
 namespace android {
@@ -912,7 +910,7 @@
         case SET_MIC_MUTE:
         case SET_LOW_RAM_DEVICE:
         case SYSTEM_READY: {
-            if (multiuser_get_app_id(IPCThreadState::self()->getCallingUid()) >= AID_APP_START) {
+            if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
                 ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
                       __func__, code, IPCThreadState::self()->getCallingPid(),
                       IPCThreadState::self()->getCallingUid());
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index a1236e7..316105c 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -24,11 +24,10 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
-#include <cutils/multiuser.h>
 #include <media/AudioEffect.h>
 #include <media/IAudioPolicyService.h>
 #include <media/TimeCheck.h>
-#include <private/android_filesystem_config.h>
+#include <mediautils/ServiceUtilities.h>
 #include <system/audio.h>
 
 namespace android {
@@ -936,7 +935,7 @@
         case STOP_AUDIO_SOURCE:
         case GET_SURROUND_FORMATS:
         case SET_SURROUND_FORMAT_ENABLED: {
-            if (multiuser_get_app_id(IPCThreadState::self()->getCallingUid()) >= AID_APP_START) {
+            if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
                 ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
                       __func__, code, IPCThreadState::self()->getCallingPid(),
                       IPCThreadState::self()->getCallingUid());
diff --git a/media/libaudioclient/include/media/AudioParameter.h b/media/libaudioclient/include/media/AudioParameter.h
index 967d895..24837e3 100644
--- a/media/libaudioclient/include/media/AudioParameter.h
+++ b/media/libaudioclient/include/media/AudioParameter.h
@@ -64,6 +64,9 @@
     static const char * const keyPresentationId;
     static const char * const keyProgramId;
 
+    //  keyAudioLanguagePreferred: Preferred audio language
+    static const char * const keyAudioLanguagePreferred;
+
     //  keyStreamConnect / Disconnect: value is an int in audio_devices_t
     static const char * const keyStreamConnect;
     static const char * const keyStreamDisconnect;
diff --git a/media/libaudioclient/include/media/AudioTimestamp.h b/media/libaudioclient/include/media/AudioTimestamp.h
index 498de8e..e5925dd 100644
--- a/media/libaudioclient/include/media/AudioTimestamp.h
+++ b/media/libaudioclient/include/media/AudioTimestamp.h
@@ -135,8 +135,23 @@
         return INVALID_OPERATION;
     }
 
+    double getOutputServerLatencyMs(uint32_t sampleRate) const {
+        return getLatencyMs(sampleRate, LOCATION_SERVER, LOCATION_KERNEL);
+    }
+
+    double getLatencyMs(uint32_t sampleRate, Location location1, Location location2) const {
+        if (sampleRate > 0 && mTimeNs[location1] > 0 && mTimeNs[location2] > 0) {
+            const int64_t frameDifference =
+                    mPosition[location1] - mPosition[location2];
+            const int64_t timeDifferenceNs =
+                    mTimeNs[location1] - mTimeNs[location2];
+            return ((double)frameDifference * 1e9 / sampleRate - timeDifferenceNs) * 1e-6;
+        }
+        return 0.;
+    }
+
     // convert fields to a printable string
-    std::string toString() {
+    std::string toString() const {
         std::stringstream ss;
 
         ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << "\n";
diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp
index 5b33592..31da263 100644
--- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp
@@ -43,9 +43,6 @@
         ALOGE("Failed to obtain IDevicesFactory service, terminating process.");
         exit(1);
     }
-    // The MSD factory is optional
-    mDevicesFactoryMsd = IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD);
-    // TODO: Register death handler, and add 'restart' directive to audioserver.rc
 }
 
 DevicesFactoryHalHidl::~DevicesFactoryHalHidl() {
diff --git a/media/libaudiohal/2.0/DevicesFactoryHalHidl.h b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h
index 0748849..e2f1ad1 100644
--- a/media/libaudiohal/2.0/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h
@@ -39,7 +39,6 @@
     friend class DevicesFactoryHalHybrid;
 
     sp<IDevicesFactory> mDevicesFactory;
-    sp<IDevicesFactory> mDevicesFactoryMsd;
 
     static status_t nameFromHal(const char *name, IDevicesFactory::Device *device);
 
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp
index c83194e..c566728 100644
--- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <string.h>
+#include <vector>
 
 #define LOG_TAG "DevicesFactoryHalHidl"
 //#define LOG_NDEBUG 0
@@ -35,40 +36,48 @@
 namespace V4_0 {
 
 DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
-    mDevicesFactory = IDevicesFactory::getService();
-    if (mDevicesFactory != 0) {
-        // It is assumed that DevicesFactory is owned by AudioFlinger
-        // and thus have the same lifespan.
-        mDevicesFactory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
-    } else {
-        ALOGE("Failed to obtain IDevicesFactory service, terminating process.");
+    sp<IDevicesFactory> defaultFactory{IDevicesFactory::getService()};
+    if (!defaultFactory) {
+        ALOGE("Failed to obtain IDevicesFactory/default service, terminating process.");
         exit(1);
     }
+    mDeviceFactories.push_back(defaultFactory);
     // The MSD factory is optional
-    mDevicesFactoryMsd = IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD);
-    // TODO: Register death handler, and add 'restart' directive to audioserver.rc
-}
-
-DevicesFactoryHalHidl::~DevicesFactoryHalHidl() {
+    sp<IDevicesFactory> msdFactory{IDevicesFactory::getService(AUDIO_HAL_SERVICE_NAME_MSD)};
+    if (msdFactory) {
+        mDeviceFactories.push_back(msdFactory);
+    }
+    for (const auto& factory : mDeviceFactories) {
+        // It is assumed that the DevicesFactoryHalInterface instance is owned
+        // by AudioFlinger and thus have the same lifespan.
+        factory->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
+    }
 }
 
 status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
-    if (mDevicesFactory == 0) return NO_INIT;
+    if (mDeviceFactories.empty()) return NO_INIT;
     Result retval = Result::NOT_INITIALIZED;
-    Return<void> ret = mDevicesFactory->openDevice(
-            name,
-            [&](Result r, const sp<IDevice>& result) {
-                retval = r;
-                if (retval == Result::OK) {
-                    *device = new DeviceHalHidl(result);
-                }
-            });
-    if (ret.isOk()) {
-        if (retval == Result::OK) return OK;
-        else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE;
-        else return NO_INIT;
+    for (const auto& factory : mDeviceFactories) {
+        Return<void> ret = factory->openDevice(
+                name,
+                [&](Result r, const sp<IDevice>& result) {
+                    retval = r;
+                    if (retval == Result::OK) {
+                        *device = new DeviceHalHidl(result);
+                    }
+                });
+        if (!ret.isOk()) return FAILED_TRANSACTION;
+        switch (retval) {
+            // Device was found and was initialized successfully.
+            case Result::OK: return OK;
+            // Device was found but failed to initalize.
+            case Result::NOT_INITIALIZED: return NO_INIT;
+            // Otherwise continue iterating.
+            default: ;
+        }
     }
-    return FAILED_TRANSACTION;
+    ALOGW("The specified device name is not recognized: \"%s\"", name);
+    return BAD_VALUE;
 }
 
 } // namespace V4_0
diff --git a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h
index 114889b..c97178f 100644
--- a/media/libaudiohal/4.0/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/4.0/DevicesFactoryHalHidl.h
@@ -39,13 +39,12 @@
   private:
     friend class DevicesFactoryHalHybrid;
 
-    sp<IDevicesFactory> mDevicesFactory;
-    sp<IDevicesFactory> mDevicesFactoryMsd;
+    std::vector<sp<IDevicesFactory>> mDeviceFactories;
 
     // Can not be constructed directly by clients.
     DevicesFactoryHalHidl();
 
-    virtual ~DevicesFactoryHalHidl();
+    virtual ~DevicesFactoryHalHidl() = default;
 };
 
 } // namespace V4_0
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 1b3a1be..a22819a 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -32,6 +32,9 @@
         "libaudioclient_headers",
         "libaudio_system_headers",
     ],
+    export_header_lib_headers: [
+        "libmedia_headers",
+    ],
     clang: true,
 }
 
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index 034f7c2..1c95e27 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -36,6 +36,8 @@
 const char * const AudioParameter::keyHwAvSync = AUDIO_PARAMETER_HW_AV_SYNC;
 const char * const AudioParameter::keyPresentationId = AUDIO_PARAMETER_STREAM_PRESENTATION_ID;
 const char * const AudioParameter::keyProgramId = AUDIO_PARAMETER_STREAM_PROGRAM_ID;
+const char * const AudioParameter::keyAudioLanguagePreferred =
+        AUDIO_PARAMETER_KEY_AUDIO_LANGUAGE_PREFERRED;
 const char * const AudioParameter::keyMonoOutput = AUDIO_PARAMETER_MONO_OUTPUT;
 const char * const AudioParameter::keyStreamHwAvSync = AUDIO_PARAMETER_STREAM_HW_AV_SYNC;
 const char * const AudioParameter::keyStreamConnect = AUDIO_PARAMETER_DEVICE_CONNECT;
diff --git a/media/libmedia/include/media/PatchBuilder.h b/media/libmedia/include/media/PatchBuilder.h
new file mode 100644
index 0000000..f2722a6
--- /dev/null
+++ b/media/libmedia/include/media/PatchBuilder.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_PATCH_BUILDER_H
+#define ANDROID_PATCH_BUILDER_H
+
+#include <functional>
+#include <utility>
+
+#include <system/audio.h>
+#include <utils/StrongPointer.h>
+
+// This is a header-only utility.
+
+namespace android {
+
+class PatchBuilder {
+  public:
+    using mix_usecase_t = decltype(audio_port_config_mix_ext::usecase);
+
+    PatchBuilder() = default;
+
+    // All existing methods operating on audio patches take a pointer to const.
+    // It's OK to construct a temporary PatchBuilder while preparing a parameter
+    // to such a function because the Builder will be kept alive until the code
+    // execution reaches the function call statement semicolon.
+    const struct audio_patch* patch() const { return &mPatch; }
+
+    template<typename T, typename... S>
+    PatchBuilder& addSink(T&& t, S&&... s) {
+        sinks().add(std::forward<T>(t), std::forward<S>(s)...);
+        return *this;
+    }
+    // Explicit type of the second parameter allows clients to provide the struct inline.
+    template<typename T>
+    PatchBuilder& addSink(T&& t, const mix_usecase_t& update) {
+        sinks().add(std::forward<T>(t), update);
+        return *this;
+    }
+    template<typename T, typename... S>
+    PatchBuilder& addSource(T&& t, S&&... s) {
+        sources().add(std::forward<T>(t), std::forward<S>(s)...);
+        return *this;
+    }
+    // Explicit type of the second parameter allows clients to provide the struct inline.
+    template<typename T>
+    PatchBuilder& addSource(T&& t, const mix_usecase_t& update) {
+        sources().add(std::forward<T>(t), update);
+        return *this;
+    }
+
+  private:
+    struct PortCfgs {
+        PortCfgs(unsigned int *countPtr, struct audio_port_config *portCfgs)
+                : mCountPtr(countPtr), mPortCfgs(portCfgs) {}
+        audio_port_config& add(const audio_port_config& portCfg) {
+            return *advance() = portCfg;
+        }
+        template<typename T>
+        audio_port_config& add(const sp<T>& entity) {
+            audio_port_config* added = advance();
+            entity->toAudioPortConfig(added);
+            return *added;
+        }
+        template<typename T>
+        void add(const sp<T>& entity, const mix_usecase_t& usecaseUpdate) {
+            add(entity).ext.mix.usecase = usecaseUpdate;
+        }
+        template<typename T>
+        void add(const sp<T>& entity,
+                std::function<mix_usecase_t(const mix_usecase_t&)> usecaseUpdater) {
+            mix_usecase_t* usecase = &add(entity).ext.mix.usecase;
+            *usecase = usecaseUpdater(*usecase);
+        }
+        struct audio_port_config* advance() {
+            return &mPortCfgs[(*mCountPtr)++];
+        }
+        unsigned int *mCountPtr;
+        struct audio_port_config *mPortCfgs;
+    };
+
+    PortCfgs sinks() { return PortCfgs(&mPatch.num_sinks, mPatch.sinks); }
+    PortCfgs sources() { return PortCfgs(&mPatch.num_sources, mPatch.sources); }
+
+    struct audio_patch mPatch = {};
+};
+
+}  // namespace android
+
+#endif  // ANDROID_PATCH_BUILDER_H
diff --git a/media/libmedia/include/media/SingleStateQueue.h b/media/libmedia/include/media/SingleStateQueue.h
index d423962..c2761cb 100644
--- a/media/libmedia/include/media/SingleStateQueue.h
+++ b/media/libmedia/include/media/SingleStateQueue.h
@@ -99,6 +99,13 @@
             return mShared->mAck - sequence >= 0;
         }
 
+        // returns the last value written (or the contents of the shared buffer after initialization
+        // if no value was written).
+        T last() const
+        {   // assume no sequence check required - we are the writer.
+            return mShared->mValue;
+        }
+
     private:
         int32_t     mSequence;
         Shared * const mShared;
diff --git a/media/libmediaextractor/MediaBuffer.cpp b/media/libmediaextractor/MediaBuffer.cpp
index 39f8d6e..d197b3f 100644
--- a/media/libmediaextractor/MediaBuffer.cpp
+++ b/media/libmediaextractor/MediaBuffer.cpp
@@ -39,7 +39,7 @@
       mRangeOffset(0),
       mRangeLength(size),
       mOwnsData(false),
-      mMetaData(new MetaData),
+      mMetaData(new MetaDataBase),
       mOriginal(NULL) {
 }
 
@@ -51,7 +51,7 @@
       mRangeOffset(0),
       mRangeLength(size),
       mOwnsData(true),
-      mMetaData(new MetaData),
+      mMetaData(new MetaDataBase),
       mOriginal(NULL) {
     if (size < kSharedMemThreshold
             || std::atomic_load_explicit(&mUseSharedMemory, std::memory_order_seq_cst) == 0) {
@@ -84,7 +84,7 @@
       mRangeLength(mSize),
       mBuffer(buffer),
       mOwnsData(false),
-      mMetaData(new MetaData),
+      mMetaData(new MetaDataBase),
       mOriginal(NULL) {
 }
 
@@ -96,7 +96,7 @@
         return;
     }
 
-    int prevCount = __sync_fetch_and_sub(&mRefCount, 1);
+    int prevCount = mRefCount.fetch_sub(1);
     if (prevCount == 1) {
         if (mObserver == NULL) {
             delete this;
@@ -110,13 +110,13 @@
 
 void MediaBuffer::claim() {
     CHECK(mObserver != NULL);
-    CHECK_EQ(mRefCount, 1);
+    CHECK_EQ(mRefCount.load(std::memory_order_relaxed), 1);
 
-    mRefCount = 0;
+    mRefCount.store(0, std::memory_order_relaxed);
 }
 
 void MediaBuffer::add_ref() {
-    (void) __sync_fetch_and_add(&mRefCount, 1);
+    (void) mRefCount.fetch_add(1);
 }
 
 void *MediaBuffer::data() const {
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
index f944d51..5a25965 100644
--- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
@@ -86,12 +86,14 @@
     virtual MediaBufferBase *clone();
 
     // sum of localRefcount() and remoteRefcount()
+    // Result should be treated as approximate unless the result precludes concurrent accesses.
     virtual int refcount() const {
         return localRefcount() + remoteRefcount();
     }
 
+    // Result should be treated as approximate unless the result precludes concurrent accesses.
     virtual int localRefcount() const {
-        return mRefCount;
+        return mRefCount.load(std::memory_order_relaxed);
     }
 
     virtual int remoteRefcount() const {
@@ -146,7 +148,7 @@
     void claim();
 
     MediaBufferObserver *mObserver;
-    int mRefCount;
+    std::atomic<int> mRefCount;
 
     void *mData;
     size_t mSize, mRangeOffset, mRangeLength;
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 29a219f..1a185ab 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -498,9 +498,10 @@
         return ERROR_MALFORMED;
     }
 
-    int32_t width, height;
+    int32_t width, height, stride;
     CHECK(outputFormat->findInt32("width", &width));
     CHECK(outputFormat->findInt32("height", &height));
+    CHECK(outputFormat->findInt32("stride", &stride));
 
     int32_t crop_left, crop_top, crop_right, crop_bottom;
     if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
@@ -527,11 +528,10 @@
     if (converter.isValid()) {
         converter.convert(
                 (const uint8_t *)videoFrameBuffer->data(),
-                width, height,
+                width, height, stride,
                 crop_left, crop_top, crop_right, crop_bottom,
                 frame->getFlattenedData(),
-                frame->mWidth,
-                frame->mHeight,
+                frame->mWidth, frame->mHeight, frame->mRowBytes,
                 crop_left, crop_top, crop_right, crop_bottom);
         return OK;
     }
@@ -678,9 +678,10 @@
         return ERROR_MALFORMED;
     }
 
-    int32_t width, height;
+    int32_t width, height, stride;
     CHECK(outputFormat->findInt32("width", &width));
     CHECK(outputFormat->findInt32("height", &height));
+    CHECK(outputFormat->findInt32("stride", &stride));
 
     if (mFrame == NULL) {
         sp<IMemory> frameMem = allocVideoFrame(
@@ -724,11 +725,10 @@
     if (converter.isValid()) {
         converter.convert(
                 (const uint8_t *)videoFrameBuffer->data(),
-                width, height,
+                width, height, stride,
                 crop_left, crop_top, crop_right, crop_bottom,
                 mFrame->getFlattenedData(),
-                mFrame->mWidth,
-                mFrame->mHeight,
+                mFrame->mWidth, mFrame->mHeight, mFrame->mRowBytes,
                 dstLeft, dstTop, dstRight, dstBottom);
         return OK;
     }
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 05f4104..c46a40f 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -85,9 +85,15 @@
             || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
 }
 
+/*
+ * If stride is non-zero, client's stride will be used. For planar
+ * or semi-planar YUV formats, stride must be even numbers.
+ * If stride is zero, it will be calculated based on width and bpp
+ * of the format, assuming no padding on the right edge.
+ */
 ColorConverter::BitmapParams::BitmapParams(
         void *bits,
-        size_t width, size_t height,
+        size_t width, size_t height, size_t stride,
         size_t cropLeft, size_t cropTop,
         size_t cropRight, size_t cropBottom,
         OMX_COLOR_FORMATTYPE colorFromat)
@@ -101,6 +107,8 @@
       mCropBottom(cropBottom) {
     switch(mColorFormat) {
     case OMX_COLOR_Format16bitRGB565:
+    case OMX_COLOR_FormatYUV420Planar16:
+    case OMX_COLOR_FormatCbYCrY:
         mBpp = 2;
         mStride = 2 * mWidth;
         break;
@@ -112,13 +120,7 @@
         mStride = 4 * mWidth;
         break;
 
-    case OMX_COLOR_FormatYUV420Planar16:
-        mBpp = 2;
-        mStride = 2 * mWidth;
-        break;
-
     case OMX_COLOR_FormatYUV420Planar:
-    case OMX_COLOR_FormatCbYCrY:
     case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
     case OMX_COLOR_FormatYUV420SemiPlanar:
     case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
@@ -132,6 +134,10 @@
         mStride = mWidth;
         break;
     }
+    // use client's stride if it's specified.
+    if (stride != 0) {
+        mStride = stride;
+    }
 }
 
 size_t ColorConverter::BitmapParams::cropWidth() const {
@@ -144,21 +150,21 @@
 
 status_t ColorConverter::convert(
         const void *srcBits,
-        size_t srcWidth, size_t srcHeight,
+        size_t srcWidth, size_t srcHeight, size_t srcStride,
         size_t srcCropLeft, size_t srcCropTop,
         size_t srcCropRight, size_t srcCropBottom,
         void *dstBits,
-        size_t dstWidth, size_t dstHeight,
+        size_t dstWidth, size_t dstHeight, size_t dstStride,
         size_t dstCropLeft, size_t dstCropTop,
         size_t dstCropRight, size_t dstCropBottom) {
     BitmapParams src(
             const_cast<void *>(srcBits),
-            srcWidth, srcHeight,
+            srcWidth, srcHeight, srcStride,
             srcCropLeft, srcCropTop, srcCropRight, srcCropBottom, mSrcFormat);
 
     BitmapParams dst(
             dstBits,
-            dstWidth, dstHeight,
+            dstWidth, dstHeight, dstStride,
             dstCropLeft, dstCropTop, dstCropRight, dstCropBottom, mDstFormat);
 
     if (!((src.mCropLeft & 1) == 0
@@ -792,15 +798,15 @@
 
     uint8_t *kAdjustedClip = initClip();
 
-    uint16_t *dst_ptr = (uint16_t *)dst.mBits
-        + dst.mCropTop * dst.mWidth + dst.mCropLeft;
+    uint16_t *dst_ptr = (uint16_t *)((uint8_t *)
+            dst.mBits + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp);
 
     const uint8_t *src_y =
-        (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
+        (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
 
     const uint8_t *src_u =
-        (const uint8_t *)src_y + src.mWidth * src.mHeight
-        + src.mCropTop * src.mWidth + src.mCropLeft;
+        (const uint8_t *)src.mBits + src.mHeight * src.mStride +
+        src.mCropTop * src.mStride / 2 + src.mCropLeft;
 
     for (size_t y = 0; y < src.cropHeight(); ++y) {
         for (size_t x = 0; x < src.cropWidth(); x += 2) {
@@ -842,13 +848,13 @@
             }
         }
 
-        src_y += src.mWidth;
+        src_y += src.mStride;
 
         if (y & 1) {
-            src_u += src.mWidth;
+            src_u += src.mStride;
         }
 
-        dst_ptr += dst.mWidth;
+        dst_ptr = (uint16_t*)((uint8_t*)dst_ptr + dst.mStride);
     }
 
     return OK;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 657a05b..359df3d 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -44,6 +44,7 @@
       mNativeWindow(nativeWindow),
       mWidth(0),
       mHeight(0),
+      mStride(0),
       mCropLeft(0),
       mCropTop(0),
       mCropRight(0),
@@ -67,9 +68,10 @@
     int32_t colorFormatNew;
     CHECK(format->findInt32("color-format", &colorFormatNew));
 
-    int32_t widthNew, heightNew;
-    CHECK(format->findInt32("stride", &widthNew));
+    int32_t widthNew, heightNew, strideNew;
+    CHECK(format->findInt32("width", &widthNew));
     CHECK(format->findInt32("slice-height", &heightNew));
+    CHECK(format->findInt32("stride", &strideNew));
 
     int32_t cropLeftNew, cropTopNew, cropRightNew, cropBottomNew;
     if (!format->findRect(
@@ -106,6 +108,7 @@
     mColorFormat = static_cast<OMX_COLOR_FORMATTYPE>(colorFormatNew);
     mWidth = widthNew;
     mHeight = heightNew;
+    mStride = strideNew;
     mCropLeft = cropLeftNew;
     mCropTop = cropTopNew;
     mCropRight = cropRightNew;
@@ -276,20 +279,15 @@
     if (mConverter) {
         mConverter->convert(
                 data,
-                mWidth, mHeight,
+                mWidth, mHeight, mStride,
                 mCropLeft, mCropTop, mCropRight, mCropBottom,
                 dst,
-                buf->stride, buf->height,
+                buf->stride, buf->height, 0,
                 0, 0, mCropWidth - 1, mCropHeight - 1);
     } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar) {
-        const uint8_t *src_y = (const uint8_t *)data;
-        const uint8_t *src_u =
-                (const uint8_t *)data + mWidth * mHeight;
-        const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
-
-        src_y +=mCropLeft + mCropTop * mWidth;
-        src_u +=(mCropLeft + mCropTop * mWidth / 2)/2;
-        src_v +=(mCropLeft + mCropTop * mWidth / 2)/2;
+        const uint8_t *src_y = (const uint8_t *)data + mCropTop * mStride + mCropLeft;
+        const uint8_t *src_u = (const uint8_t *)data + mStride * mHeight + mCropTop * mStride / 4;
+        const uint8_t *src_v = (const uint8_t *)src_u + mStride * mHeight / 4;
 
         uint8_t *dst_y = (uint8_t *)dst;
         size_t dst_y_size = buf->stride * buf->height;
@@ -305,7 +303,7 @@
         for (int y = 0; y < mCropHeight; ++y) {
             memcpy(dst_y, src_y, mCropWidth);
 
-            src_y += mWidth;
+            src_y += mStride;
             dst_y += buf->stride;
         }
 
@@ -313,19 +311,15 @@
             memcpy(dst_u, src_u, (mCropWidth + 1) / 2);
             memcpy(dst_v, src_v, (mCropWidth + 1) / 2);
 
-            src_u += mWidth / 2;
-            src_v += mWidth / 2;
+            src_u += mStride / 2;
+            src_v += mStride / 2;
             dst_u += dst_c_stride;
             dst_v += dst_c_stride;
         }
     } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
-        const uint16_t *src_y = (const uint16_t *)data;
-        const uint16_t *src_u = (const uint16_t *)data + mWidth * mHeight;
-        const uint16_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
-
-        src_y += mCropLeft + mCropTop * mWidth;
-        src_u += (mCropLeft + mCropTop * mWidth / 2) / 2;
-        src_v += (mCropLeft + mCropTop * mWidth / 2) / 2;
+        const uint8_t *src_y = (const uint8_t *)data + mCropTop * mStride + mCropLeft * 2;
+        const uint8_t *src_u = (const uint8_t *)data + mStride * mHeight + mCropTop * mStride / 4;
+        const uint8_t *src_v = (const uint8_t *)src_u + mStride * mHeight / 4;
 
         uint8_t *dst_y = (uint8_t *)dst;
         size_t dst_y_size = buf->stride * buf->height;
@@ -340,21 +334,21 @@
 
         for (int y = 0; y < mCropHeight; ++y) {
             for (int x = 0; x < mCropWidth; ++x) {
-                dst_y[x] = (uint8_t)(src_y[x] >> 2);
+                dst_y[x] = (uint8_t)(((uint16_t *)src_y)[x] >> 2);
             }
 
-            src_y += mWidth;
+            src_y += mStride;
             dst_y += buf->stride;
         }
 
         for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
             for (int x = 0; x < (mCropWidth + 1) / 2; ++x) {
-                dst_u[x] = (uint8_t)(src_u[x] >> 2);
-                dst_v[x] = (uint8_t)(src_v[x] >> 2);
+                dst_u[x] = (uint8_t)(((uint16_t *)src_u)[x] >> 2);
+                dst_v[x] = (uint8_t)(((uint16_t *)src_v)[x] >> 2);
             }
 
-            src_u += mWidth / 2;
-            src_v += mWidth / 2;
+            src_u += mStride / 2;
+            src_v += mStride / 2;
             dst_u += dst_c_stride;
             dst_v += dst_c_stride;
         }
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index c286516..64dca4e 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -51,7 +51,7 @@
     ColorConverter *mConverter;
     YUVMode mYUVMode;
     sp<ANativeWindow> mNativeWindow;
-    int32_t mWidth, mHeight;
+    int32_t mWidth, mHeight, mStride;
     int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
     int32_t mCropWidth, mCropHeight;
     int32_t mRotationDegrees;
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index 5b3543d..2b8c7c8 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -37,11 +37,11 @@
 
     status_t convert(
             const void *srcBits,
-            size_t srcWidth, size_t srcHeight,
+            size_t srcWidth, size_t srcHeight, size_t srcStride,
             size_t srcCropLeft, size_t srcCropTop,
             size_t srcCropRight, size_t srcCropBottom,
             void *dstBits,
-            size_t dstWidth, size_t dstHeight,
+            size_t dstWidth, size_t dstHeight, size_t dstStride,
             size_t dstCropLeft, size_t dstCropTop,
             size_t dstCropRight, size_t dstCropBottom);
 
@@ -49,7 +49,7 @@
     struct BitmapParams {
         BitmapParams(
                 void *bits,
-                size_t width, size_t height,
+                size_t width, size_t height, size_t stride,
                 size_t cropLeft, size_t cropTop,
                 size_t cropRight, size_t cropBottom,
                 OMX_COLOR_FORMATTYPE colorFromat);
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 935dc34..672a37c 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -154,12 +154,12 @@
     outDef->format.video.nFrameWidth = outputBufferWidth();
     outDef->format.video.nFrameHeight = outputBufferHeight();
     outDef->format.video.eColorFormat = mOutputFormat;
-    outDef->format.video.nStride = outDef->format.video.nFrameWidth;
     outDef->format.video.nSliceHeight = outDef->format.video.nFrameHeight;
 
     int32_t bpp = (mOutputFormat == OMX_COLOR_FormatYUV420Planar16) ? 2 : 1;
+    outDef->format.video.nStride = outDef->format.video.nFrameWidth * bpp;
     outDef->nBufferSize =
-        (outDef->format.video.nStride * outDef->format.video.nSliceHeight * bpp * 3) / 2;
+            (outDef->format.video.nStride * outDef->format.video.nSliceHeight * 3) / 2;
 
     OMX_PARAM_PORTDEFINITIONTYPE *inDef = &editPortInfo(kInputPortIndex)->mDef;
     inDef->format.video.nFrameWidth = mWidth;
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index d6dae5b..de8e46a 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -21,9 +21,11 @@
         "MemoryLeakTrackUtil.cpp",
         "ProcessInfo.cpp",
         "SchedulingPolicyService.cpp",
+        "ServiceUtilities.cpp",
     ],
     shared_libs: [
         "libbinder",
+        "libcutils",
         "liblog",
         "libutils",
         "libmemunreachable",
diff --git a/services/audioflinger/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
similarity index 84%
rename from services/audioflinger/ServiceUtilities.cpp
rename to media/utils/ServiceUtilities.cpp
index aa267ea..0d50be0 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "ServiceUtilities"
+
 #include <binder/AppOpsManager.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
-#include <private/android_filesystem_config.h>
-#include "ServiceUtilities.h"
+#include "mediautils/ServiceUtilities.h"
 
 /* When performing permission checks we do not use permission cache for
  * runtime permissions (protection level dangerous) as they may change at
@@ -32,24 +33,6 @@
 
 static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
 
-// Not valid until initialized by AudioFlinger constructor.  It would have to be
-// re-initialized if the process containing AudioFlinger service forks (which it doesn't).
-// This is often used to validate binder interface calls within audioserver
-// (e.g. AudioPolicyManager to AudioFlinger).
-pid_t getpid_cached;
-
-// A trusted calling UID may specify the client UID as part of a binder interface call.
-// otherwise the calling UID must be equal to the client UID.
-bool isTrustedCallingUid(uid_t uid) {
-    switch (uid) {
-    case AID_MEDIA:
-    case AID_AUDIOSERVER:
-        return true;
-    default:
-        return false;
-    }
-}
-
 static String16 resolveCallingPackage(PermissionController& permissionController,
         const String16& opPackageName, uid_t uid) {
     if (opPackageName.size() > 0) {
@@ -71,16 +54,11 @@
     return packages[0];
 }
 
-static inline bool isAudioServerOrRoot(uid_t uid) {
-    // AID_ROOT is OK for command-line tests.  Native unforked audioserver always OK.
-    return uid == AID_ROOT || uid == AID_AUDIOSERVER ;
-}
-
 static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
         uid_t uid, bool start) {
     // Okay to not track in app ops as audio server is us and if
     // device is rooted security model is considered compromised.
-    if (isAudioServerOrRoot(uid)) return true;
+    if (isAudioServerOrRootUid(uid)) return true;
 
     // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
     // may open a record track on behalf of a client.  Note that pid may be a tid.
@@ -127,7 +105,7 @@
 void finishRecording(const String16& opPackageName, uid_t uid) {
     // Okay to not track in app ops as audio server is us and if
     // device is rooted security model is considered compromised.
-    if (isAudioServerOrRoot(uid)) return;
+    if (isAudioServerOrRootUid(uid)) return;
 
     PermissionController permissionController;
     String16 resolvedOpPackageName = resolveCallingPackage(
@@ -142,7 +120,7 @@
 }
 
 bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {
-    if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+    if (isAudioServerOrRootUid(uid)) return true;
     static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
     bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);
     if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
@@ -163,7 +141,8 @@
 }
 
 bool settingsAllowed() {
-    if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+    // given this is a permission check, could this be isAudioServerOrRootUid()?
+    if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
     static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
     // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
     bool ok = PermissionCache::checkCallingPermission(sAudioSettings);
@@ -180,7 +159,6 @@
 }
 
 bool dumpAllowed() {
-    // don't optimize for same pid, since mediaserver never dumps itself
     static const String16 sDump("android.permission.DUMP");
     // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
     bool ok = PermissionCache::checkCallingPermission(sDump);
@@ -196,4 +174,29 @@
     return ok;
 }
 
+status_t checkIMemory(const sp<IMemory>& iMemory)
+{
+    if (iMemory == 0) {
+        ALOGE("%s check failed: NULL IMemory pointer", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    sp<IMemoryHeap> heap = iMemory->getMemory();
+    if (heap == 0) {
+        ALOGE("%s check failed: NULL heap pointer", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    off_t size = lseek(heap->getHeapID(), 0, SEEK_END);
+    lseek(heap->getHeapID(), 0, SEEK_SET);
+
+    if (iMemory->pointer() == NULL || size < (off_t)iMemory->size()) {
+        ALOGE("%s check failed: pointer %p size %zu fd size %u",
+              __FUNCTION__, iMemory->pointer(), iMemory->size(), (uint32_t)size);
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
 } // namespace android
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
new file mode 100644
index 0000000..0911744
--- /dev/null
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 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 <unistd.h>
+
+#include <binder/IMemory.h>
+#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+
+// Audio permission utilities
+
+// Used for calls that should originate from system services.
+// We allow that some services might have separate processes to
+// handle multiple users, e.g. u10_system, u10_bluetooth, u10_radio.
+static inline bool isServiceUid(uid_t uid) {
+    return multiuser_get_app_id(uid) < AID_APP_START;
+}
+
+// Used for calls that should originate from audioserver.
+static inline bool isAudioServerUid(uid_t uid) {
+    return uid == AID_AUDIOSERVER;
+}
+
+// Used for some permission checks.
+// AID_ROOT is OK for command-line tests.  Native audioserver always OK.
+static inline bool isAudioServerOrRootUid(uid_t uid) {
+    return uid == AID_AUDIOSERVER || uid == AID_ROOT;
+}
+
+// Used for calls that should come from system server or internal.
+// Note: system server is multiprocess for multiple users.  audioserver is not.
+static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
+    return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
+}
+
+// Mediaserver may forward the client PID and UID as part of a binder interface call;
+// otherwise the calling UID must be equal to the client UID.
+static inline bool isAudioServerOrMediaServerUid(uid_t uid) {
+    switch (uid) {
+    case AID_MEDIA:
+    case AID_AUDIOSERVER:
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
+bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid);
+void finishRecording(const String16& opPackageName, uid_t uid);
+bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
+bool captureHotwordAllowed(pid_t pid, uid_t uid);
+bool settingsAllowed();
+bool modifyAudioRoutingAllowed();
+bool dumpAllowed();
+bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
+status_t checkIMemory(const sp<IMemory>& iMemory);
+}
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index def9dc5..55a5424 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -42,7 +42,7 @@
 #
 #LOCAL_MULTILIB := first
 #
-#LOCAL_JAVA_LIBRARIES += android-support-annotations
+#LOCAL_JAVA_LIBRARIES += androidx.annotation_annotation
 #
 ## To embed native libraries in package, uncomment the lines below.
 ##LOCAL_MODULE_TAGS := samples
@@ -60,9 +60,9 @@
 #
 ## TODO: Remove dependency with other support libraries.
 #LOCAL_STATIC_ANDROID_LIBRARIES += \
-#    android-support-v4 \
-#    android-support-v7-appcompat \
-#    android-support-v7-palette
+#    androidx.legacy_legacy-support-v4 \
+#    androidx.appcompat_appcompat \
+#    androidx.palette_palette
 #LOCAL_USE_AAPT2 := true
 #
 #include $(BUILD_PACKAGE)
diff --git a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
index b304471..f6f7be5 100644
--- a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
+++ b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
@@ -169,7 +169,7 @@
             android:layout_height="wrap_content"
             android:fillViewport="true"
             android:scrollIndicators="top|bottom">
-            <android.support.v7.widget.ButtonBarLayout
+            <androidx.appcompat.widget.ButtonBarLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:gravity="bottom"
@@ -184,7 +184,7 @@
                     style="?android:attr/buttonBarNeutralButtonStyle"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"/>
-                <android.support.v4.widget.Space
+                <androidx.legacy.widget.Space
                     android:id="@+id/spacer"
                     android:layout_width="0dp"
                     android:layout_height="0dp"
@@ -200,7 +200,7 @@
                     style="?android:attr/buttonBarPositiveButtonStyle"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"/>
-            </android.support.v7.widget.ButtonBarLayout>
+            </androidx.appcompat.widget.ButtonBarLayout>
         </ScrollView>
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/MediaComponents/res/layout/mr_controller_volume_item.xml b/packages/MediaComponents/res/layout/mr_controller_volume_item.xml
index a89058b..12d85ae 100644
--- a/packages/MediaComponents/res/layout/mr_controller_volume_item.xml
+++ b/packages/MediaComponents/res/layout/mr_controller_volume_item.xml
@@ -40,7 +40,7 @@
                        android:layout_marginBottom="8dp"
                        android:scaleType="fitCenter"
                        android:src="?attr/mediaRouteAudioTrackDrawable" />
-            <android.support.v7.app.MediaRouteVolumeSlider
+            <androidx.mediarouter.app.MediaRouteVolumeSlider
                 android:id="@+id/mr_volume_slider"
                 android:layout_width="fill_parent"
                 android:layout_height="40dp"
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index c909099..0327beb 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
-import android.media.MediaController2;
 import android.media.MediaItem2;
 import android.media.SessionToken2;
 import android.media.update.MediaBrowser2Provider;
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 249365a..2883087 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -16,7 +16,6 @@
 
 package com.android.media;
 
-import static android.media.SessionCommand2.COMMAND_CODE_SET_VOLUME;
 import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_ADD_ITEM;
 import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM;
 import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM;
@@ -30,6 +29,7 @@
 import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID;
 import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
 import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
+import static android.media.SessionCommand2.COMMAND_CODE_SET_VOLUME;
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -44,11 +44,11 @@
 import android.media.MediaMetadata2;
 import android.media.MediaPlaylistAgent.RepeatMode;
 import android.media.MediaPlaylistAgent.ShuffleMode;
-import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
-import android.media.SessionCommandGroup2;
 import android.media.MediaSessionService2;
 import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
 import android.media.update.MediaController2Provider;
 import android.net.Uri;
@@ -58,10 +58,11 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
-import android.support.annotation.GuardedBy;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
index 2cfc5df..ece4a00 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
@@ -21,8 +21,8 @@
 import android.media.MediaController2;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
-import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
+import android.media.SessionCommand2;
 import android.media.SessionCommandGroup2;
 import android.os.Bundle;
 import android.os.ResultReceiver;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 4ec6042..72ecf54 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -44,13 +44,13 @@
 import android.media.MediaPlaylistAgent.PlaylistEventCallback;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Builder;
-import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
-import android.media.SessionCommandGroup2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
 import android.media.session.MediaSessionManager;
@@ -60,10 +60,11 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.ResultReceiver;
-import android.support.annotation.GuardedBy;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
+
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index ec657d7..11ccd9f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -22,11 +22,11 @@
 import android.media.MediaItem2;
 import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaMetadata2;
-import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
-import android.media.SessionCommandGroup2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.VolumeProvider2;
 import android.net.Uri;
 import android.os.Binder;
@@ -35,13 +35,14 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.support.annotation.GuardedBy;
-import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+
 import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
 import com.android.media.MediaSession2Impl.CommandButtonImpl;
 import com.android.media.MediaSession2Impl.CommandGroupImpl;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index c33eb65..d975839 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -20,7 +20,6 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
-import android.content.Context;
 import android.content.Intent;
 import android.media.MediaPlayerBase;
 import android.media.MediaPlayerBase.PlayerEventCallback;
@@ -31,9 +30,10 @@
 import android.media.SessionToken2.TokenType;
 import android.media.update.MediaSessionService2Provider;
 import android.os.IBinder;
-import android.support.annotation.GuardedBy;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
+
 // TODO(jaewan): Need a test for session service itself.
 public class MediaSessionService2Impl implements MediaSessionService2Provider {
 
diff --git a/packages/MediaComponents/src/com/android/media/Rating2Impl.java b/packages/MediaComponents/src/com/android/media/Rating2Impl.java
index d558129..e2b9f0a 100644
--- a/packages/MediaComponents/src/com/android/media/Rating2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/Rating2Impl.java
@@ -18,7 +18,6 @@
 
 import static android.media.Rating2.*;
 
-import android.content.Context;
 import android.media.Rating2;
 import android.media.Rating2.Style;
 import android.media.update.Rating2Provider;
diff --git a/packages/MediaComponents/src/com/android/media/RoutePlayer.java b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
index 9450d34..ebff0e2 100644
--- a/packages/MediaComponents/src/com/android/media/RoutePlayer.java
+++ b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
@@ -23,7 +23,8 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 import com.android.support.mediarouter.media.MediaItemStatus;
 import com.android.support.mediarouter.media.MediaRouter;
@@ -33,8 +34,6 @@
 import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback;
 import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback;
 
-import java.util.Map;
-
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 public class RoutePlayer extends MediaSession.Callback {
     public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE
diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
index a5cf8c4..f792712 100644
--- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -16,9 +16,9 @@
 
 package com.android.media;
 
+import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
 import static android.media.SessionToken2.TYPE_SESSION;
 import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
-import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
 
 import android.content.Context;
 import android.content.Intent;
diff --git a/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java b/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java
index a4d55d7..97d3927 100644
--- a/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java
+++ b/packages/MediaComponents/src/com/android/media/subtitle/SubtitleController.java
@@ -16,12 +16,8 @@
 
 package com.android.media.subtitle;
 
-import java.util.Locale;
-import java.util.Vector;
-
 import android.content.Context;
 import android.media.MediaFormat;
-import android.media.MediaPlayer2;
 import android.media.MediaPlayer2.TrackInfo;
 import android.os.Handler;
 import android.os.Looper;
@@ -30,6 +26,9 @@
 
 import com.android.media.subtitle.SubtitleTrack.RenderingWidget;
 
+import java.util.Locale;
+import java.util.Vector;
+
 // Note: This is forked from android.media.SubtitleController since P
 /**
  * The subtitle controller provides the architecture to display subtitles for a
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index d7be549..f75b75e 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -31,13 +31,13 @@
 import android.media.MediaMetadata2;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
-import android.media.SessionCommand2;
-import android.media.SessionCommandGroup2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
 import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
 import android.media.update.MediaBrowser2Provider;
@@ -59,11 +59,12 @@
 import android.media.update.VolumeProvider2Provider;
 import android.os.Bundle;
 import android.os.IInterface;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import androidx.annotation.Nullable;
+
 import com.android.media.IMediaController2;
 import com.android.media.MediaBrowser2Impl;
 import com.android.media.MediaController2Impl;
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
index ad8bb48..dc5e5e2 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
@@ -18,21 +18,21 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.XmlResourceParser;
-import android.support.annotation.GuardedBy;
-import android.support.v4.widget.Space;
-import android.support.v7.widget.ButtonBarLayout;
 import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.GuardedBy;
+import androidx.appcompat.widget.ButtonBarLayout;
+import androidx.legacy.widget.Space;
+
 import com.android.support.mediarouter.app.MediaRouteButton;
 import com.android.support.mediarouter.app.MediaRouteExpandCollapseButton;
 import com.android.support.mediarouter.app.MediaRouteVolumeSlider;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java
index d3e8d47..98c0d17 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java
@@ -19,11 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.support.v4.view.ActionProvider;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.core.view.ActionProvider;
+
 import com.android.support.mediarouter.media.MediaRouteSelector;
 import com.android.support.mediarouter.media.MediaRouter;
 
@@ -48,7 +49,7 @@
  * <h3>Prerequisites</h3>
  * <p>
  * To use the media route action provider, the activity must be a subclass of
- * {@link AppCompatActivity} from the <code>android.support.v7.appcompat</code>
+ * {@link AppCompatActivity} from the <code>androidx.appcompat.appcompat</code>
  * support library.  Refer to support library documentation for details.
  * </p>
  *
@@ -65,7 +66,7 @@
  *     &lt;item android:id="@+id/media_route_menu_item"
  *         android:title="@string/media_route_menu_title"
  *         app:showAsAction="always"
- *         app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/>
+ *         app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
  * &lt;/menu>
  * </pre><p>
  * Then configure the menu and set the route selector for the chooser.
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
index fde8a63..e82fcb9 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
@@ -28,14 +28,15 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v7.widget.TooltipCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.SoundEffectConstants;
 import android.view.View;
 
+import androidx.appcompat.widget.TooltipCompat;
+import androidx.core.graphics.drawable.DrawableCompat;
+
 import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
 import com.android.support.mediarouter.media.MediaRouteSelector;
@@ -70,7 +71,7 @@
  * <h3>Prerequisites</h3>
  * <p>
  * To use the media route button, the activity must be a subclass of
- * {@link FragmentActivity} from the <code>android.support.v4</code>
+ * {@link FragmentActivity} from the <code>androidx.core./code>
  * support library.  Refer to support library documentation for details.
  * </p>
  *
@@ -81,9 +82,9 @@
     private static final String TAG = "MediaRouteButton";
 
     private static final String CHOOSER_FRAGMENT_TAG =
-            "android.support.v7.mediarouter:MediaRouteChooserDialogFragment";
+            "androidx.mediarouter.media.outer:MediaRouteChooserDialogFragment";
     private static final String CONTROLLER_FRAGMENT_TAG =
-            "android.support.v7.mediarouter:MediaRouteControllerDialogFragment";
+            "androidx.mediarouter.media.outer:MediaRouteControllerDialogFragment";
 
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
index cac64d9..f24028a 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
@@ -16,13 +16,14 @@
 
 package com.android.support.mediarouter.app;
 
-import static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED;
-import static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
+import static com.android.support.mediarouter.media.MediaRouter.RouteInfo
+        .CONNECTION_STATE_CONNECTED;
+import static com.android.support.mediarouter.media.MediaRouter.RouteInfo
+        .CONNECTION_STATE_CONNECTING;
 
 import android.annotation.NonNull;
 import android.app.Dialog;
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -30,12 +31,10 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
-import android.support.v7.app.AppCompatDialog;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
index 060cfca..f6c1d2f 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
@@ -40,9 +40,6 @@
 import android.support.v4.media.session.MediaControllerCompat;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.media.session.PlaybackStateCompat;
-import android.support.v4.util.ObjectsCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v7.graphics.Palette;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -72,11 +69,15 @@
 import android.widget.SeekBar;
 import android.widget.TextView;
 
+import androidx.core.util.ObjectsCompat;
+import androidx.core.view.accessibility.AccessibilityEventCompat;
+import androidx.palette.graphics.Palette;
+
 import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
+import com.android.support.mediarouter.app.OverlayListView.OverlayObject;
 import com.android.support.mediarouter.media.MediaRouteSelector;
 import com.android.support.mediarouter.media.MediaRouter;
-import com.android.support.mediarouter.app.OverlayListView.OverlayObject;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java
index a9eaf39..b5ee63e 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java
@@ -16,7 +16,7 @@
 
 package com.android.support.mediarouter.app;
 
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
 
 /**
  * The media route dialog factory is responsible for creating the media route
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java
index 02ee118..52aecd88 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java
@@ -17,10 +17,11 @@
 package com.android.support.mediarouter.app;
 
 import android.os.Bundle;
-import android.support.v4.app.Fragment;
 
-import com.android.support.mediarouter.media.MediaRouter;
+import androidx.fragment.app.Fragment;
+
 import com.android.support.mediarouter.media.MediaRouteSelector;
+import com.android.support.mediarouter.media.MediaRouter;
 
 /**
  * Media route discovery fragment.
@@ -34,7 +35,7 @@
  * provide the {@link MediaRouter} callback to register.
  * </p><p>
  * Note that the discovery callback makes the application be connected with all the
- * {@link android.support.v7.media.MediaRouteProviderService media route provider services}
+ * {@link androidx.mediarouter.media.MediaRouteProviderService media route provider services}
  * while it is registered.
  * </p>
  */
@@ -114,7 +115,7 @@
     }
 
     /**
-     * Called to create the {@link android.support.v7.media.MediaRouter.Callback callback}
+     * Called to create the {@link androidx.mediarouter.media.MediaRouter.Callback callback}
      * that will be registered.
      * <p>
      * The default callback does nothing.  The application may override this method to
@@ -129,7 +130,7 @@
 
     /**
      * Called to prepare the callback flags that will be used when the
-     * {@link android.support.v7.media.MediaRouter.Callback callback} is registered.
+     * {@link androidx.mediarouter.media.MediaRouter.Callback callback} is registered.
      * <p>
      * The default implementation returns {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}.
      * </p>
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
index 6a0a95a..dcca6a0 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
@@ -21,7 +21,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.AnimationDrawable;
-import android.support.v4.content.ContextCompat;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageButton;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
index 63f042f..b4bf8d1 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
@@ -19,12 +19,13 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.support.annotation.IntDef;
-import android.support.v4.graphics.ColorUtils;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 
+import androidx.annotation.IntDef;
+import androidx.core.graphics.ColorUtils;
+
 import com.android.media.update.R;
 
 import java.lang.annotation.Retention;
@@ -170,7 +171,7 @@
     private static boolean isLightTheme(Context context) {
         TypedValue value = new TypedValue();
         // TODO(sungsoo): Switch to com.android.internal.R.attr.isLightTheme
-        return context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.isLightTheme,
+        return context.getTheme().resolveAttribute(androidx.appcompat.R.attr.isLightTheme,
                 value, true) && value.data != 0;
     }
 
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java
index f8539bd..5a0bc95 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java
@@ -20,7 +20,6 @@
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Handler;
-import android.support.annotation.RequiresApi;
 import android.util.Log;
 import android.view.Display;
 
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java
index 90ea2d5..92f608b 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java
@@ -19,7 +19,8 @@
 import android.app.PendingIntent;
 import android.os.Bundle;
 import android.os.SystemClock;
-import android.support.v4.util.TimeUtils;
+
+import androidx.core.util.TimeUtils;
 
 /**
  * Describes the playback status of a media item.
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java
index 91a2e1a..7ea328c 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java
@@ -23,7 +23,8 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
-import android.support.v4.util.ObjectsCompat;
+
+import androidx.core.util.ObjectsCompat;
 
 import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback;
 
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java
index 43cde10..a186fee 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java
@@ -29,12 +29,14 @@
         .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_ROUTE_CONTROL_REQUEST;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .CLIENT_MSG_SELECT_ROUTE;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_SET_DISCOVERY_REQUEST;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_SET_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .CLIENT_MSG_UNREGISTER;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_UNSELECT_ROUTE;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
@@ -51,9 +53,12 @@
         .SERVICE_MSG_GENERIC_FAILURE;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .SERVICE_MSG_GENERIC_SUCCESS;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_CURRENT;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .SERVICE_MSG_REGISTERED;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .SERVICE_VERSION_CURRENT;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .isValidRemoteMessenger;
 
 import android.app.Service;
 import android.content.Intent;
@@ -65,11 +70,12 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.util.ObjectsCompat;
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.annotation.VisibleForTesting;
+import androidx.core.util.ObjectsCompat;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java
index 5669b19..f20dcc0 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java
@@ -17,8 +17,9 @@
 
 import android.content.IntentFilter;
 import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java
index db0052e..4b56b19 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java
@@ -33,15 +33,16 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.support.v4.app.ActivityManagerCompat;
-import android.support.v4.hardware.display.DisplayManagerCompat;
-import android.support.v4.media.VolumeProviderCompat;
 import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.util.Pair;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
 
+import androidx.core.app.ActivityManagerCompat;
+import androidx.core.hardware.display.DisplayManagerCompat;
+import androidx.core.util.Pair;
+import androidx.media.VolumeProviderCompat;
+
 import com.android.support.mediarouter.media.MediaRouteProvider.ProviderMetadata;
 import com.android.support.mediarouter.media.MediaRouteProvider.RouteController;
 
@@ -81,13 +82,13 @@
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
+     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
      * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route
      * was unselected is unknown.
      */
     public static final int UNSELECT_REASON_UNKNOWN = 0;
     /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
+     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
      * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
      * the disconnect button to disconnect and keep playing.
      * <p>
@@ -96,13 +97,13 @@
      */
     public static final int UNSELECT_REASON_DISCONNECTED = 1;
     /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
+     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
      * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
      * the stop casting button.
      */
     public static final int UNSELECT_REASON_STOPPED = 2;
     /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
+     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
      * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected
      * a different route.
      */
@@ -174,7 +175,7 @@
      * Applications should typically add a callback using this flag in the
      * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart}
      * method and remove it in the {@link android.app.Activity#onStop onStop} method.
-     * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may
+     * The {@link androidx.mediarouter.app.MediaRouteDiscoveryFragment} fragment may
      * also be used for this purpose.
      * </p><p class="note">
      * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag
@@ -182,7 +183,7 @@
      * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
      * </p>
      *
-     * @see android.support.v7.app.MediaRouteDiscoveryFragment
+     * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment
      */
     public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
 
@@ -197,7 +198,7 @@
      * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
      * </p>
      *
-     * @see android.support.v7.app.MediaRouteDiscoveryFragment
+     * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment
      */
     public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3;
 
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java
index 3206596..0e7514c 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java
@@ -19,7 +19,8 @@
 import android.app.PendingIntent;
 import android.os.Bundle;
 import android.os.SystemClock;
-import android.support.v4.util.TimeUtils;
+
+import androidx.core.util.TimeUtils;
 
 /**
  * Describes the playback status of a media session.
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java
index 98e4e28..eacf1c8 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java
@@ -29,17 +29,20 @@
         .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_ROUTE_CONTROL_REQUEST;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .CLIENT_MSG_SELECT_ROUTE;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_SET_DISCOVERY_REQUEST;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_SET_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .CLIENT_MSG_UNREGISTER;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_UNSELECT_ROUTE;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .CLIENT_MSG_UPDATE_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_VERSION_CURRENT;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .CLIENT_VERSION_CURRENT;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .SERVICE_MSG_CONTROL_REQUEST_FAILED;
@@ -51,9 +54,11 @@
         .SERVICE_MSG_GENERIC_FAILURE;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
         .SERVICE_MSG_GENERIC_SUCCESS;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .SERVICE_MSG_REGISTERED;
 import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_1;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.isValidRemoteMessenger;
+import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
+        .isValidRemoteMessenger;
 
 import android.annotation.NonNull;
 import android.content.ComponentName;
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java
index 826449b..65c5518 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Build;
-import android.support.annotation.RequiresApi;
 
 import java.lang.ref.WeakReference;
 
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java
index f6e1497..e76564e 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java
@@ -22,9 +22,10 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.v4.util.ObjectsCompat;
 import android.util.Log;
 
+import androidx.core.util.ObjectsCompat;
+
 /**
  * A helper class for playing media on remote routes using the remote playback protocol
  * defined by {@link MediaControlIntent}.
@@ -867,11 +868,11 @@
 
     private final class ActionReceiver extends BroadcastReceiver {
         public static final String ACTION_ITEM_STATUS_CHANGED =
-                "android.support.v7.media.actions.ACTION_ITEM_STATUS_CHANGED";
+                "androidx.mediarouter.media.actions.ACTION_ITEM_STATUS_CHANGED";
         public static final String ACTION_SESSION_STATUS_CHANGED =
-                "android.support.v7.media.actions.ACTION_SESSION_STATUS_CHANGED";
+                "androidx.mediarouter.media.actions.ACTION_SESSION_STATUS_CHANGED";
         public static final String ACTION_MESSAGE_RECEIVED =
-                "android.support.v7.media.actions.ACTION_MESSAGE_RECEIVED";
+                "androidx.mediarouter.media.actions.ACTION_MESSAGE_RECEIVED";
 
         ActionReceiver() {
         }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
index a38491f..53901a4 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
@@ -24,7 +24,6 @@
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.os.Build;
-import android.support.annotation.RequiresApi;
 import android.view.Display;
 
 import com.android.media.update.ApiHelper;
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 3aff150..ad85af4 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -20,15 +20,13 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.media.MediaMetadata;
+import android.media.SessionToken2;
 import android.media.session.MediaController;
 import android.media.session.PlaybackState;
-import android.media.SessionToken2;
 import android.media.update.MediaControlView2Provider;
 import android.media.update.ViewGroupProvider;
 import android.os.Bundle;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -36,27 +34,28 @@
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
-import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.MediaControlView2;
-import android.widget.ProgressBar;
 import android.widget.PopupWindow;
+import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
 import com.android.support.mediarouter.app.MediaRouteButton;
-import com.android.support.mediarouter.media.MediaRouter;
 import com.android.support.mediarouter.media.MediaRouteSelector;
+import com.android.support.mediarouter.media.MediaRouter;
 
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Formatter;
 import java.util.List;
 import java.util.Locale;
diff --git a/packages/MediaComponents/src/com/android/widget/SubtitleView.java b/packages/MediaComponents/src/com/android/widget/SubtitleView.java
index 67b2cd1..db0ae33 100644
--- a/packages/MediaComponents/src/com/android/widget/SubtitleView.java
+++ b/packages/MediaComponents/src/com/android/widget/SubtitleView.java
@@ -19,10 +19,11 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.os.Looper;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
 import com.android.media.subtitle.SubtitleController.Anchor;
 import com.android.media.subtitle.SubtitleTrack.RenderingWidget;
 
diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
index fc92e85..c9869c0 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
@@ -16,17 +16,18 @@
 
 package com.android.widget;
 
+import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.media.MediaPlayer2;
-import android.support.annotation.NonNull;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
 
-import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW;
+import androidx.annotation.NonNull;
 
 class VideoSurfaceView extends SurfaceView implements VideoViewInterface, SurfaceHolder.Callback {
     private static final String TAG = "VideoSurfaceView";
diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
index 024a3aa..40fb046 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
@@ -16,18 +16,19 @@
 
 package com.android.widget;
 
+import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW;
+
 import android.content.Context;
 import android.graphics.SurfaceTexture;
 import android.media.MediaPlayer2;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Surface;
 import android.view.TextureView;
 import android.view.View;
 
-import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(26)
 class VideoTextureView extends TextureView
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 97279d6..ffb145a 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -28,30 +28,29 @@
 import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.media.DataSourceDesc;
+import android.media.MediaItem2;
 import android.media.MediaMetadata;
+import android.media.MediaMetadata2;
+import android.media.MediaMetadataRetriever;
 import android.media.MediaPlayer2;
 import android.media.MediaPlayer2.MediaPlayer2EventCallback;
 import android.media.MediaPlayer2.OnSubtitleDataListener;
 import android.media.MediaPlayer2Impl;
-import android.media.SubtitleData;
-import android.media.MediaItem2;
-import android.media.MediaMetadata2;
-import android.media.MediaMetadataRetriever;
 import android.media.Metadata;
 import android.media.PlaybackParams;
+import android.media.SessionToken2;
+import android.media.SubtitleData;
 import android.media.TimedText;
 import android.media.session.MediaController;
 import android.media.session.MediaController.PlaybackInfo;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
-import android.media.SessionToken2;
 import android.media.update.VideoView2Provider;
 import android.media.update.ViewGroupProvider;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.ResultReceiver;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -66,6 +65,8 @@
 import android.widget.TextView;
 import android.widget.VideoView2;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.graphics.palette.Palette;
 import com.android.media.RoutePlayer;
 import com.android.media.subtitle.ClosedCaptionRenderer;
@@ -73,10 +74,10 @@
 import com.android.media.subtitle.SubtitleTrack;
 import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
-import com.android.support.mediarouter.media.MediaItemStatus;
 import com.android.support.mediarouter.media.MediaControlIntent;
-import com.android.support.mediarouter.media.MediaRouter;
+import com.android.support.mediarouter.media.MediaItemStatus;
 import com.android.support.mediarouter.media.MediaRouteSelector;
+import com.android.support.mediarouter.media.MediaRouter;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 7419e64..c0aa477 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -2,24 +2,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-    ServiceUtilities.cpp
-
-# FIXME Move this library to frameworks/native
-LOCAL_MODULE := libserviceutility
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libutils \
-    liblog \
-    libbinder
-
-LOCAL_CFLAGS := -Wall -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
 LOCAL_SRC_FILES:=               \
     AudioFlinger.cpp            \
     Threads.cpp                 \
@@ -31,7 +13,8 @@
     PatchPanel.cpp              \
     StateQueue.cpp              \
     BufLog.cpp                  \
-    TypedLogger.cpp
+    TypedLogger.cpp             \
+    NBAIO_Tee.cpp               \
 
 LOCAL_C_INCLUDES := \
     frameworks/av/services/audiopolicy \
@@ -53,13 +36,13 @@
     libnbaio \
     libnblog \
     libpowermanager \
-    libserviceutility \
     libmediautils \
     libmemunreachable \
     libmedia_helper
 
 LOCAL_STATIC_LIBRARIES := \
     libcpustats \
+    libsndfile \
 
 LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
 
@@ -82,6 +65,7 @@
 LOCAL_CFLAGS += -fvisibility=hidden
 
 LOCAL_CFLAGS += -Werror -Wall
+LOCAL_SANITIZE := integer_overflow
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index bdd39c6..13477f3 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -28,7 +28,6 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <cutils/multiuser.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include <binder/Parcel.h>
@@ -47,7 +46,7 @@
 #include <system/audio.h>
 
 #include "AudioFlinger.h"
-#include "ServiceUtilities.h"
+#include "NBAIO_Tee.h"
 
 #include <media/AudioResamplerPublic.h>
 
@@ -56,7 +55,6 @@
 #include <system/audio_effects/effect_aec.h>
 
 #include <audio_utils/primitives.h>
-#include <audio_utils/string.h>
 
 #include <powermanager/PowerManager.h>
 
@@ -66,6 +64,7 @@
 #include <media/nbaio/PipeReader.h>
 #include <media/AudioParameter.h>
 #include <mediautils/BatteryNotifier.h>
+#include <mediautils/ServiceUtilities.h>
 #include <private/android_filesystem_config.h>
 
 //#define BUFLOG_NDEBUG 0
@@ -100,17 +99,6 @@
 
 uint32_t AudioFlinger::mScreenState;
 
-
-#ifdef TEE_SINK
-bool AudioFlinger::mTeeSinkInputEnabled = false;
-bool AudioFlinger::mTeeSinkOutputEnabled = false;
-bool AudioFlinger::mTeeSinkTrackEnabled = false;
-
-size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault;
-size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
-size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
-#endif
-
 // In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
 // we define a minimum time during which a global effect is considered enabled.
 static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
@@ -160,6 +148,7 @@
       mTotalMemory(0),
       mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes),
       mGlobalEffectEnableTime(0),
+      mPatchPanel(this),
       mSystemReady(false)
 {
     // unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum
@@ -168,7 +157,6 @@
         mNextUniqueIds[use] = AUDIO_UNIQUE_ID_USE_MAX;
     }
 
-    getpid_cached = getpid();
     const bool doLog = property_get_bool("ro.test_harness", false);
     if (doLog) {
         mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters",
@@ -185,27 +173,6 @@
     mEffectsFactoryHal = EffectsFactoryHalInterface::create();
 
     mMediaLogNotifier->run("MediaLogNotifier");
-
-#ifdef TEE_SINK
-    char value[PROPERTY_VALUE_MAX];
-    (void) property_get("ro.debuggable", value, "0");
-    int debuggable = atoi(value);
-    int teeEnabled = 0;
-    if (debuggable) {
-        (void) property_get("af.tee", value, "0");
-        teeEnabled = atoi(value);
-    }
-    // FIXME symbolic constants here
-    if (teeEnabled & 1) {
-        mTeeSinkInputEnabled = true;
-    }
-    if (teeEnabled & 2) {
-        mTeeSinkOutputEnabled = true;
-    }
-    if (teeEnabled & 4) {
-        mTeeSinkTrackEnabled = true;
-    }
-#endif
 }
 
 void AudioFlinger::onFirstRef()
@@ -226,8 +193,6 @@
         }
     }
 
-    mPatchPanel = new PatchPanel(this);
-
     mMode = AUDIO_MODE_NORMAL;
 
     gAudioFlinger = this;
@@ -534,12 +499,7 @@
             dev->dump(fd);
         }
 
-#ifdef TEE_SINK
-        // dump the serially shared record tee sink
-        if (mRecordTeeSource != 0) {
-            dumpTee(fd, mRecordTeeSource, AUDIO_IO_HANDLE_NONE, 'C');
-        }
-#endif
+        mPatchPanel.dump(fd);
 
         BUFLOG_RESET;
 
@@ -547,6 +507,10 @@
             mLock.unlock();
         }
 
+#ifdef TEE_SINK
+        // NBAIO_Tee dump is safe to call outside of AF lock.
+        NBAIO_Tee::dumpAll(fd, "_DUMP");
+#endif
         // append a copy of media.log here by forwarding fd to it, but don't attempt
         // to lookup the service if it's not running, as it will block for a second
         if (sMediaLogServiceAsBinder != 0) {
@@ -666,7 +630,7 @@
     bool updatePid = (input.clientInfo.clientPid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
     uid_t clientUid = input.clientInfo.clientUid;
-    if (!isTrustedCallingUid(callingUid)) {
+    if (!isAudioServerOrMediaServerUid(callingUid)) {
         ALOGW_IF(clientUid != callingUid,
                 "%s uid %d tried to pass itself off as %d",
                 __FUNCTION__, callingUid, clientUid);
@@ -1078,9 +1042,9 @@
         ALOGW("checkStreamType() invalid stream %d", stream);
         return BAD_VALUE;
     }
-    pid_t caller = IPCThreadState::self()->getCallingPid();
-    if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && caller != getpid_cached) {
-        ALOGW("checkStreamType() pid %d cannot use internal stream type %d", caller, stream);
+    const uid_t callerUid = IPCThreadState::self()->getCallingUid();
+    if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && !isAudioServerUid(callerUid)) {
+        ALOGW("checkStreamType() uid %d cannot use internal stream type %d", callerUid, stream);
         return PERMISSION_DENIED;
     }
 
@@ -1200,9 +1164,8 @@
         String8(AudioParameter::keyStreamSupportedSamplingRates),
     };
 
-    // multiuser friendly app ID check for requests coming from audioserver
-    if (multiuser_get_app_id(callingUid) == AID_AUDIOSERVER) {
-        return;
+    if (isAudioServerUid(callingUid)) {
+        return; // no need to filter if audioserver.
     }
 
     AudioParameter param = AudioParameter(keyValuePairs);
@@ -1636,7 +1599,7 @@
     bool updatePid = (input.clientInfo.clientPid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
     uid_t clientUid = input.clientInfo.clientUid;
-    if (!isTrustedCallingUid(callingUid)) {
+    if (!isAudioServerOrMediaServerUid(callingUid)) {
         ALOGW_IF(clientUid != callingUid,
                 "%s uid %d tried to pass itself off as %d",
                 __FUNCTION__, callingUid, clientUid);
@@ -1885,7 +1848,7 @@
 status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory)
 {
     uid_t uid = IPCThreadState::self()->getCallingUid();
-    if (uid != AID_SYSTEM) {
+    if (!isAudioServerOrSystemServerUid(uid)) {
         return PERMISSION_DENIED;
     }
     Mutex::Autolock _l(mLock);
@@ -1930,6 +1893,28 @@
     return mClientSharedHeapSize;
 }
 
+status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config)
+{
+    ALOGV(__func__);
+
+    audio_module_handle_t module;
+    if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+        module = config->ext.device.hw_module;
+    } else {
+        module = config->ext.mix.hw_module;
+    }
+
+    Mutex::Autolock _l(mLock);
+    ssize_t index = mAudioHwDevs.indexOfKey(module);
+    if (index < 0) {
+        ALOGW("%s() bad hw module %d", __func__, module);
+        return BAD_VALUE;
+    }
+
+    AudioHwDevice *audioHwDevice = mAudioHwDevs.valueAt(index);
+    return audioHwDevice->hwDevice()->setAudioPortConfig(config);
+}
+
 audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId)
 {
     Mutex::Autolock _l(mLock);
@@ -2274,7 +2259,7 @@
     delete out;
 }
 
-void AudioFlinger::closeOutputInternal_l(const sp<PlaybackThread>& thread)
+void AudioFlinger::closeThreadInternal_l(const sp<PlaybackThread>& thread)
 {
     mPlaybackThreads.removeItem(thread->mId);
     thread->exit();
@@ -2407,55 +2392,6 @@
                     thread.get());
             return thread;
         } else {
-#ifdef TEE_SINK
-            // Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
-            // or (re-)create if current Pipe is idle and does not match the new format
-            sp<NBAIO_Sink> teeSink;
-            enum {
-                TEE_SINK_NO,    // don't copy input
-                TEE_SINK_NEW,   // copy input using a new pipe
-                TEE_SINK_OLD,   // copy input using an existing pipe
-            } kind;
-            NBAIO_Format format = Format_from_SR_C(halconfig.sample_rate,
-                    audio_channel_count_from_in_mask(halconfig.channel_mask), halconfig.format);
-            if (!mTeeSinkInputEnabled) {
-                kind = TEE_SINK_NO;
-            } else if (!Format_isValid(format)) {
-                kind = TEE_SINK_NO;
-            } else if (mRecordTeeSink == 0) {
-                kind = TEE_SINK_NEW;
-            } else if (mRecordTeeSink->getStrongCount() != 1) {
-                kind = TEE_SINK_NO;
-            } else if (Format_isEqual(format, mRecordTeeSink->format())) {
-                kind = TEE_SINK_OLD;
-            } else {
-                kind = TEE_SINK_NEW;
-            }
-            switch (kind) {
-            case TEE_SINK_NEW: {
-                Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
-                size_t numCounterOffers = 0;
-                const NBAIO_Format offers[1] = {format};
-                ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
-                ALOG_ASSERT(index == 0);
-                PipeReader *pipeReader = new PipeReader(*pipe);
-                numCounterOffers = 0;
-                index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
-                ALOG_ASSERT(index == 0);
-                mRecordTeeSink = pipe;
-                mRecordTeeSource = pipeReader;
-                teeSink = pipe;
-                }
-                break;
-            case TEE_SINK_OLD:
-                teeSink = mRecordTeeSink;
-                break;
-            case TEE_SINK_NO:
-            default:
-                break;
-            }
-#endif
-
             // Start record thread
             // RecordThread requires both input and output device indication to forward to audio
             // pre processing modules
@@ -2465,9 +2401,6 @@
                                       primaryOutputDevice_l(),
                                       devices,
                                       mSystemReady
-#ifdef TEE_SINK
-                                      , teeSink
-#endif
                                       );
             mRecordThreads.add(*input, thread);
             ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get());
@@ -2567,7 +2500,7 @@
     delete in;
 }
 
-void AudioFlinger::closeInputInternal_l(const sp<RecordThread>& thread)
+void AudioFlinger::closeThreadInternal_l(const sp<RecordThread>& thread)
 {
     mRecordThreads.removeItem(thread->mId);
     closeInputFinish(thread);
@@ -2605,7 +2538,8 @@
     Mutex::Autolock _l(mLock);
     pid_t caller = IPCThreadState::self()->getCallingPid();
     ALOGV("acquiring %d from %d, for %d", audioSession, caller, pid);
-    if (pid != -1 && (caller == getpid_cached)) {
+    const uid_t callerUid = IPCThreadState::self()->getCallingUid();
+    if (pid != -1 && isAudioServerUid(callerUid)) { // check must match releaseAudioSessionId()
         caller = pid;
     }
 
@@ -2639,7 +2573,8 @@
     Mutex::Autolock _l(mLock);
     pid_t caller = IPCThreadState::self()->getCallingPid();
     ALOGV("releasing %d from %d for %d", audioSession, caller, pid);
-    if (pid != -1 && (caller == getpid_cached)) {
+    const uid_t callerUid = IPCThreadState::self()->getCallingUid();
+    if (pid != -1 && isAudioServerUid(callerUid)) { // check must match acquireAudioSessionId()
         caller = pid;
     }
     size_t num = mAudioSessionRefs.size();
@@ -2656,9 +2591,10 @@
             return;
         }
     }
-    // If the caller is mediaserver it is likely that the session being released was acquired
+    // If the caller is audioserver it is likely that the session being released was acquired
     // on behalf of a process not in notification clients and we ignore the warning.
-    ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
+    ALOGW_IF(!isAudioServerUid(callerUid),
+            "session id %d not found for pid %d", audioSession, caller);
 }
 
 bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession)
@@ -2966,7 +2902,7 @@
     effect_descriptor_t desc;
 
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (pid == -1 || !isTrustedCallingUid(callingUid)) {
+    if (pid == -1 || !isAudioServerOrMediaServerUid(callingUid)) {
         const pid_t callingPid = IPCThreadState::self()->getCallingPid();
         ALOGW_IF(pid != -1 && pid != callingPid,
                  "%s uid %d pid %d tried to pass itself off as pid %d",
@@ -2989,8 +2925,8 @@
     }
 
     // Session AUDIO_SESSION_OUTPUT_STAGE is reserved for output stage effects
-    // that can only be created by audio policy manager (running in same process)
-    if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid_cached != pid) {
+    // that can only be created by audio policy manager
+    if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && !isAudioServerUid(callingUid)) {
         lStatus = PERMISSION_DENIED;
         goto Exit;
     }
@@ -3370,136 +3306,6 @@
 }
 
 
-struct Entry {
-#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21
-    char mFileName[TEE_MAX_FILENAME];
-};
-
-int comparEntry(const void *p1, const void *p2)
-{
-    return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName);
-}
-
-#ifdef TEE_SINK
-void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix)
-{
-    NBAIO_Source *teeSource = source.get();
-    if (teeSource != NULL) {
-        // .wav rotation
-        // There is a benign race condition if 2 threads call this simultaneously.
-        // They would both traverse the directory, but the result would simply be
-        // failures at unlink() which are ignored.  It's also unlikely since
-        // normally dumpsys is only done by bugreport or from the command line.
-        char teePath[PATH_MAX] = "/data/misc/audioserver";
-        size_t teePathLen = strlen(teePath);
-        DIR *dir = opendir(teePath);
-        teePath[teePathLen++] = '/';
-        if (dir != NULL) {
-#define TEE_MAX_SORT 20 // number of entries to sort
-#define TEE_MAX_KEEP 10 // number of entries to keep
-            struct Entry entries[TEE_MAX_SORT];
-            size_t entryCount = 0;
-            while (entryCount < TEE_MAX_SORT) {
-                errno = 0; // clear errno before readdir() to track potential errors.
-                const struct dirent *result = readdir(dir);
-                if (result == nullptr) {
-                    ALOGW_IF(errno != 0, "tee readdir() failure %s", strerror(errno));
-                    break;
-                }
-                // ignore non .wav file entries
-                const size_t nameLen = strlen(result->d_name);
-                if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME ||
-                        strcmp(&result->d_name[nameLen - 4], ".wav")) {
-                    continue;
-                }
-                (void)audio_utils_strlcpy(entries[entryCount++].mFileName, result->d_name);
-            }
-            (void) closedir(dir);
-            if (entryCount > TEE_MAX_KEEP) {
-                qsort(entries, entryCount, sizeof(Entry), comparEntry);
-                for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) {
-                    strcpy(&teePath[teePathLen], entries[i].mFileName);
-                    (void) unlink(teePath);
-                }
-            }
-        } else {
-            if (fd >= 0) {
-                dprintf(fd, "unable to rotate tees in %.*s: %s\n", (int) teePathLen, teePath,
-                        strerror(errno));
-            }
-        }
-        char teeTime[16];
-        struct timeval tv;
-        gettimeofday(&tv, NULL);
-        struct tm tm;
-        localtime_r(&tv.tv_sec, &tm);
-        strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
-        snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d_%c.wav", teeTime, id,
-                suffix);
-        // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
-        int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
-        if (teeFd >= 0) {
-            // FIXME use libsndfile
-            char wavHeader[44];
-            memcpy(wavHeader,
-                "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0",
-                sizeof(wavHeader));
-            NBAIO_Format format = teeSource->format();
-            unsigned channelCount = Format_channelCount(format);
-            uint32_t sampleRate = Format_sampleRate(format);
-            size_t frameSize = Format_frameSize(format);
-            wavHeader[22] = channelCount;       // number of channels
-            wavHeader[24] = sampleRate;         // sample rate
-            wavHeader[25] = sampleRate >> 8;
-            wavHeader[32] = frameSize;          // block alignment
-            wavHeader[33] = frameSize >> 8;
-            write(teeFd, wavHeader, sizeof(wavHeader));
-            size_t total = 0;
-            bool firstRead = true;
-#define TEE_SINK_READ 1024                      // frames per I/O operation
-            void *buffer = malloc(TEE_SINK_READ * frameSize);
-            for (;;) {
-                size_t count = TEE_SINK_READ;
-                ssize_t actual = teeSource->read(buffer, count);
-                bool wasFirstRead = firstRead;
-                firstRead = false;
-                if (actual <= 0) {
-                    if (actual == (ssize_t) OVERRUN && wasFirstRead) {
-                        continue;
-                    }
-                    break;
-                }
-                ALOG_ASSERT(actual <= (ssize_t)count);
-                write(teeFd, buffer, actual * frameSize);
-                total += actual;
-            }
-            free(buffer);
-            lseek(teeFd, (off_t) 4, SEEK_SET);
-            uint32_t temp = 44 + total * frameSize - 8;
-            // FIXME not big-endian safe
-            write(teeFd, &temp, sizeof(temp));
-            lseek(teeFd, (off_t) 40, SEEK_SET);
-            temp =  total * frameSize;
-            // FIXME not big-endian safe
-            write(teeFd, &temp, sizeof(temp));
-            close(teeFd);
-            // TODO Should create file with temporary name and then rename to final if non-empty.
-            if (total > 0) {
-                if (fd >= 0) {
-                    dprintf(fd, "tee copied to %s\n", teePath);
-                }
-            } else {
-                unlink(teePath);
-            }
-        } else {
-            if (fd >= 0) {
-                dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
-            }
-        }
-    }
-}
-#endif
-
 // ----------------------------------------------------------------------------
 
 status_t AudioFlinger::onTransact(
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 963a87d..a59c13e 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -71,6 +71,7 @@
 #include "AudioStreamOut.h"
 #include "SpdifStreamOut.h"
 #include "AudioHwDevice.h"
+#include "NBAIO_Tee.h"
 
 #include <powermanager/IPowerManager.h>
 
@@ -791,44 +792,16 @@
 
     // for use from destructor
     status_t    closeOutput_nonvirtual(audio_io_handle_t output);
-    void        closeOutputInternal_l(const sp<PlaybackThread>& thread);
+    void        closeThreadInternal_l(const sp<PlaybackThread>& thread);
     status_t    closeInput_nonvirtual(audio_io_handle_t input);
-    void        closeInputInternal_l(const sp<RecordThread>& thread);
+    void        closeThreadInternal_l(const sp<RecordThread>& thread);
     void        setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId);
 
     status_t    checkStreamType(audio_stream_type_t stream) const;
 
     void        filterReservedParameters(String8& keyValuePairs, uid_t callingUid);
 
-#ifdef TEE_SINK
-    // all record threads serially share a common tee sink, which is re-created on format change
-    sp<NBAIO_Sink>   mRecordTeeSink;
-    sp<NBAIO_Source> mRecordTeeSource;
-#endif
-
 public:
-
-#ifdef TEE_SINK
-    // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
-    static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix);
-
-    // whether tee sink is enabled by property
-    static bool mTeeSinkInputEnabled;
-    static bool mTeeSinkOutputEnabled;
-    static bool mTeeSinkTrackEnabled;
-
-    // runtime configured size of each tee sink pipe, in frames
-    static size_t mTeeSinkInputFrames;
-    static size_t mTeeSinkOutputFrames;
-    static size_t mTeeSinkTrackFrames;
-
-    // compile-time default size of tee sink pipes, in frames
-    // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
-    static const size_t kTeeSinkInputFramesDefault = 0x200000;
-    static const size_t kTeeSinkOutputFramesDefault = 0x200000;
-    static const size_t kTeeSinkTrackFramesDefault = 0x200000;
-#endif
-
     // These methods read variables atomically without mLock,
     // though the variables are updated with mLock.
     bool    isLowRamDevice() const { return mIsLowRamDevice; }
@@ -843,7 +816,8 @@
 
     nsecs_t mGlobalEffectEnableTime;  // when a global effect was last enabled
 
-    sp<PatchPanel> mPatchPanel;
+    // protected by mLock
+    PatchPanel mPatchPanel;
     sp<EffectsFactoryHalInterface> mEffectsFactoryHal;
 
     bool        mSystemReady;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 2047dfd..786c4af 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -31,9 +31,9 @@
 #include <media/AudioEffect.h>
 #include <media/audiohal/EffectHalInterface.h>
 #include <media/audiohal/EffectsFactoryHalInterface.h>
+#include <mediautils/ServiceUtilities.h>
 
 #include "AudioFlinger.h"
-#include "ServiceUtilities.h"
 
 // ----------------------------------------------------------------------------
 
@@ -1815,7 +1815,7 @@
     bool locked = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock);
 
     snprintf(buffer, size, "\t\t\t%5d    %5d  %3s    %3s  %5u  %5u\n",
-            (mClient == 0) ? getpid_cached : mClient->pid(),
+            (mClient == 0) ? getpid() : mClient->pid(),
             mPriority,
             mHasControl ? "yes" : "no",
             locked ? "yes" : "no",
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 79bb9fe..caeede9 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -47,7 +47,8 @@
 
 /*static*/ const FastMixerState FastMixer::sInitial;
 
-FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
+FastMixer::FastMixer(audio_io_handle_t parentIoHandle)
+    : FastThread("cycle_ms", "load_us"),
     // mFastTrackNames
     // mGenerations
     mOutputSink(NULL),
@@ -66,8 +67,11 @@
     mTotalNativeFramesWritten(0),
     // timestamp
     mNativeFramesWrittenButNotPresented(0),   // the = 0 is to silence the compiler
-    mMasterMono(false)
+    mMasterMono(false),
+    mThreadIoHandle(parentIoHandle)
 {
+    (void)mThreadIoHandle; // prevent unused warning, see C++17 [[maybe_unused]]
+
     // FIXME pass sInitial as parameter to base class constructor, and make it static local
     mPrevious = &sInitial;
     mCurrent = &sInitial;
@@ -220,6 +224,10 @@
         previousTrackMask = 0;
         mFastTracksGen = current->mFastTracksGen - 1;
         dumpState->mFrameCount = frameCount;
+#ifdef TEE_SINK
+        mTee.set(mFormat, NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD);
+        mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_F");
+#endif
     } else {
         previousTrackMask = previous->mTrackMask;
     }
@@ -446,10 +454,9 @@
                     frameCount * Format_channelCount(mFormat));
         }
         // if non-NULL, then duplicate write() to this non-blocking sink
-        NBAIO_Sink* teeSink;
-        if ((teeSink = current->mTeeSink) != NULL) {
-            (void) teeSink->write(buffer, frameCount);
-        }
+#ifdef TEE_SINK
+        mTee.write(buffer, frameCount);
+#endif
         // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
         //       but this code should be modified to handle both non-blocking and blocking sinks
         dumpState->mWriteSequence++;
@@ -482,6 +489,10 @@
                         timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
                 mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
                         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 =
+                        (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate;
             } else {
                 // HAL reported that more frames were presented than were written
                 mNativeFramesWrittenButNotPresented = 0;
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 235d23f..1c86d9a 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -22,6 +22,7 @@
 #include "StateQueue.h"
 #include "FastMixerState.h"
 #include "FastMixerDumpState.h"
+#include "NBAIO_Tee.h"
 
 namespace android {
 
@@ -32,7 +33,9 @@
 class FastMixer : public FastThread {
 
 public:
-            FastMixer();
+    /** FastMixer constructor takes as param the parent MixerThread's io handle (id)
+        for purposes of identification. */
+    explicit FastMixer(audio_io_handle_t threadIoHandle);
     virtual ~FastMixer();
 
             FastMixerStateQueue* sq();
@@ -87,6 +90,11 @@
     // accessed without lock between multiple threads.
     std::atomic_bool mMasterMono;
     std::atomic_int_fast64_t mBoottimeOffset;
+
+    const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes
+#ifdef TEE_SINK
+    NBAIO_Tee       mTee;
+#endif
 };  // class FastMixer
 
 }   // namespace android
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index 2e4fb8c..b3a2520 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -68,11 +68,11 @@
     dprintf(fd, "  FastMixer command=%s writeSequence=%u framesWritten=%u\n"
                 "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
                 "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
-                "            mixPeriod=%.2f ms\n",
+                "            mixPeriod=%.2f ms latency=%.2f ms\n",
                 FastMixerState::commandToString(mCommand), mWriteSequence, mFramesWritten,
                 mNumTracks, mWriteErrors, mUnderruns, mOverruns,
                 mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
-                mixPeriodSec * 1e3);
+                mixPeriodSec * 1e3, mLatencyMs);
 #ifdef FAST_THREAD_STATISTICS
     // find the interval of valid samples
     uint32_t bounds = mBounds;
diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h
index 8ef31d1..aed6bc5 100644
--- a/services/audioflinger/FastMixerDumpState.h
+++ b/services/audioflinger/FastMixerDumpState.h
@@ -66,6 +66,7 @@
 
     void dump(int fd) const;    // should only be called on a stable copy, not the original
 
+    double   mLatencyMs = 0.;   // measured latency, default of 0 if no valid timestamp read.
     uint32_t mWriteSequence;    // incremented before and after each write()
     uint32_t mFramesWritten;    // total number of frames written successfully
     uint32_t mNumTracks;        // total number of active fast tracks
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 36d8eef..b98842d 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -35,7 +35,7 @@
 FastMixerState::FastMixerState() : FastThreadState(),
     // mFastTracks
     mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
-    mFrameCount(0), mTeeSink(NULL)
+    mFrameCount(0)
 {
     int ok = pthread_once(&sMaxFastTracksOnce, sMaxFastTracksInit);
     if (ok != 0) {
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 2be1e91..c7fcbd8 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -77,9 +77,6 @@
         WRITE = 0x10,           // write to output sink
         MIX_WRITE = 0x18;       // mix tracks and write to output sink
 
-    // This might be a one-time configuration rather than per-state
-    NBAIO_Sink* mTeeSink;       // if non-NULL, then duplicate write()s to this non-blocking sink
-
     // never returns NULL; asserts if command is invalid
     static const char *commandToString(Command command);
 
diff --git a/services/audioflinger/NBAIO_Tee.cpp b/services/audioflinger/NBAIO_Tee.cpp
new file mode 100644
index 0000000..53083d5
--- /dev/null
+++ b/services/audioflinger/NBAIO_Tee.cpp
@@ -0,0 +1,517 @@
+/*
+ * 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 "NBAIO_Tee"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include <deque>
+#include <dirent.h>
+#include <future>
+#include <list>
+#include <vector>
+
+#include <audio_utils/format.h>
+#include <audio_utils/sndfile.h>
+#include <media/nbaio/PipeReader.h>
+
+#include "Configuration.h"
+#include "NBAIO_Tee.h"
+
+// Enabled with TEE_SINK in Configuration.h
+#ifdef TEE_SINK
+
+namespace android {
+
+/*
+ Tee filenames generated as follows:
+
+ "aftee_Date_ThreadId_C_reason.wav" RecordThread
+ "aftee_Date_ThreadId_M_reason.wav" MixerThread (Normal)
+ "aftee_Date_ThreadId_F_reason.wav" MixerThread (Fast)
+ "aftee_Date_ThreadId_TrackId_R_reason.wav" RecordTrack
+ "aftee_Date_ThreadId_TrackId_TrackName_T_reason.wav" PlaybackTrack
+
+ where Date = YYYYmmdd_HHMMSS_MSEC
+
+ where Reason = [ DTOR | DUMP | REMOVE ]
+
+ Examples:
+  aftee_20180424_153811_038_13_57_2_T_REMOVE.wav
+  aftee_20180424_153811_218_13_57_2_T_REMOVE.wav
+  aftee_20180424_153811_378_13_57_2_T_REMOVE.wav
+  aftee_20180424_153825_147_62_C_DUMP.wav
+  aftee_20180424_153825_148_62_59_R_DUMP.wav
+  aftee_20180424_153825_149_13_F_DUMP.wav
+  aftee_20180424_153842_125_62_59_R_REMOVE.wav
+  aftee_20180424_153842_168_62_C_DTOR.wav
+*/
+
+static constexpr char DEFAULT_PREFIX[] = "aftee_";
+static constexpr char DEFAULT_DIRECTORY[] = "/data/misc/audioserver";
+static constexpr size_t DEFAULT_THREADPOOL_SIZE = 8;
+
+/** AudioFileHandler manages temporary audio wav files with a least recently created
+    retention policy.
+
+    The temporary filenames are systematically generated. A common filename prefix,
+    storage directory, and concurrency pool are passed in on creating the object.
+
+    Temporary files are created by "create", which returns a filename generated by
+
+    prefix + 14 char date + suffix
+
+    TODO Move to audio_utils.
+    TODO Avoid pointing two AudioFileHandlers to the same directory and prefix
+    as we don't have a prefix specific lock file. */
+
+class AudioFileHandler {
+public:
+
+    AudioFileHandler(const std::string &prefix, const std::string &directory, size_t pool)
+        : mThreadPool(pool)
+        , mPrefix(prefix)
+    {
+        (void)setDirectory(directory);
+    }
+
+    /** returns filename of created audio file, else empty string on failure. */
+    std::string create(
+            std::function<ssize_t /* frames_read */
+                        (void * /* buffer */, size_t /* size_in_frames */)> reader,
+            uint32_t sampleRate,
+            uint32_t channelCount,
+            audio_format_t format,
+            const std::string &suffix);
+
+private:
+    /** sets the current directory. this is currently private to avoid confusion
+        when changing while pending operations are occurring (it's okay, but
+        weakly synchronized). */
+    status_t setDirectory(const std::string &directory);
+
+    /** cleans current directory and returns the directory name done. */
+    status_t clean(std::string *dir = nullptr);
+
+    /** creates an audio file from a reader functor passed in. */
+    status_t createInternal(
+            std::function<ssize_t /* frames_read */
+                        (void * /* buffer */, size_t /* size_in_frames */)> reader,
+            uint32_t sampleRate,
+            uint32_t channelCount,
+            audio_format_t format,
+            const std::string &filename);
+
+    static bool isDirectoryValid(const std::string &directory) {
+        return directory.size() > 0 && directory[0] == '/';
+    }
+
+    std::string generateFilename(const std::string &suffix) const {
+        char fileTime[sizeof("YYYYmmdd_HHMMSS_\0")];
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        struct tm tm;
+        localtime_r(&tv.tv_sec, &tm);
+        LOG_ALWAYS_FATAL_IF(strftime(fileTime, sizeof(fileTime), "%Y%m%d_%H%M%S_", &tm) == 0,
+            "incorrect fileTime buffer");
+        char msec[4];
+        (void)snprintf(msec, sizeof(msec), "%03d", (int)(tv.tv_usec / 1000));
+        return mPrefix + fileTime + msec + suffix + ".wav";
+    }
+
+    bool isManagedFilename(const char *name) {
+        constexpr size_t FILENAME_LEN_DATE = 4 + 2 + 2 // %Y%m%d%
+            + 1 + 2 + 2 + 2 // _H%M%S
+            + 1 + 3; //_MSEC
+        const size_t prefixLen = mPrefix.size();
+        const size_t nameLen = strlen(name);
+
+        // reject on size, prefix, and .wav
+        if (nameLen < prefixLen + FILENAME_LEN_DATE + 4 /* .wav */
+             || strncmp(name, mPrefix.c_str(), prefixLen) != 0
+             || strcmp(name + nameLen - 4, ".wav") != 0) {
+            return false;
+        }
+
+        // validate date portion
+        const char *date = name + prefixLen;
+        return std::all_of(date, date + 8, isdigit)
+            && date[8] == '_'
+            && std::all_of(date + 9, date + 15, isdigit)
+            && date[15] == '_'
+            && std::all_of(date + 16, date + 19, isdigit);
+    }
+
+    // yet another ThreadPool implementation.
+    class ThreadPool {
+    public:
+        ThreadPool(size_t size)
+            : mThreadPoolSize(size)
+        { }
+
+        /** launches task "name" with associated function "func".
+            if the threadpool is exhausted, it will launch on calling function */
+        status_t launch(const std::string &name, std::function<status_t()> func);
+
+    private:
+        std::mutex mLock;
+        std::list<std::pair<
+                std::string, std::future<status_t>>> mFutures; // GUARDED_BY(mLock)
+
+        const size_t mThreadPoolSize;
+    } mThreadPool;
+
+    const std::string mPrefix;
+    std::mutex mLock;
+    std::string mDirectory;         // GUARDED_BY(mLock)
+    std::deque<std::string> mFiles; // GUARDED_BY(mLock)  sorted list of files by creation time
+
+    static constexpr size_t FRAMES_PER_READ = 1024;
+    static constexpr size_t MAX_FILES_READ = 1024;
+    static constexpr size_t MAX_FILES_KEEP = 32;
+};
+
+/* static */
+void NBAIO_Tee::NBAIO_TeeImpl::dumpTee(
+        int fd, const NBAIO_SinkSource &sinkSource, const std::string &suffix)
+{
+    // Singleton. Constructed thread-safe on first call, never destroyed.
+    static AudioFileHandler audioFileHandler(
+            DEFAULT_PREFIX, DEFAULT_DIRECTORY, DEFAULT_THREADPOOL_SIZE);
+
+    auto &source = sinkSource.second;
+    if (source.get() == nullptr) {
+        return;
+    }
+
+    const NBAIO_Format format = source->format();
+    bool firstRead = true;
+    std::string filename = audioFileHandler.create(
+            // this functor must not hold references to stack
+            [firstRead, sinkSource] (void *buffer, size_t frames) mutable {
+                    auto &source = sinkSource.second;
+                    ssize_t actualRead = source->read(buffer, frames);
+                    if (actualRead == (ssize_t)OVERRUN && firstRead) {
+                        // recheck once
+                        actualRead = source->read(buffer, frames);
+                    }
+                    firstRead = false;
+                    return actualRead;
+                },
+            Format_sampleRate(format),
+            Format_channelCount(format),
+            format.mFormat,
+            suffix);
+
+    if (fd >= 0 && filename.size() > 0) {
+        dprintf(fd, "tee wrote to %s\n", filename.c_str());
+    }
+}
+
+/* static */
+NBAIO_Tee::NBAIO_TeeImpl::NBAIO_SinkSource NBAIO_Tee::NBAIO_TeeImpl::makeSinkSource(
+        const NBAIO_Format &format, size_t frames, bool *enabled)
+{
+    if (Format_isValid(format) && audio_is_linear_pcm(format.mFormat)) {
+        Pipe *pipe = new Pipe(frames, format);
+        size_t numCounterOffers = 0;
+        const NBAIO_Format offers[1] = {format};
+        ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+        if (index != 0) {
+            ALOGW("pipe failure to negotiate: %zd", index);
+            goto exit;
+        }
+        PipeReader *pipeReader = new PipeReader(*pipe);
+        numCounterOffers = 0;
+        index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+        if (index != 0) {
+            ALOGW("pipeReader failure to negotiate: %zd", index);
+            goto exit;
+        }
+        if (enabled != nullptr) *enabled = true;
+        return {pipe, pipeReader};
+    }
+exit:
+    if (enabled != nullptr) *enabled = false;
+    return {nullptr, nullptr};
+}
+
+std::string AudioFileHandler::create(
+        std::function<ssize_t /* frames_read */
+                    (void * /* buffer */, size_t /* size_in_frames */)> reader,
+        uint32_t sampleRate,
+        uint32_t channelCount,
+        audio_format_t format,
+        const std::string &suffix)
+{
+    const std::string filename = generateFilename(suffix);
+
+    if (mThreadPool.launch(std::string("create ") + filename,
+            [=]() { return createInternal(reader, sampleRate, channelCount, format, filename); })
+            == NO_ERROR) {
+        return filename;
+    }
+    return "";
+}
+
+status_t AudioFileHandler::setDirectory(const std::string &directory)
+{
+    if (!isDirectoryValid(directory)) return BAD_VALUE;
+
+    // TODO: consider using std::filesystem in C++17
+    DIR *dir = opendir(directory.c_str());
+
+    if (dir == nullptr) {
+        ALOGW("%s: cannot open directory %s", __func__, directory.c_str());
+        return BAD_VALUE;
+    }
+
+    size_t toRemove = 0;
+    decltype(mFiles) files;
+
+    while (files.size() < MAX_FILES_READ) {
+        errno = 0;
+        const struct dirent *result = readdir(dir);
+        if (result == nullptr) {
+            ALOGW_IF(errno != 0, "%s: readdir failure %s", __func__, strerror(errno));
+            break;
+        }
+        // is it a managed filename?
+        if (!isManagedFilename(result->d_name)) {
+            continue;
+        }
+        files.emplace_back(result->d_name);
+    }
+    (void)closedir(dir);
+
+    // OPTIMIZATION: we don't need to stat each file, the filenames names are
+    // already (roughly) ordered by creation date.  we use std::deque instead
+    // of std::set for faster insertion and sorting times.
+
+    if (files.size() > MAX_FILES_KEEP) {
+        // removed files can use a partition (no need to do a full sort).
+        toRemove = files.size() - MAX_FILES_KEEP;
+        std::nth_element(files.begin(), files.begin() + toRemove - 1, files.end());
+    }
+
+    // kept files must be sorted.
+    std::sort(files.begin() + toRemove, files.end());
+
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+
+        mDirectory = directory;
+        mFiles = std::move(files);
+    }
+
+    if (toRemove > 0) { // launch a clean in background.
+        (void)mThreadPool.launch(
+                std::string("cleaning ") + directory, [this]() { return clean(); });
+    }
+    return NO_ERROR;
+}
+
+status_t AudioFileHandler::clean(std::string *directory)
+{
+    std::vector<std::string> filesToRemove;
+    std::string dir;
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+
+        if (!isDirectoryValid(mDirectory)) return NO_INIT;
+
+        dir = mDirectory;
+        if (mFiles.size() > MAX_FILES_KEEP) {
+            size_t toRemove = mFiles.size() - MAX_FILES_KEEP;
+
+            // use move and erase to efficiently transfer std::string
+            std::move(mFiles.begin(),
+                    mFiles.begin() + toRemove,
+                    std::back_inserter(filesToRemove));
+            mFiles.erase(mFiles.begin(), mFiles.begin() + toRemove);
+        }
+    }
+
+    std::string dirp = dir + "/";
+    // remove files outside of lock for better concurrency.
+    for (const auto &file : filesToRemove) {
+        (void)unlink((dirp + file).c_str());
+    }
+
+    // return the directory if requested.
+    if (directory != nullptr) {
+        *directory = dir;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioFileHandler::ThreadPool::launch(
+        const std::string &name, std::function<status_t()> func)
+{
+    if (mThreadPoolSize > 1) {
+        std::lock_guard<std::mutex> _l(mLock);
+        if (mFutures.size() >= mThreadPoolSize) {
+            for (auto it = mFutures.begin(); it != mFutures.end();) {
+                const std::string &filename = it->first;
+                std::future<status_t> &future = it->second;
+                if (!future.valid() ||
+                        future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
+                    ALOGV("%s: future %s ready", __func__, filename.c_str());
+                    it = mFutures.erase(it);
+                } else {
+                    ALOGV("%s: future %s not ready", __func__, filename.c_str());
+                    ++it;
+                }
+            }
+        }
+        if (mFutures.size() < mThreadPoolSize) {
+            ALOGV("%s: deferred calling %s", __func__, name.c_str());
+            mFutures.emplace_back(name, std::async(std::launch::async, func));
+            return NO_ERROR;
+        }
+    }
+    ALOGV("%s: immediate calling %s", __func__, name.c_str());
+    return func();
+}
+
+status_t AudioFileHandler::createInternal(
+        std::function<ssize_t /* frames_read */
+                    (void * /* buffer */, size_t /* size_in_frames */)> reader,
+        uint32_t sampleRate,
+        uint32_t channelCount,
+        audio_format_t format,
+        const std::string &filename)
+{
+    // Attempt to choose the best matching file format.
+    // We can choose any sf_format
+    // but writeFormat must be one of 16, 32, float
+    // due to sf_writef compatibility.
+    int sf_format;
+    audio_format_t writeFormat;
+    switch (format) {
+    case AUDIO_FORMAT_PCM_8_BIT:
+    case AUDIO_FORMAT_PCM_16_BIT:
+        sf_format = SF_FORMAT_PCM_16;
+        writeFormat = AUDIO_FORMAT_PCM_16_BIT;
+        ALOGV("%s: %s using PCM_16 for format %#x", __func__, filename.c_str(), format);
+        break;
+    case AUDIO_FORMAT_PCM_8_24_BIT:
+    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+    case AUDIO_FORMAT_PCM_32_BIT:
+        sf_format = SF_FORMAT_PCM_32;
+        writeFormat = AUDIO_FORMAT_PCM_32_BIT;
+        ALOGV("%s: %s using PCM_32 for format %#x", __func__, filename.c_str(), format);
+        break;
+    case AUDIO_FORMAT_PCM_FLOAT:
+        sf_format = SF_FORMAT_FLOAT;
+        writeFormat = AUDIO_FORMAT_PCM_FLOAT;
+        ALOGV("%s: %s using PCM_FLOAT for format %#x", __func__, filename.c_str(), format);
+        break;
+    default:
+        // TODO:
+        // handle audio_has_proportional_frames() formats.
+        // handle compressed formats as single byte files.
+        return BAD_VALUE;
+    }
+
+    std::string directory;
+    status_t status = clean(&directory);
+    if (status != NO_ERROR) return status;
+    std::string dirPrefix = directory + "/";
+
+    const std::string path = dirPrefix + filename;
+
+    /* const */ SF_INFO info = {
+        .frames = 0,
+        .samplerate = (int)sampleRate,
+        .channels = (int)channelCount,
+        .format = SF_FORMAT_WAV | sf_format,
+    };
+    SNDFILE *sf = sf_open(path.c_str(), SFM_WRITE, &info);
+    if (sf == nullptr) {
+        return INVALID_OPERATION;
+    }
+
+    size_t total = 0;
+    void *buffer = malloc(FRAMES_PER_READ * std::max(
+            channelCount * audio_bytes_per_sample(writeFormat), //output framesize
+            channelCount * audio_bytes_per_sample(format))); // input framesize
+    if (buffer == nullptr) {
+        sf_close(sf);
+        return NO_MEMORY;
+    }
+
+    for (;;) {
+        const ssize_t actualRead = reader(buffer, FRAMES_PER_READ);
+        if (actualRead <= 0) {
+            break;
+        }
+
+        // Convert input format to writeFormat as needed.
+        if (format != writeFormat) {
+            memcpy_by_audio_format(
+                    buffer, writeFormat, buffer, format, actualRead * info.channels);
+        }
+
+        ssize_t reallyWritten;
+        switch (writeFormat) {
+        case AUDIO_FORMAT_PCM_16_BIT:
+            reallyWritten = sf_writef_short(sf, (const int16_t *)buffer, actualRead);
+            break;
+        case AUDIO_FORMAT_PCM_32_BIT:
+            reallyWritten = sf_writef_int(sf, (const int32_t *)buffer, actualRead);
+            break;
+        case AUDIO_FORMAT_PCM_FLOAT:
+            reallyWritten = sf_writef_float(sf, (const float *)buffer, actualRead);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("%s: %s writeFormat: %#x", __func__, filename.c_str(), writeFormat);
+            break;
+        }
+
+        if (reallyWritten < 0) {
+            ALOGW("%s: %s write error: %zd", __func__, filename.c_str(), reallyWritten);
+            break;
+        }
+        total += reallyWritten;
+        if (reallyWritten < actualRead) {
+            ALOGW("%s: %s write short count: %zd < %zd",
+                     __func__, filename.c_str(), reallyWritten, actualRead);
+            break;
+        }
+    }
+    sf_close(sf);
+    free(buffer);
+    if (total == 0) {
+        (void)unlink(path.c_str());
+        return NOT_ENOUGH_DATA;
+    }
+
+    // Success: add our name to managed files.
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+        // weak synchronization - only update mFiles if the directory hasn't changed.
+        if (mDirectory == directory) {
+            mFiles.emplace_back(filename);  // add to the end to preserve sort.
+        }
+    }
+    return NO_ERROR; // return full path
+}
+
+} // namespace android
+
+#endif // TEE_SINK
diff --git a/services/audioflinger/NBAIO_Tee.h b/services/audioflinger/NBAIO_Tee.h
new file mode 100644
index 0000000..fed8cc8
--- /dev/null
+++ b/services/audioflinger/NBAIO_Tee.h
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+// Enabled with TEE_SINK in Configuration.h
+#ifndef ANDROID_NBAIO_TEE_H
+#define ANDROID_NBAIO_TEE_H
+
+#ifdef TEE_SINK
+
+#include <atomic>
+#include <mutex>
+#include <set>
+
+#include <cutils/properties.h>
+#include <media/nbaio/NBAIO.h>
+
+namespace android {
+
+/**
+ * The NBAIO_Tee uses the NBAIO Pipe and PipeReader for nonblocking
+ * data collection, for eventual dump to log files.
+ * See https://source.android.com/devices/audio/debugging for how to
+ * enable by ro.debuggable and af.tee properties.
+ *
+ * The write() into the NBAIO_Tee is therefore nonblocking,
+ * but changing NBAIO_Tee formats with set() cannot be done during a write();
+ * usually the caller already implements this mutual exclusion.
+ *
+ * All other calls except set() vs write() may occur at any time.
+ *
+ * dump() disruption is minimized to the caller since system calls are executed
+ * in an asynchronous thread (when possible).
+ *
+ * Currently the NBAIO_Tee is "hardwired" for AudioFlinger support.
+ *
+ * Some AudioFlinger specific notes:
+ *
+ * 1) Tees capture only linear PCM data.
+ * 2) Tees without any data written are considered empty and do not generate
+ *    any output files.
+ * 2) Once a Tee dumps data, it is considered "emptied" and new data
+ *    needs to be written before another Tee file is generated.
+ * 3) Tee file format is
+ *    WAV integer PCM 16 bit for AUDIO_FORMAT_PCM_8_BIT, AUDIO_FORMAT_PCM_16_BIT.
+ *    WAV integer PCM 32 bit for AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED
+ *                               AUDIO_FORMAT_PCM_32_BIT.
+ *    WAV float PCM 32 bit for AUDIO_FORMAT_PCM_FLOAT.
+ *
+ * Input_Thread:
+ * 1) Capture buffer is teed when read from the HAL, before resampling for the AudioRecord
+ *    client.
+ *
+ * Output_Thread:
+ * 1) MixerThreads will tee at the FastMixer output (if it has one) or at the
+ *    NormalMixer output (if no FastMixer).
+ * 2) DuplicatingThreads do not tee any mixed data. Apply a tee on the downstream OutputTrack
+ *    or on the upstream playback Tracks.
+ * 3) DirectThreads and OffloadThreads do not tee any data. The upstream track
+ *    (if linear PCM format) may be teed to discover data.
+ * 4) MmapThreads are not supported.
+ *
+ * Tracks:
+ * 1) RecordTracks and playback Tracks tee as data is being written to or
+ *    read from the shared client-server track buffer by the associated Threads.
+ * 2) The mechanism is on the AudioBufferProvider release() so large static Track
+ *    playback may not show any Tee data depending on when it is released.
+ * 3) When a track becomes inactive, the Thread will trigger a dump.
+ */
+
+class NBAIO_Tee {
+public:
+    /* TEE_FLAG is used in set() and must match the flags for the af.tee property
+       given in https://source.android.com/devices/audio/debugging
+    */
+    enum TEE_FLAG {
+        TEE_FLAG_NONE = 0,
+        TEE_FLAG_INPUT_THREAD = (1 << 0),  // treat as a Tee for input (Capture) Threads
+        TEE_FLAG_OUTPUT_THREAD = (1 << 1), // treat as a Tee for output (Playback) Threads
+        TEE_FLAG_TRACK = (1 << 2),         // treat as a Tee for tracks (Record and Playback)
+    };
+
+    NBAIO_Tee()
+        : mTee(std::make_shared<NBAIO_TeeImpl>())
+    {
+        getRunningTees().add(mTee);
+    }
+
+    ~NBAIO_Tee() {
+        getRunningTees().remove(mTee);
+        dump(-1, "_DTOR"); // log any data remaining in Tee.
+    }
+
+    /**
+     * \brief set is used for deferred configuration of Tee.
+     *
+     *  May be called anytime except concurrently with write().
+     *
+     * \param format NBAIO_Format used to open NBAIO pipes
+     * \param flags (https://source.android.com/devices/audio/debugging)
+     *              - TEE_FLAG_NONE to bypass af.tee property checks (default);
+     *              - TEE_FLAG_INPUT_THREAD to check af.tee if input thread logging set;
+     *              - TEE_FLAG_OUTPUT_THREAD to check af.tee if output thread logging set;
+     *              - TEE_FLAG_TRACK to check af.tee if track logging set.
+     * \param frames number of frames to open the NBAIO pipe (set to 0 to use default).
+     *
+     * \return
+     *         - NO_ERROR on success (or format unchanged)
+     *         - BAD_VALUE if format or flags invalid.
+     *         - PERMISSION_DENIED if flags not allowed by af.tee
+     */
+
+    status_t set(const NBAIO_Format &format,
+            TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
+        return mTee->set(format, flags, frames);
+    }
+
+    status_t set(uint32_t sampleRate, uint32_t channelCount, audio_format_t format,
+            TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
+        return mTee->set(Format_from_SR_C(sampleRate, channelCount, format), flags, frames);
+    }
+
+    /**
+     * \brief write data to the tee.
+     *
+     * This call is lock free (as shared pointer and NBAIO is lock free);
+     * may be called simultaneous to all methods except set().
+     *
+     * \param buffer to write to pipe.
+     * \param frameCount in frames as specified by the format passed to set()
+     */
+
+    void write(const void *buffer, size_t frameCount) const {
+        mTee->write(buffer, frameCount);
+    }
+
+    /** sets Tee id string which identifies the generated file (should be unique). */
+    void setId(const std::string &id) const {
+        mTee->setId(id);
+    }
+
+    /**
+     * \brief dump the audio content written to the Tee.
+     *
+     * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
+     * \param reason string suffix to append to the generated file.
+     */
+    void dump(int fd, const std::string &reason = "") const {
+        mTee->dump(fd, reason);
+    }
+
+    /**
+     * \brief dump all Tees currently alive.
+     *
+     * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
+     * \param reason string suffix to append to the generated file.
+     */
+    static void dumpAll(int fd, const std::string &reason = "") {
+        getRunningTees().dump(fd, reason);
+    }
+
+private:
+
+    /** The underlying implementation of the Tee - the lifetime is through
+        a shared pointer so destruction of the NBAIO_Tee container may proceed
+        even though dumping is occurring. */
+    class NBAIO_TeeImpl {
+    public:
+        status_t set(const NBAIO_Format &format, TEE_FLAG flags, size_t frames) {
+            static const int teeConfig = property_get_bool("ro.debuggable", false)
+                   ? property_get_int32("af.tee", 0) : 0;
+
+            // check the type of Tee
+            const TEE_FLAG type = TEE_FLAG(
+                    flags & (TEE_FLAG_INPUT_THREAD | TEE_FLAG_OUTPUT_THREAD | TEE_FLAG_TRACK));
+
+            // parameter flags can't select multiple types.
+            if (__builtin_popcount(type) > 1) {
+                return BAD_VALUE;
+            }
+
+            // if type is set, we check to see if it is permitted by configuration.
+            if (type != 0 && (type & teeConfig) == 0) {
+                return PERMISSION_DENIED;
+            }
+
+            // determine number of frames for Tee
+            if (frames == 0) {
+                // TODO: consider varying frame count based on type.
+                frames = DEFAULT_TEE_FRAMES;
+            }
+
+            // TODO: should we check minimum number of frames?
+
+            // don't do anything if format and frames are the same.
+            if (Format_isEqual(format, mFormat) && frames == mFrames) {
+                return NO_ERROR;
+            }
+
+            bool enabled = false;
+            auto sinksource = makeSinkSource(format, frames, &enabled);
+
+            // enabled is set if makeSinkSource is successful.
+            // Note: as mentioned in NBAIO_Tee::set(), don't call set() while write() is
+            // ongoing.
+            if (enabled) {
+                std::lock_guard<std::mutex> _l(mLock);
+                mFlags = flags;
+                mFormat = format; // could get this from the Sink.
+                mFrames = frames;
+                mSinkSource = std::move(sinksource);
+                mEnabled.store(true);
+                return NO_ERROR;
+            }
+            return BAD_VALUE;
+        }
+
+        void setId(const std::string &id) {
+            std::lock_guard<std::mutex> _l(mLock);
+            mId = id;
+        }
+
+        void dump(int fd, const std::string &reason) {
+            if (!mDataReady.exchange(false)) return;
+            std::string suffix;
+            NBAIO_SinkSource sinkSource;
+            {
+                std::lock_guard<std::mutex> _l(mLock);
+                suffix = mId + reason;
+                sinkSource = mSinkSource;
+            }
+            dumpTee(fd, sinkSource, suffix);
+        }
+
+        void write(const void *buffer, size_t frameCount) {
+            if (!mEnabled.load() || frameCount == 0) return;
+            (void)mSinkSource.first->write(buffer, frameCount);
+            mDataReady.store(true);
+        }
+
+    private:
+        // TRICKY: We need to keep the NBAIO_Sink and NBAIO_Source both alive at the same time
+        // because PipeReader holds a naked reference (not a strong or weak pointer) to Pipe.
+        using NBAIO_SinkSource = std::pair<sp<NBAIO_Sink>, sp<NBAIO_Source>>;
+
+        static void dumpTee(int fd, const NBAIO_SinkSource& sinkSource, const std::string& suffix);
+
+        static NBAIO_SinkSource makeSinkSource(
+                const NBAIO_Format &format, size_t frames, bool *enabled);
+
+        // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
+        static constexpr size_t DEFAULT_TEE_FRAMES = 0x200000;
+
+        // atomic status checking
+        std::atomic<bool> mEnabled{false};
+        std::atomic<bool> mDataReady{false};
+
+        // locked dump information
+        mutable std::mutex mLock;
+        std::string mId;                                         // GUARDED_BY(mLock)
+        TEE_FLAG mFlags = TEE_FLAG_NONE;                         // GUARDED_BY(mLock)
+        NBAIO_Format mFormat = Format_Invalid;                   // GUARDED_BY(mLock)
+        size_t mFrames = 0;                                      // GUARDED_BY(mLock)
+        NBAIO_SinkSource mSinkSource;                            // GUARDED_BY(mLock)
+    };
+
+    /** RunningTees tracks current running tees for dump purposes.
+        It is implemented to have minimal locked regions, to be transparent to the caller. */
+    class RunningTees {
+    public:
+        void add(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
+            std::lock_guard<std::mutex> _l(mLock);
+            ALOGW_IF(!mTees.emplace(tee).second,
+                    "%s: %p already exists in mTees", __func__, tee.get());
+        }
+
+        void remove(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
+            std::lock_guard<std::mutex> _l(mLock);
+            ALOGW_IF(mTees.erase(tee) != 1,
+                    "%s: %p doesn't exist in mTees", __func__, tee.get());
+        }
+
+        void dump(int fd, const std::string &reason) {
+            std::vector<std::shared_ptr<NBAIO_TeeImpl>> tees; // safe snapshot of tees
+            {
+                std::lock_guard<std::mutex> _l(mLock);
+                tees.insert(tees.end(), mTees.begin(), mTees.end());
+            }
+            for (const auto &tee : tees) {
+                tee->dump(fd, reason);
+            }
+        }
+
+    private:
+        std::mutex mLock;
+        std::set<std::shared_ptr<NBAIO_TeeImpl>> mTees; // GUARDED_BY(mLock)
+    };
+
+    // singleton
+    static RunningTees &getRunningTees() {
+        static RunningTees runningTees;
+        return runningTees;
+    }
+
+    // The NBAIO TeeImpl may have lifetime longer than NBAIO_Tee if
+    // RunningTees::dump() is being called simultaneous to ~NBAIO_Tee().
+    // This is allowed for maximum concurrency.
+    const std::shared_ptr<NBAIO_TeeImpl> mTee;
+}; // NBAIO_Tee
+
+} // namespace android
+
+#endif // TEE_SINK
+#endif // !ANDROID_NBAIO_TEE_H
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index e5cb8a2..c2927ba 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -24,8 +24,9 @@
 #include <audio_utils/primitives.h>
 
 #include "AudioFlinger.h"
-#include "ServiceUtilities.h"
 #include <media/AudioParameter.h>
+#include <media/PatchBuilder.h>
+#include <mediautils/ServiceUtilities.h>
 
 // ----------------------------------------------------------------------------
 
@@ -49,107 +50,65 @@
                                 struct audio_port *ports)
 {
     Mutex::Autolock _l(mLock);
-    if (mPatchPanel != 0) {
-        return mPatchPanel->listAudioPorts(num_ports, ports);
-    }
-    return NO_INIT;
+    return mPatchPanel.listAudioPorts(num_ports, ports);
 }
 
 /* Get supported attributes for a given audio port */
 status_t AudioFlinger::getAudioPort(struct audio_port *port)
 {
     Mutex::Autolock _l(mLock);
-    if (mPatchPanel != 0) {
-        return mPatchPanel->getAudioPort(port);
-    }
-    return NO_INIT;
+    return mPatchPanel.getAudioPort(port);
 }
 
-
 /* Connect a patch between several source and sink ports */
 status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch,
                                    audio_patch_handle_t *handle)
 {
     Mutex::Autolock _l(mLock);
-    if (mPatchPanel != 0) {
-        return mPatchPanel->createAudioPatch(patch, handle);
-    }
-    return NO_INIT;
+    return mPatchPanel.createAudioPatch(patch, handle);
 }
 
 /* Disconnect a patch */
 status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle)
 {
     Mutex::Autolock _l(mLock);
-    if (mPatchPanel != 0) {
-        return mPatchPanel->releaseAudioPatch(handle);
-    }
-    return NO_INIT;
+    return mPatchPanel.releaseAudioPatch(handle);
 }
 
-
 /* List connected audio ports and they attributes */
 status_t AudioFlinger::listAudioPatches(unsigned int *num_patches,
                                   struct audio_patch *patches)
 {
     Mutex::Autolock _l(mLock);
-    if (mPatchPanel != 0) {
-        return mPatchPanel->listAudioPatches(num_patches, patches);
-    }
-    return NO_INIT;
-}
-
-/* Set audio port configuration */
-status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config)
-{
-    Mutex::Autolock _l(mLock);
-    if (mPatchPanel != 0) {
-        return mPatchPanel->setAudioPortConfig(config);
-    }
-    return NO_INIT;
-}
-
-
-AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger)
-                                   : mAudioFlinger(audioFlinger)
-{
-}
-
-AudioFlinger::PatchPanel::~PatchPanel()
-{
+    return mPatchPanel.listAudioPatches(num_patches, patches);
 }
 
 /* List connected audio ports and their attributes */
 status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
                                 struct audio_port *ports __unused)
 {
-    ALOGV("listAudioPorts");
+    ALOGV(__func__);
     return NO_ERROR;
 }
 
 /* Get supported attributes for a given audio port */
 status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused)
 {
-    ALOGV("getAudioPort");
+    ALOGV(__func__);
     return NO_ERROR;
 }
 
-
 /* Connect a patch between several source and sink ports */
 status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch,
                                    audio_patch_handle_t *handle)
 {
-    status_t status = NO_ERROR;
-    audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
-    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
     if (handle == NULL || patch == NULL) {
         return BAD_VALUE;
     }
-    ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d",
-          patch->num_sources, patch->num_sinks, *handle);
-    if (audioflinger == 0) {
-        return NO_INIT;
-    }
+    ALOGV("%s() num_sources %d num_sinks %d handle %d",
+            __func__, patch->num_sources, patch->num_sinks, *handle);
+    status_t status = NO_ERROR;
+    audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
 
     if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
             (patch->num_sinks == 0 && patch->num_sources != 2) ||
@@ -163,81 +122,73 @@
     }
 
     if (*handle != AUDIO_PATCH_HANDLE_NONE) {
-        for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
-            if (*handle == mPatches[index]->mHandle) {
-                ALOGV("createAudioPatch() removing patch handle %d", *handle);
-                halHandle = mPatches[index]->mHalHandle;
-                Patch *removedPatch = mPatches[index];
-                // free resources owned by the removed patch if applicable
-                // 1) if a software patch is present, release the playback and capture threads and
-                // tracks created. This will also release the corresponding audio HAL patches
-                if ((removedPatch->mRecordPatchHandle
-                        != AUDIO_PATCH_HANDLE_NONE) ||
-                        (removedPatch->mPlaybackPatchHandle !=
-                                AUDIO_PATCH_HANDLE_NONE)) {
-                    clearPatchConnections(removedPatch);
-                }
-                // 2) if the new patch and old patch source or sink are devices from different
-                // hw modules,  clear the audio HAL patches now because they will not be updated
-                // by call to create_audio_patch() below which will happen on a different HW module
-                if (halHandle != AUDIO_PATCH_HANDLE_NONE) {
-                    audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE;
-                    if ((removedPatch->mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE) &&
-                        ((patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE) ||
-                          (removedPatch->mAudioPatch.sources[0].ext.device.hw_module !=
-                           patch->sources[0].ext.device.hw_module))) {
-                        hwModule = removedPatch->mAudioPatch.sources[0].ext.device.hw_module;
-                    } else if ((patch->num_sinks == 0) ||
-                            ((removedPatch->mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
-                             ((patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) ||
-                              (removedPatch->mAudioPatch.sinks[0].ext.device.hw_module !=
-                               patch->sinks[0].ext.device.hw_module)))) {
-                        // Note on (patch->num_sinks == 0): this situation should not happen as
-                        // these special patches are only created by the policy manager but just
-                        // in case, systematically clear the HAL patch.
-                        // Note that removedPatch->mAudioPatch.num_sinks cannot be 0 here because
-                        // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case.
-                        hwModule = removedPatch->mAudioPatch.sinks[0].ext.device.hw_module;
-                    }
-                    if (hwModule != AUDIO_MODULE_HANDLE_NONE) {
-                        ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(hwModule);
-                        if (index >= 0) {
-                            sp<DeviceHalInterface> hwDevice =
-                                    audioflinger->mAudioHwDevs.valueAt(index)->hwDevice();
-                            hwDevice->releaseAudioPatch(halHandle);
-                        }
-                    }
-                }
-                mPatches.removeAt(index);
-                delete removedPatch;
-                break;
+        auto iter = mPatches.find(*handle);
+        if (iter != mPatches.end()) {
+            ALOGV("%s() removing patch handle %d", __func__, *handle);
+            Patch &removedPatch = iter->second;
+            // free resources owned by the removed patch if applicable
+            // 1) if a software patch is present, release the playback and capture threads and
+            // tracks created. This will also release the corresponding audio HAL patches
+            if (removedPatch.isSoftware()) {
+                removedPatch.clearConnections(this);
             }
+            // 2) if the new patch and old patch source or sink are devices from different
+            // hw modules,  clear the audio HAL patches now because they will not be updated
+            // by call to create_audio_patch() below which will happen on a different HW module
+            if (removedPatch.mHalHandle != AUDIO_PATCH_HANDLE_NONE) {
+                audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE;
+                const struct audio_patch &oldPatch = removedPatch.mAudioPatch;
+                if (oldPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
+                        (patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE ||
+                                oldPatch.sources[0].ext.device.hw_module !=
+                                patch->sources[0].ext.device.hw_module)) {
+                    hwModule = oldPatch.sources[0].ext.device.hw_module;
+                } else if (patch->num_sinks == 0 ||
+                        (oldPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
+                                (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE ||
+                                        oldPatch.sinks[0].ext.device.hw_module !=
+                                        patch->sinks[0].ext.device.hw_module))) {
+                    // Note on (patch->num_sinks == 0): this situation should not happen as
+                    // these special patches are only created by the policy manager but just
+                    // in case, systematically clear the HAL patch.
+                    // Note that removedPatch.mAudioPatch.num_sinks cannot be 0 here because
+                    // removedPatch.mHalHandle would be AUDIO_PATCH_HANDLE_NONE in this case.
+                    hwModule = oldPatch.sinks[0].ext.device.hw_module;
+                }
+                sp<DeviceHalInterface> hwDevice = findHwDeviceByModule(hwModule);
+                if (hwDevice != 0) {
+                    hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
+                }
+            }
+            mPatches.erase(iter);
         }
     }
 
-    Patch *newPatch = new Patch(patch);
+    Patch newPatch{*patch};
 
     switch (patch->sources[0].type) {
         case AUDIO_PORT_TYPE_DEVICE: {
             audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
-            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
+            ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule);
             if (index < 0) {
-                ALOGW("createAudioPatch() bad src hw module %d", srcModule);
+                ALOGW("%s() bad src hw module %d", __func__, srcModule);
                 status = BAD_VALUE;
                 goto exit;
             }
-            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+            AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index);
             for (unsigned int i = 0; i < patch->num_sinks; i++) {
                 // support only one sink if connection to a mix or across HW modules
                 if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
-                        patch->sinks[i].ext.mix.hw_module != srcModule) &&
+                                (patch->sinks[i].type == AUDIO_PORT_TYPE_DEVICE &&
+                                        patch->sinks[i].ext.device.hw_module != srcModule)) &&
                         patch->num_sinks > 1) {
+                    ALOGW("%s() multiple sinks for mix or across modules not supported", __func__);
                     status = INVALID_OPERATION;
                     goto exit;
                 }
                 // reject connection to different sink types
                 if (patch->sinks[i].type != patch->sinks[0].type) {
-                    ALOGW("createAudioPatch() different sink types in same patch not supported");
+                    ALOGW("%s() different sink types in same patch not supported", __func__);
                     status = BAD_VALUE;
                     goto exit;
                 }
@@ -256,38 +207,42 @@
                     if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX ||
                             (patch->num_sinks != 0 && patch->sinks[0].ext.device.hw_module !=
                                     patch->sources[1].ext.mix.hw_module)) {
-                        ALOGW("createAudioPatch() invalid source combination");
+                        ALOGW("%s() invalid source combination", __func__);
                         status = INVALID_OPERATION;
                         goto exit;
                     }
 
                     sp<ThreadBase> thread =
-                            audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle);
-                    newPatch->mPlaybackThread = (MixerThread *)thread.get();
+                            mAudioFlinger.checkPlaybackThread_l(patch->sources[1].ext.mix.handle);
                     if (thread == 0) {
-                        ALOGW("createAudioPatch() cannot get playback thread");
+                        ALOGW("%s() cannot get playback thread", __func__);
                         status = INVALID_OPERATION;
                         goto exit;
                     }
+                    // existing playback thread is reused, so it is not closed when patch is cleared
+                    newPatch.mPlayback.setThread(
+                            reinterpret_cast<PlaybackThread*>(thread.get()), false /*closeThread*/);
                 } else {
                     audio_config_t config = AUDIO_CONFIG_INITIALIZER;
                     audio_devices_t device = patch->sinks[0].ext.device.type;
                     String8 address = String8(patch->sinks[0].ext.device.address);
                     audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
-                    sp<ThreadBase> thread = audioflinger->openOutput_l(
+                    audio_output_flags_t flags =
+                            patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
+                            patch->sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE;
+                    sp<ThreadBase> thread = mAudioFlinger.openOutput_l(
                                                             patch->sinks[0].ext.device.hw_module,
                                                             &output,
                                                             &config,
                                                             device,
                                                             address,
-                                                            AUDIO_OUTPUT_FLAG_NONE);
-                    newPatch->mPlaybackThread = (PlaybackThread *)thread.get();
-                    ALOGV("audioflinger->openOutput_l() returned %p",
-                                          newPatch->mPlaybackThread.get());
-                    if (newPatch->mPlaybackThread == 0) {
+                                                            flags);
+                    ALOGV("mAudioFlinger.openOutput_l() returned %p", thread.get());
+                    if (thread == 0) {
                         status = NO_MEMORY;
                         goto exit;
                     }
+                    newPatch.mPlayback.setThread(reinterpret_cast<PlaybackThread*>(thread.get()));
                 }
                 audio_devices_t device = patch->sources[0].ext.device.type;
                 String8 address = String8(patch->sources[0].ext.device.address);
@@ -297,47 +252,50 @@
                 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
                     config.sample_rate = patch->sources[0].sample_rate;
                 } else {
-                    config.sample_rate = newPatch->mPlaybackThread->sampleRate();
+                    config.sample_rate = newPatch.mPlayback.thread()->sampleRate();
                 }
                 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
                     config.channel_mask = patch->sources[0].channel_mask;
                 } else {
-                    config.channel_mask =
-                        audio_channel_in_mask_from_count(newPatch->mPlaybackThread->channelCount());
+                    config.channel_mask = audio_channel_in_mask_from_count(
+                            newPatch.mPlayback.thread()->channelCount());
                 }
                 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) {
                     config.format = patch->sources[0].format;
                 } else {
-                    config.format = newPatch->mPlaybackThread->format();
+                    config.format = newPatch.mPlayback.thread()->format();
                 }
+                audio_input_flags_t flags =
+                        patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
+                        patch->sources[0].flags.input : AUDIO_INPUT_FLAG_NONE;
                 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
-                sp<ThreadBase> thread = audioflinger->openInput_l(srcModule,
+                sp<ThreadBase> thread = mAudioFlinger.openInput_l(srcModule,
                                                                     &input,
                                                                     &config,
                                                                     device,
                                                                     address,
                                                                     AUDIO_SOURCE_MIC,
-                                                                    AUDIO_INPUT_FLAG_NONE);
-                newPatch->mRecordThread = (RecordThread *)thread.get();
-                ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x",
-                      newPatch->mRecordThread.get(), config.channel_mask);
-                if (newPatch->mRecordThread == 0) {
+                                                                    flags);
+                ALOGV("mAudioFlinger.openInput_l() returned %p inChannelMask %08x",
+                      thread.get(), config.channel_mask);
+                if (thread == 0) {
                     status = NO_MEMORY;
                     goto exit;
                 }
-                status = createPatchConnections(newPatch, patch);
+                newPatch.mRecord.setThread(reinterpret_cast<RecordThread*>(thread.get()));
+                status = newPatch.createConnections(this);
                 if (status != NO_ERROR) {
                     goto exit;
                 }
             } else {
                 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
-                    sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+                    sp<ThreadBase> thread = mAudioFlinger.checkRecordThread_l(
                                                               patch->sinks[0].ext.mix.handle);
                     if (thread == 0) {
-                        thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle);
+                        thread = mAudioFlinger.checkMmapThread_l(patch->sinks[0].ext.mix.handle);
                         if (thread == 0) {
-                            ALOGW("createAudioPatch() bad capture I/O handle %d",
-                                                                  patch->sinks[0].ext.mix.handle);
+                            ALOGW("%s() bad capture I/O handle %d",
+                                    __func__, patch->sinks[0].ext.mix.handle);
                             status = BAD_VALUE;
                             goto exit;
                         }
@@ -356,9 +314,9 @@
         } break;
         case AUDIO_PORT_TYPE_MIX: {
             audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
-            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
+            ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule);
             if (index < 0) {
-                ALOGW("createAudioPatch() bad src hw module %d", srcModule);
+                ALOGW("%s() bad src hw module %d", __func__, srcModule);
                 status = BAD_VALUE;
                 goto exit;
             }
@@ -366,8 +324,8 @@
             audio_devices_t type = AUDIO_DEVICE_NONE;
             for (unsigned int i = 0; i < patch->num_sinks; i++) {
                 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
-                    ALOGW("createAudioPatch() invalid sink type %d for mix source",
-                          patch->sinks[i].type);
+                    ALOGW("%s() invalid sink type %d for mix source",
+                            __func__, patch->sinks[i].type);
                     status = BAD_VALUE;
                     goto exit;
                 }
@@ -379,21 +337,21 @@
                 type |= patch->sinks[i].ext.device.type;
             }
             sp<ThreadBase> thread =
-                            audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
+                            mAudioFlinger.checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
             if (thread == 0) {
-                thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle);
+                thread = mAudioFlinger.checkMmapThread_l(patch->sources[0].ext.mix.handle);
                 if (thread == 0) {
-                    ALOGW("createAudioPatch() bad playback I/O handle %d",
-                              patch->sources[0].ext.mix.handle);
+                    ALOGW("%s() bad playback I/O handle %d",
+                            __func__, patch->sources[0].ext.mix.handle);
                     status = BAD_VALUE;
                     goto exit;
                 }
             }
-            if (thread == audioflinger->primaryPlaybackThread_l()) {
+            if (thread == mAudioFlinger.primaryPlaybackThread_l()) {
                 AudioParameter param = AudioParameter();
                 param.addInt(String8(AudioParameter::keyRouting), (int)type);
 
-                audioflinger->broacastParametersToRecordThreads_l(param.toString());
+                mAudioFlinger.broacastParametersToRecordThreads_l(param.toString());
             }
 
             status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
@@ -403,72 +361,70 @@
             goto exit;
     }
 exit:
-    ALOGV("createAudioPatch() status %d", status);
+    ALOGV("%s() status %d", __func__, status);
     if (status == NO_ERROR) {
-        *handle = (audio_patch_handle_t) audioflinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
-        newPatch->mHandle = *handle;
-        newPatch->mHalHandle = halHandle;
-        mPatches.add(newPatch);
-        ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle);
+        *handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
+        newPatch.mHalHandle = halHandle;
+        mPatches.insert(std::make_pair(*handle, std::move(newPatch)));
+        ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle);
     } else {
-        clearPatchConnections(newPatch);
-        delete newPatch;
+        newPatch.clearConnections(this);
     }
     return status;
 }
 
-status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch,
-                                                          const struct audio_patch *audioPatch)
+AudioFlinger::PatchPanel::Patch::~Patch()
+{
+    ALOGE_IF(isSoftware(), "Software patch connections leaked %d %d",
+            mRecord.handle(), mPlayback.handle());
+}
+
+status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel)
 {
     // create patch from source device to record thread input
-    struct audio_patch subPatch;
-    subPatch.num_sources = 1;
-    subPatch.sources[0] = audioPatch->sources[0];
-    subPatch.num_sinks = 1;
-
-    patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]);
-    subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC;
-
-    status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle);
+    status_t status = panel->createAudioPatch(
+            PatchBuilder().addSource(mAudioPatch.sources[0]).
+                addSink(mRecord.thread(), { .source = AUDIO_SOURCE_MIC }).patch(),
+            mRecord.handlePtr());
     if (status != NO_ERROR) {
-        patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+        *mRecord.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
         return status;
     }
 
     // create patch from playback thread output to sink device
-    if (audioPatch->num_sinks != 0) {
-        patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]);
-        subPatch.sinks[0] = audioPatch->sinks[0];
-        status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle);
+    if (mAudioPatch.num_sinks != 0) {
+        status = panel->createAudioPatch(
+                PatchBuilder().addSource(mPlayback.thread()).addSink(mAudioPatch.sinks[0]).patch(),
+                mPlayback.handlePtr());
         if (status != NO_ERROR) {
-            patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
             return status;
         }
     } else {
-        patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+        *mPlayback.handlePtr() = AUDIO_PATCH_HANDLE_NONE;
     }
 
     // use a pseudo LCM between input and output framecount
-    size_t playbackFrameCount = patch->mPlaybackThread->frameCount();
+    size_t playbackFrameCount = mPlayback.thread()->frameCount();
     int playbackShift = __builtin_ctz(playbackFrameCount);
-    size_t recordFramecount = patch->mRecordThread->frameCount();
+    size_t recordFramecount = mRecord.thread()->frameCount();
     int shift = __builtin_ctz(recordFramecount);
     if (playbackShift < shift) {
         shift = playbackShift;
     }
     size_t frameCount = (playbackFrameCount * recordFramecount) >> shift;
-    ALOGV("createPatchConnections() playframeCount %zu recordFramecount %zu frameCount %zu",
-          playbackFrameCount, recordFramecount, frameCount);
+    ALOGV("%s() playframeCount %zu recordFramecount %zu frameCount %zu",
+            __func__, playbackFrameCount, recordFramecount, frameCount);
 
     // create a special record track to capture from record thread
-    uint32_t channelCount = patch->mPlaybackThread->channelCount();
+    uint32_t channelCount = mPlayback.thread()->channelCount();
     audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount);
-    audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask();
-    uint32_t sampleRate = patch->mPlaybackThread->sampleRate();
-    audio_format_t format = patch->mPlaybackThread->format();
+    audio_channel_mask_t outChannelMask = mPlayback.thread()->channelMask();
+    uint32_t sampleRate = mPlayback.thread()->sampleRate();
+    audio_format_t format = mPlayback.thread()->format();
 
-    patch->mPatchRecord = new RecordThread::PatchRecord(
-                                             patch->mRecordThread.get(),
+    sp<RecordThread::PatchRecord> tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord(
+                                             mRecord.thread().get(),
                                              sampleRate,
                                              inChannelMask,
                                              format,
@@ -476,222 +432,202 @@
                                              NULL,
                                              (size_t)0 /* bufferSize */,
                                              AUDIO_INPUT_FLAG_NONE);
-    if (patch->mPatchRecord == 0) {
-        return NO_MEMORY;
-    }
-    status = patch->mPatchRecord->initCheck();
+    status = mRecord.checkTrack(tempRecordTrack.get());
     if (status != NO_ERROR) {
         return status;
     }
-    patch->mRecordThread->addPatchRecord(patch->mPatchRecord);
 
     // create a special playback track to render to playback thread.
     // this track is given the same buffer as the PatchRecord buffer
-    patch->mPatchTrack = new PlaybackThread::PatchTrack(
-                                           patch->mPlaybackThread.get(),
-                                           audioPatch->sources[1].ext.mix.usecase.stream,
+    sp<PlaybackThread::PatchTrack> tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack(
+                                           mPlayback.thread().get(),
+                                           mAudioPatch.sources[1].ext.mix.usecase.stream,
                                            sampleRate,
                                            outChannelMask,
                                            format,
                                            frameCount,
-                                           patch->mPatchRecord->buffer(),
-                                           patch->mPatchRecord->bufferSize(),
+                                           tempRecordTrack->buffer(),
+                                           tempRecordTrack->bufferSize(),
                                            AUDIO_OUTPUT_FLAG_NONE);
-    status = patch->mPatchTrack->initCheck();
+    status = mPlayback.checkTrack(tempPatchTrack.get());
     if (status != NO_ERROR) {
         return status;
     }
-    patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack);
 
     // tie playback and record tracks together
-    patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get());
-    patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get());
+    mRecord.setTrackAndPeer(tempRecordTrack, tempPatchTrack.get());
+    mPlayback.setTrackAndPeer(tempPatchTrack, tempRecordTrack.get());
 
     // start capture and playback
-    patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
-    patch->mPatchTrack->start();
+    mRecord.track()->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
+    mPlayback.track()->start();
 
     return status;
 }
 
-void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch)
+void AudioFlinger::PatchPanel::Patch::clearConnections(PatchPanel *panel)
 {
-    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
-    if (audioflinger == 0) {
-        return;
+    ALOGV("%s() mRecord.handle %d mPlayback.handle %d",
+            __func__, mRecord.handle(), mPlayback.handle());
+    mRecord.stopTrack();
+    mPlayback.stopTrack();
+    mRecord.closeConnections(panel);
+    mPlayback.closeConnections(panel);
+}
+
+status_t AudioFlinger::PatchPanel::Patch::getLatencyMs(double *latencyMs) const
+{
+    if (!isSoftware()) return INVALID_OPERATION;
+
+    auto recordTrack = mRecord.const_track();
+    if (recordTrack.get() == nullptr) return INVALID_OPERATION;
+
+    auto playbackTrack = mPlayback.const_track();
+    if (playbackTrack.get() == nullptr) return INVALID_OPERATION;
+
+    // Latency information for tracks may be called without obtaining
+    // the underlying thread lock.
+    //
+    // We use record server latency + playback track latency (generally smaller than the
+    // reverse due to internal biases).
+    //
+    // TODO: is this stable enough? Consider a PatchTrack synchronized version of this.
+    double recordServerLatencyMs;
+    if (recordTrack->getServerLatencyMs(&recordServerLatencyMs) != OK) return INVALID_OPERATION;
+
+    double playbackTrackLatencyMs;
+    if (playbackTrack->getTrackLatencyMs(&playbackTrackLatencyMs) != OK) return INVALID_OPERATION;
+
+    *latencyMs = recordServerLatencyMs + playbackTrackLatencyMs;
+    return OK;
+}
+
+String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle)
+{
+    String8 result;
+
+    // TODO: Consider table dump form for patches, just like tracks.
+    result.appendFormat("Patch %d: thread %p => thread %p",
+            myHandle, mRecord.thread().get(), mPlayback.thread().get());
+
+    // add latency if it exists
+    double latencyMs;
+    if (getLatencyMs(&latencyMs) == OK) {
+        result.appendFormat("  latency: %.2lf", latencyMs);
     }
 
-    ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d",
-          patch->mRecordPatchHandle, patch->mPlaybackPatchHandle);
-
-    if (patch->mPatchRecord != 0) {
-        patch->mPatchRecord->stop();
-    }
-    if (patch->mPatchTrack != 0) {
-        patch->mPatchTrack->stop();
-    }
-    if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
-        releaseAudioPatch(patch->mRecordPatchHandle);
-        patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-    }
-    if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
-        releaseAudioPatch(patch->mPlaybackPatchHandle);
-        patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-    }
-    if (patch->mRecordThread != 0) {
-        if (patch->mPatchRecord != 0) {
-            patch->mRecordThread->deletePatchRecord(patch->mPatchRecord);
-        }
-        audioflinger->closeInputInternal_l(patch->mRecordThread);
-    }
-    if (patch->mPlaybackThread != 0) {
-        if (patch->mPatchTrack != 0) {
-            patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack);
-        }
-        // if num sources == 2 we are reusing an existing playback thread so we do not close it
-        if (patch->mAudioPatch.num_sources != 2) {
-            audioflinger->closeOutputInternal_l(patch->mPlaybackThread);
-        }
-    }
-    if (patch->mRecordThread != 0) {
-        if (patch->mPatchRecord != 0) {
-            patch->mPatchRecord.clear();
-        }
-        patch->mRecordThread.clear();
-    }
-    if (patch->mPlaybackThread != 0) {
-        if (patch->mPatchTrack != 0) {
-            patch->mPatchTrack.clear();
-        }
-        patch->mPlaybackThread.clear();
-    }
-
+    result.append("\n");
+    return result;
 }
 
 /* Disconnect a patch */
 status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
 {
-    ALOGV("releaseAudioPatch handle %d", handle);
+    ALOGV("%s handle %d", __func__, handle);
     status_t status = NO_ERROR;
-    size_t index;
 
-    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
-    if (audioflinger == 0) {
-        return NO_INIT;
-    }
-
-    for (index = 0; index < mPatches.size(); index++) {
-        if (handle == mPatches[index]->mHandle) {
-            break;
-        }
-    }
-    if (index == mPatches.size()) {
+    auto iter = mPatches.find(handle);
+    if (iter == mPatches.end()) {
         return BAD_VALUE;
     }
-    Patch *removedPatch = mPatches[index];
-    mPatches.removeAt(index);
+    Patch &removedPatch = iter->second;
+    const struct audio_patch &patch = removedPatch.mAudioPatch;
 
-    struct audio_patch *patch = &removedPatch->mAudioPatch;
-
-    switch (patch->sources[0].type) {
+    const struct audio_port_config &src = patch.sources[0];
+    switch (src.type) {
         case AUDIO_PORT_TYPE_DEVICE: {
-            audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
-            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
-            if (index < 0) {
-                ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
+            sp<DeviceHalInterface> hwDevice = findHwDeviceByModule(src.ext.device.hw_module);
+            if (hwDevice == 0) {
+                ALOGW("%s() bad src hw module %d", __func__, src.ext.device.hw_module);
                 status = BAD_VALUE;
                 break;
             }
 
-            if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE ||
-                    removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
-                clearPatchConnections(removedPatch);
+            if (removedPatch.isSoftware()) {
+                removedPatch.clearConnections(this);
                 break;
             }
 
-            if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
-                sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
-                                                                patch->sinks[0].ext.mix.handle);
+            if (patch.sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+                audio_io_handle_t ioHandle = patch.sinks[0].ext.mix.handle;
+                sp<ThreadBase> thread = mAudioFlinger.checkRecordThread_l(ioHandle);
                 if (thread == 0) {
-                    thread = audioflinger->checkMmapThread_l(patch->sinks[0].ext.mix.handle);
+                    thread = mAudioFlinger.checkMmapThread_l(ioHandle);
                     if (thread == 0) {
-                        ALOGW("releaseAudioPatch() bad capture I/O handle %d",
-                                                                  patch->sinks[0].ext.mix.handle);
+                        ALOGW("%s() bad capture I/O handle %d", __func__, ioHandle);
                         status = BAD_VALUE;
                         break;
                     }
                 }
-                status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
+                status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
             } else {
-                AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
-                sp<DeviceHalInterface> hwDevice = audioHwDevice->hwDevice();
-                status = hwDevice->releaseAudioPatch(removedPatch->mHalHandle);
+                status = hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
             }
         } break;
         case AUDIO_PORT_TYPE_MIX: {
-            audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
-            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
-            if (index < 0) {
-                ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
+            if (findHwDeviceByModule(src.ext.mix.hw_module) == 0) {
+                ALOGW("%s() bad src hw module %d", __func__, src.ext.mix.hw_module);
                 status = BAD_VALUE;
                 break;
             }
-            sp<ThreadBase> thread =
-                            audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
+            audio_io_handle_t ioHandle = src.ext.mix.handle;
+            sp<ThreadBase> thread = mAudioFlinger.checkPlaybackThread_l(ioHandle);
             if (thread == 0) {
-                thread = audioflinger->checkMmapThread_l(patch->sources[0].ext.mix.handle);
+                thread = mAudioFlinger.checkMmapThread_l(ioHandle);
                 if (thread == 0) {
-                    ALOGW("releaseAudioPatch() bad playback I/O handle %d",
-                                                                  patch->sources[0].ext.mix.handle);
+                    ALOGW("%s() bad playback I/O handle %d", __func__, ioHandle);
                     status = BAD_VALUE;
                     break;
                 }
             }
-            status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
+            status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
         } break;
         default:
             status = BAD_VALUE;
-            break;
     }
 
-    delete removedPatch;
+    mPatches.erase(iter);
     return status;
 }
 
-
 /* List connected audio ports and they attributes */
 status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
                                   struct audio_patch *patches __unused)
 {
-    ALOGV("listAudioPatches");
+    ALOGV(__func__);
     return NO_ERROR;
 }
 
-/* Set audio port configuration */
-status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
+sp<DeviceHalInterface> AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module)
 {
-    ALOGV("setAudioPortConfig");
-
-    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
-    if (audioflinger == 0) {
-        return NO_INIT;
-    }
-
-    audio_module_handle_t module;
-    if (config->type == AUDIO_PORT_TYPE_DEVICE) {
-        module = config->ext.device.hw_module;
-    } else {
-        module = config->ext.mix.hw_module;
-    }
-
-    ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
+    if (module == AUDIO_MODULE_HANDLE_NONE) return nullptr;
+    ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(module);
     if (index < 0) {
-        ALOGW("setAudioPortConfig() bad hw module %d", module);
-        return BAD_VALUE;
+        return nullptr;
     }
+    return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice();
+}
 
-    AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
-    return audioHwDevice->hwDevice()->setAudioPortConfig(config);
+void AudioFlinger::PatchPanel::dump(int fd)
+{
+    // Only dump software patches.
+    bool headerPrinted = false;
+    for (auto& iter : mPatches) {
+        if (iter.second.isSoftware()) {
+            if (!headerPrinted) {
+                String8 header("\nSoftware patches:\n");
+                write(fd, header.string(), header.size());
+                headerPrinted = true;
+            }
+            String8 patchDump("  ");
+            patchDump.append(iter.second.dump(iter.first));
+            write(fd, patchDump.string(), patchDump.size());
+        }
+    }
+    if (headerPrinted) {
+        String8 trailing("\n");
+        write(fd, trailing.string(), trailing.size());
+    }
 }
 
 } // namespace android
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index d37c0d3..5d6bf00 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -19,13 +19,10 @@
     #error This header file should only be included from AudioFlinger.h
 #endif
 
-class PatchPanel : public RefBase {
+// PatchPanel is concealed within AudioFlinger, their lifetimes are the same.
+class PatchPanel {
 public:
-
-    class Patch;
-
-    explicit PatchPanel(const sp<AudioFlinger>& audioFlinger);
-    virtual ~PatchPanel();
+    explicit PatchPanel(AudioFlinger* audioFlinger) : mAudioFlinger(*audioFlinger) {}
 
     /* List connected audio ports and their attributes */
     status_t listAudioPorts(unsigned int *num_ports,
@@ -45,46 +42,104 @@
     status_t listAudioPatches(unsigned int *num_patches,
                                       struct audio_patch *patches);
 
-    /* Set audio port configuration */
-    status_t setAudioPortConfig(const struct audio_port_config *config);
+    void dump(int fd);
 
-    status_t createPatchConnections(Patch *patch,
-                                    const struct audio_patch *audioPatch);
-    void clearPatchConnections(Patch *patch);
+private:
+    template<typename ThreadType, typename TrackType>
+    class Endpoint {
+    public:
+        Endpoint() = default;
+        Endpoint(Endpoint&& other) { *this = std::move(other); }
+        Endpoint& operator=(Endpoint&& other) {
+            ALOGE_IF(mHandle != AUDIO_PATCH_HANDLE_NONE,
+                    "A non empty Patch Endpoint leaked, handle %d", mHandle);
+            *this = other;
+            other.mHandle = AUDIO_PATCH_HANDLE_NONE;
+            return *this;
+        }
+
+        status_t checkTrack(TrackType *trackOrNull) const {
+            if (trackOrNull == nullptr) return NO_MEMORY;
+            return trackOrNull->initCheck();
+        }
+        audio_patch_handle_t handle() const { return mHandle; }
+        sp<ThreadType> thread() { return mThread; }
+        sp<TrackType> track() { return mTrack; }
+        sp<const TrackType> const_track() const { return mTrack; }
+
+        void closeConnections(PatchPanel *panel) {
+            if (mHandle != AUDIO_PATCH_HANDLE_NONE) {
+                panel->releaseAudioPatch(mHandle);
+                mHandle = AUDIO_PATCH_HANDLE_NONE;
+            }
+            if (mThread != 0) {
+                if (mTrack != 0) {
+                    mThread->deletePatchTrack(mTrack);
+                }
+                if (mCloseThread) {
+                    panel->mAudioFlinger.closeThreadInternal_l(mThread);
+                }
+            }
+        }
+        audio_patch_handle_t* handlePtr() { return &mHandle; }
+        void setThread(const sp<ThreadType>& thread, bool closeThread = true) {
+            mThread = thread;
+            mCloseThread = closeThread;
+        }
+        void setTrackAndPeer(const sp<TrackType>& track,
+                             ThreadBase::PatchProxyBufferProvider *peer) {
+            mTrack = track;
+            mThread->addPatchTrack(mTrack);
+            mTrack->setPeerProxy(peer);
+        }
+        void stopTrack() { if (mTrack) mTrack->stop(); }
+
+    private:
+        Endpoint(const Endpoint&) = default;
+        Endpoint& operator=(const Endpoint&) = default;
+
+        sp<ThreadType> mThread;
+        bool mCloseThread = true;
+        audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE;
+        sp<TrackType> mTrack;
+    };
 
     class Patch {
     public:
-        explicit Patch(const struct audio_patch *patch) :
-            mAudioPatch(*patch), mHandle(AUDIO_PATCH_HANDLE_NONE),
-            mHalHandle(AUDIO_PATCH_HANDLE_NONE), mRecordPatchHandle(AUDIO_PATCH_HANDLE_NONE),
-            mPlaybackPatchHandle(AUDIO_PATCH_HANDLE_NONE) {}
-        ~Patch() {}
+        explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {}
+        ~Patch();
+        Patch(const Patch&) = delete;
+        Patch(Patch&&) = default;
+        Patch& operator=(const Patch&) = delete;
+        Patch& operator=(Patch&&) = default;
 
+        status_t createConnections(PatchPanel *panel);
+        void clearConnections(PatchPanel *panel);
+        bool isSoftware() const {
+            return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE ||
+                    mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; }
+
+        // returns the latency of the patch (from record to playback).
+        status_t getLatencyMs(double *latencyMs) const;
+
+        String8 dump(audio_patch_handle_t myHandle);
+
+        // Note that audio_patch::id is only unique within a HAL module
         struct audio_patch              mAudioPatch;
-        audio_patch_handle_t            mHandle;
         // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0
-        audio_patch_handle_t            mHalHandle;
+        audio_patch_handle_t            mHalHandle = AUDIO_PATCH_HANDLE_NONE;
         // below members are used by a software audio patch connecting a source device from a
         // given audio HW module to a sink device on an other audio HW module.
-        // playback thread created by createAudioPatch() and released by clearPatchConnections() if
-        // no existing playback thread can be used by the software patch
-        sp<PlaybackThread>              mPlaybackThread;
-        // audio track created by createPatchConnections() and released by clearPatchConnections()
-        sp<PlaybackThread::PatchTrack>  mPatchTrack;
-        // record thread created by createAudioPatch() and released by clearPatchConnections()
-        sp<RecordThread>                mRecordThread;
-        // audio record created by createPatchConnections() and released by clearPatchConnections()
-        sp<RecordThread::PatchRecord>   mPatchRecord;
-        // handle for audio patch connecting source device to record thread input.
-        // created by createPatchConnections() and released by clearPatchConnections()
-        audio_patch_handle_t            mRecordPatchHandle;
-        // handle for audio patch connecting playback thread output to sink device
-        // created by createPatchConnections() and released by clearPatchConnections()
-        audio_patch_handle_t            mPlaybackPatchHandle;
-
+        // the objects are created by createConnections() and released by clearConnections()
+        // playback thread is created if no existing playback thread can be used
+        // connects playback thread output to sink device
+        Endpoint<PlaybackThread, PlaybackThread::PatchTrack> mPlayback;
+        // connects source device to record thread input
+        Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
     };
 
-private:
-    const wp<AudioFlinger>      mAudioFlinger;
-    SortedVector <Patch *>      mPatches;
+    sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
+
+    AudioFlinger &mAudioFlinger;
+    std::map<audio_patch_handle_t, Patch> mPatches;
 };
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a78be992..a93899b 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -41,7 +41,7 @@
     virtual             ~Track();
     virtual status_t    initCheck() const;
 
-    static  void        appendDumpHeader(String8& result);
+            void        appendDumpHeader(String8& result);
             void        appendDump(String8& result, bool active);
     virtual status_t    start(AudioSystem::sync_event_t event =
                                     AudioSystem::SYNC_EVENT_NONE,
@@ -56,6 +56,12 @@
                 LOG_ALWAYS_FATAL_IF(mName >= 0 && name >= 0,
                         "%s both old name %d and new name %d are valid", __func__, mName, name);
                 mName = name;
+#ifdef TEE_SINK
+                mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+                        + "_" + std::to_string(mId)
+                        + "_" + std::to_string(mName)
+                        + "_T");
+#endif
             }
 
     virtual uint32_t    sampleRate() const;
@@ -69,6 +75,7 @@
             bool        isOffloadedOrDirect() const { return (mFlags
                             & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
                                     | AUDIO_OUTPUT_FLAG_DIRECT)) != 0; }
+            bool        isStatic() const { return  mSharedBuffer.get() != nullptr; }
 
             status_t    setParameters(const String8& keyValuePairs);
             status_t    attachAuxEffect(int EffectId);
@@ -87,6 +94,10 @@
 
     virtual bool        isFastTrack() const { return (mFlags & AUDIO_OUTPUT_FLAG_FAST) != 0; }
 
+            double      bufferLatencyMs() const override {
+                            return isStatic() ? 0. : TrackBase::bufferLatencyMs();
+                        }
+
 // implement volume handling.
     media::VolumeShaper::Status applyVolumeShaper(
                                 const sp<media::VolumeShaper::Configuration>& configuration,
@@ -140,7 +151,7 @@
     bool isResumePending();
     void resumeAck();
     void updateTrackFrameInfo(int64_t trackFramesReleased, int64_t sinkFramesWritten,
-            const ExtendedTimestamp &timeStamp);
+            uint32_t halSampleRate, const ExtendedTimestamp &timeStamp);
 
     sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
 
@@ -233,7 +244,7 @@
                                     AudioSystem::SYNC_EVENT_NONE,
                              audio_session_t triggerSession = AUDIO_SESSION_NONE);
     virtual void        stop();
-            bool        write(void* data, uint32_t frames);
+            ssize_t     write(void* data, uint32_t frames);
             bool        bufferQueueEmpty() const { return mBufferQueue.size() == 0; }
             bool        isActive() const { return mActive; }
     const wp<ThreadBase>& thread() const { return mThread; }
@@ -241,6 +252,18 @@
             void        copyMetadataTo(MetadataInserter& backInserter) const override;
     /** Set the metadatas of the upstream tracks. Thread safe. */
             void        setMetadatas(const SourceMetadatas& metadatas);
+    /** returns client timestamp to the upstream duplicating thread. */
+    ExtendedTimestamp   getClientProxyTimestamp() const {
+                            // server - kernel difference is not true latency when drained
+                            // i.e. mServerProxy->isDrained().
+                            ExtendedTimestamp timestamp;
+                            (void) mClientProxy->getTimestamp(&timestamp);
+                            // On success, the timestamp LOCATION_SERVER and LOCATION_KERNEL
+                            // entries will be properly filled. If getTimestamp()
+                            // is unsuccessful, then a default initialized timestamp
+                            // (with mTimeNs[] filled with -1's) is returned.
+                            return timestamp;
+                        }
 
 private:
     status_t            obtainBuffer(AudioBufferProvider::Buffer* buffer,
@@ -257,6 +280,7 @@
     bool                        mActive;
     DuplicatingThread* const    mSourceThread; // for waitTimeMs() in write()
     sp<AudioTrackClientProxy>   mClientProxy;
+
     /** Attributes of the source tracks.
      *
      * This member must be accessed with mTrackMetadatasMutex taken.
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index fc2dbbb..43b6391 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -51,7 +51,7 @@
             bool        setOverflow() { bool tmp = mOverflow; mOverflow = true;
                                                 return tmp; }
 
-    static  void        appendDumpHeader(String8& result);
+            void        appendDumpHeader(String8& result);
             void        appendDump(String8& result, bool active);
 
             void        handleSyncStartEvent(const sp<SyncEvent>& event);
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
deleted file mode 100644
index f45ada1..0000000
--- a/services/audioflinger/ServiceUtilities.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 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 <unistd.h>
-
-#include <binder/PermissionController.h>
-
-namespace android {
-
-extern pid_t getpid_cached;
-bool isTrustedCallingUid(uid_t uid);
-bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
-bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid);
-void finishRecording(const String16& opPackageName, uid_t uid);
-bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
-bool captureHotwordAllowed(pid_t pid, uid_t uid);
-bool settingsAllowed();
-bool modifyAudioRoutingAllowed();
-bool dumpAllowed();
-bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
-}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index dcad866..65bbd8d 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -62,8 +62,8 @@
 #include "AudioFlinger.h"
 #include "FastMixer.h"
 #include "FastCapture.h"
-#include "ServiceUtilities.h"
-#include "mediautils/SchedulingPolicyService.h"
+#include <mediautils/SchedulingPolicyService.h>
+#include <mediautils/ServiceUtilities.h>
 
 #ifdef ADD_BATTERY_DATA
 #include <media/IMediaPlayerService.h>
@@ -769,6 +769,8 @@
             if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_LEFT) s.append("top-back-left, ");
             if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_CENTER) s.append("top-back-center, " );
             if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " );
+            if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT) s.append("top-side-left, " );
+            if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT) s.append("top-side-right, " );
             if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown,  ");
         } else {
             if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, ");
@@ -783,6 +785,12 @@
             if (mask & AUDIO_CHANNEL_IN_X_AXIS) s.append("X, ");
             if (mask & AUDIO_CHANNEL_IN_Y_AXIS) s.append("Y, ");
             if (mask & AUDIO_CHANNEL_IN_Z_AXIS) s.append("Z, ");
+            if (mask & AUDIO_CHANNEL_IN_BACK_LEFT) s.append("back-left, ");
+            if (mask & AUDIO_CHANNEL_IN_BACK_RIGHT) s.append("back-right, ");
+            if (mask & AUDIO_CHANNEL_IN_CENTER) s.append("center, ");
+            if (mask & AUDIO_CHANNEL_IN_LOW_FREQUENCY) s.append("low freq, ");
+            if (mask & AUDIO_CHANNEL_IN_TOP_LEFT) s.append("top-left, " );
+            if (mask & AUDIO_CHANNEL_IN_TOP_RIGHT) s.append("top-right, " );
             if (mask & AUDIO_CHANNEL_IN_VOICE_UPLINK) s.append("voice-uplink, ");
             if (mask & AUDIO_CHANNEL_IN_VOICE_DNLINK) s.append("voice-dnlink, ");
             if (mask & ~AUDIO_CHANNEL_IN_ALL) s.append("unknown,  ");
@@ -1519,7 +1527,7 @@
     }
 }
 
-void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config)
+void AudioFlinger::ThreadBase::toAudioPortConfig(struct audio_port_config *config)
 {
     config->type = AUDIO_PORT_TYPE_MIX;
     config->ext.mix.handle = mId;
@@ -1571,6 +1579,9 @@
     --mBatteryCounter[track->uid()].second;
     // mLatestActiveTrack is not cleared even if is the same as track.
     mHasChanged = true;
+#ifdef TEE_SINK
+    track->dumpTee(-1 /* fd */, "_REMOVE");
+#endif
     return index;
 }
 
@@ -1773,7 +1784,7 @@
     if (numtracks) {
         dprintf(fd, " of which %zu are active\n", numactive);
         result.append(prefix);
-        Track::appendDumpHeader(result);
+        mTracks[0]->appendDumpHeader(result);
         for (size_t i = 0; i < numtracks; ++i) {
             sp<Track> track = mTracks[i];
             if (track != 0) {
@@ -1793,7 +1804,7 @@
         result.append("  The following tracks are in the active list but"
                 " not in the track list\n");
         result.append(prefix);
-        Track::appendDumpHeader(result);
+        mActiveTracks[0]->appendDumpHeader(result);
         for (size_t i = 0; i < numactive; ++i) {
             sp<Track> track = mActiveTracks[i];
             if (mTracks.indexOf(track) < 0) {
@@ -1880,11 +1891,17 @@
     status_t lStatus;
     audio_output_flags_t outputFlags = mOutput->flags;
     audio_output_flags_t requestedFlags = *flags;
+    uint32_t sampleRate;
+
+    if (sharedBuffer != 0 && checkIMemory(sharedBuffer) != NO_ERROR) {
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
 
     if (*pSampleRate == 0) {
         *pSampleRate = mSampleRate;
     }
-    uint32_t sampleRate = *pSampleRate;
+    sampleRate = *pSampleRate;
 
     // special case for FAST flag considered OK if fast mixer is present
     if (hasFastMixer()) {
@@ -2845,6 +2862,9 @@
         ATRACE_END();
         if (framesWritten > 0) {
             bytesWritten = framesWritten * mFrameSize;
+#ifdef TEE_SINK
+            mTee.write((char *)mSinkBuffer + offset, framesWritten);
+#endif
         } else {
             bytesWritten = framesWritten;
         }
@@ -3189,12 +3209,10 @@
             // and associate with the sink frames written out.  We need
             // this to convert the sink timestamp to the track timestamp.
             bool kernelLocationUpdate = false;
-            if (mNormalSink != 0) {
-                // Note: The DuplicatingThread may not have a mNormalSink.
+            ExtendedTimestamp timestamp; // use private copy to fetch
+            if (threadloop_getHalTimestamp_l(&timestamp) == OK) {
                 // We always fetch the timestamp here because often the downstream
                 // sink will block while writing.
-                ExtendedTimestamp timestamp; // use private copy to fetch
-                (void) mNormalSink->getTimestamp(timestamp);
 
                 // We keep track of the last valid kernel position in case we are in underrun
                 // and the normal mixer period is the same as the fast mixer period, or there
@@ -3251,6 +3269,7 @@
                         t->updateTrackFrameInfo(
                                 t->mAudioTrackServerProxy->framesReleased(),
                                 mFramesWritten,
+                                mSampleRate,
                                 mTimestamp);
                     }
                 }
@@ -3772,12 +3791,16 @@
     destroyTrack_l(track);
 }
 
-void AudioFlinger::PlaybackThread::getAudioPortConfig(struct audio_port_config *config)
+void AudioFlinger::PlaybackThread::toAudioPortConfig(struct audio_port_config *config)
 {
-    ThreadBase::getAudioPortConfig(config);
+    ThreadBase::toAudioPortConfig(config);
     config->role = AUDIO_PORT_ROLE_SOURCE;
     config->ext.mix.hw_module = mOutput->audioHwDev->handle();
     config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
+    if (mOutput && mOutput->flags != AUDIO_OUTPUT_FLAG_NONE) {
+        config->config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+        config->flags.output = mOutput->flags;
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -3858,9 +3881,7 @@
 
         // create a MonoPipe to connect our submix to FastMixer
         NBAIO_Format format = mOutputSink->format();
-#ifdef TEE_SINK
-        NBAIO_Format origformat = format;
-#endif
+
         // adjust format to match that of the Fast Mixer
         ALOGV("format changed from %#x to %#x", format.mFormat, fastMixerFormat);
         format.mFormat = fastMixerFormat;
@@ -3872,7 +3893,7 @@
         MonoPipe *monoPipe = new MonoPipe(mNormalFrameCount * 4, format, true /*writeCanBlock*/);
         const NBAIO_Format offers[1] = {format};
         size_t numCounterOffers = 0;
-#if !LOG_NDEBUG || defined(TEE_SINK)
+#if !LOG_NDEBUG
         ssize_t index =
 #else
         (void)
@@ -3883,25 +3904,8 @@
                 (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
         mPipeSink = monoPipe;
 
-#ifdef TEE_SINK
-        if (mTeeSinkOutputEnabled) {
-            // create a Pipe to archive a copy of FastMixer's output for dumpsys
-            Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, origformat);
-            const NBAIO_Format offers2[1] = {origformat};
-            numCounterOffers = 0;
-            index = teeSink->negotiate(offers2, 1, NULL, numCounterOffers);
-            ALOG_ASSERT(index == 0);
-            mTeeSink = teeSink;
-            PipeReader *teeSource = new PipeReader(*teeSink);
-            numCounterOffers = 0;
-            index = teeSource->negotiate(offers2, 1, NULL, numCounterOffers);
-            ALOG_ASSERT(index == 0);
-            mTeeSource = teeSource;
-        }
-#endif
-
         // create fast mixer and configure it initially with just one fast track for our submix
-        mFastMixer = new FastMixer();
+        mFastMixer = new FastMixer(mId);
         FastMixerStateQueue *sq = mFastMixer->sq();
 #ifdef STATE_QUEUE_DUMP
         sq->setObserverDump(&mStateQueueObserverDump);
@@ -3927,9 +3931,6 @@
         state->mColdFutexAddr = &mFastMixerFutex;
         state->mColdGen++;
         state->mDumpState = &mFastMixerDumpState;
-#ifdef TEE_SINK
-        state->mTeeSink = mTeeSink.get();
-#endif
         mFastMixerNBLogWriter = audioFlinger->newWriter_l(kFastMixerLogSize, "FastMixer");
         state->mNBLogWriter = mFastMixerNBLogWriter.get();
         sq->end();
@@ -3938,7 +3939,7 @@
         // start the fast mixer
         mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
         pid_t tid = mFastMixer->getTid();
-        sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/);
+        sendPrioConfigEvent(getpid(), tid, kPriorityFastMixer, false /*forApp*/);
         stream()->setHalThreadPriority(kPriorityFastMixer);
 
 #ifdef AUDIO_WATCHDOG
@@ -3947,9 +3948,14 @@
         mAudioWatchdog->setDump(&mAudioWatchdogDump);
         mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
         tid = mAudioWatchdog->getTid();
-        sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/);
+        sendPrioConfigEvent(getpid(), tid, kPriorityFastMixer, false /*forApp*/);
 #endif
-
+    } else {
+#ifdef TEE_SINK
+        // Only use the MixerThread tee if there is no FastMixer.
+        mTee.set(mOutputSink->format(), NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD);
+        mTee.setId(std::string("_") + std::to_string(mId) + "_M");
+#endif
     }
 
     switch (kUseFastMixer) {
@@ -4265,6 +4271,37 @@
     mMixerBufferValid = false;  // mMixerBuffer has no valid data until appropriate tracks found.
     mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.
 
+    // DeferredOperations handles statistics after setting mixerStatus.
+    class DeferredOperations {
+    public:
+        DeferredOperations(mixer_state *mixerStatus)
+            : mMixerStatus(mixerStatus) { }
+
+        // when leaving scope, tally frames properly.
+        ~DeferredOperations() {
+            // Tally underrun frames only if we are actually mixing (MIXER_TRACKS_READY)
+            // because that is when the underrun occurs.
+            // We do not distinguish between FastTracks and NormalTracks here.
+            if (*mMixerStatus == MIXER_TRACKS_READY) {
+                for (const auto &underrun : mUnderrunFrames) {
+                    underrun.first->mAudioTrackServerProxy->tallyUnderrunFrames(
+                            underrun.second);
+                }
+            }
+        }
+
+        // tallyUnderrunFrames() is called to update the track counters
+        // with the number of underrun frames for a particular mixer period.
+        // We defer tallying until we know the final mixer status.
+        void tallyUnderrunFrames(sp<Track> track, size_t underrunFrames) {
+            mUnderrunFrames.emplace_back(track, underrunFrames);
+        }
+
+    private:
+        const mixer_state * const mMixerStatus;
+        std::vector<std::pair<sp<Track>, size_t>> mUnderrunFrames;
+    } deferredOperations(&mixerStatus); // implicit nested scope for variable capture
+
     for (size_t i=0 ; i<count ; i++) {
         const sp<Track> t = mActiveTracks[i];
 
@@ -4299,13 +4336,14 @@
             track->mObservedUnderruns = underruns;
             // don't count underruns that occur while stopping or pausing
             // or stopped which can occur when flush() is called while active
+            size_t underrunFrames = 0;
             if (!(track->isStopping() || track->isPausing() || track->isStopped()) &&
                     recentUnderruns > 0) {
                 // FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
-                track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
-            } else {
-                track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
+                underrunFrames = recentUnderruns * mFrameCount;
             }
+            // Immediately account for FastTrack underruns.
+            track->mAudioTrackServerProxy->tallyUnderrunFrames(underrunFrames);
 
             // This is similar to the state machine for normal tracks,
             // with a few modifications for fast tracks.
@@ -4720,13 +4758,13 @@
                 mixerStatus = MIXER_TRACKS_READY;
             }
         } else {
+            size_t underrunFrames = 0;
             if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
                 ALOGV("track(%p) underrun,  framesReady(%zu) < framesDesired(%zd)",
                         track, framesReady, desiredFrames);
-                track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
-            } else {
-                track->mAudioTrackServerProxy->tallyUnderrunFrames(0);
+                underrunFrames = desiredFrames;
             }
+            deferredOperations.tallyUnderrunFrames(track, underrunFrames);
 
             // clear effect chain input buffer if an active track underruns to avoid sending
             // previous audio buffer again to effects
@@ -5025,6 +5063,12 @@
     dprintf(fd, "  Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs);
     dprintf(fd, "  AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str());
     dprintf(fd, "  Master mono: %s\n", mMasterMono ? "on" : "off");
+    const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate);
+    if (latencyMs != 0.) {
+        dprintf(fd, "  NormalMixer latency ms: %.2lf\n", latencyMs);
+    } else {
+        dprintf(fd, "  NormalMixer latency ms: unavail\n");
+    }
 
     if (hasFastMixer()) {
         dprintf(fd, "  FastMixer thread %p tid=%d", mFastMixer.get(), mFastMixer->getTid());
@@ -5056,12 +5100,6 @@
     } else {
         dprintf(fd, "  No FastMixer\n");
     }
-
-#ifdef TEE_SINK
-    // Write the tee output to a .wav file
-    dumpTee(fd, mTeeSource, mId, 'M');
-#endif
-
 }
 
 uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
@@ -6062,7 +6100,22 @@
 ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
 {
     for (size_t i = 0; i < outputTracks.size(); i++) {
-        outputTracks[i]->write(mSinkBuffer, writeFrames);
+        const ssize_t actualWritten = outputTracks[i]->write(mSinkBuffer, writeFrames);
+
+        // Consider the first OutputTrack for timestamp and frame counting.
+
+        // The threadLoop() generally assumes writing a full sink buffer size at a time.
+        // Here, we correct for writeFrames of 0 (a stop) or underruns because
+        // we always claim success.
+        if (i == 0) {
+            const ssize_t correction = mSinkBufferSize / mFrameSize - actualWritten;
+            ALOGD_IF(correction != 0 && writeFrames != 0,
+                    "%s: writeFrames:%u  actualWritten:%zd  correction:%zd  mFramesWritten:%lld",
+                    __func__, writeFrames, actualWritten, correction, (long long)mFramesWritten);
+            mFramesWritten -= correction;
+        }
+
+        // TODO: Report correction for the other output tracks and show in the dump.
     }
     mStandby = false;
     return (ssize_t)mSinkBufferSize;
@@ -6227,9 +6280,6 @@
                                          audio_devices_t outDevice,
                                          audio_devices_t inDevice,
                                          bool systemReady
-#ifdef TEE_SINK
-                                         , const sp<NBAIO_Sink>& teeSink
-#endif
                                          ) :
     ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
     mInput(input),
@@ -6237,9 +6287,6 @@
     mRsmpInBuffer(NULL),
     // mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
     mRsmpInRear(0)
-#ifdef TEE_SINK
-    , mTeeSink(teeSink)
-#endif
     , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize,
             "RecordThreadRO", MemoryHeapBase::READ_ONLY))
     // mFastCapture below
@@ -6354,7 +6401,7 @@
         // start the fast capture
         mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO);
         pid_t tid = mFastCapture->getTid();
-        sendPrioConfigEvent(getpid_cached, tid, kPriorityFastCapture, false /*forApp*/);
+        sendPrioConfigEvent(getpid(), tid, kPriorityFastCapture, false /*forApp*/);
         stream()->setHalThreadPriority(kPriorityFastCapture);
 #ifdef AUDIO_WATCHDOG
         // FIXME
@@ -6362,6 +6409,10 @@
 
         mFastTrackAvail = true;
     }
+#ifdef TEE_SINK
+    mTee.set(mInputSource->format(), NBAIO_Tee::TEE_FLAG_INPUT_THREAD);
+    mTee.setId(std::string("_") + std::to_string(mId) + "_C");
+#endif
 failed: ;
 
     // FIXME mNormalSource
@@ -6704,9 +6755,9 @@
         }
         ALOG_ASSERT(framesRead > 0);
 
-        if (mTeeSink != 0) {
-            (void) mTeeSink->write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
-        }
+#ifdef TEE_SINK
+        (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
+#endif
         // If destination is non-contiguous, we now correct for reading past end of buffer.
         {
             size_t part1 = mRsmpInFramesP2 - rear;
@@ -7304,6 +7355,13 @@
         (void)input->stream->dump(fd);
     }
 
+    const double latencyMs = - mTimestamp.getOutputServerLatencyMs(mSampleRate);
+    if (latencyMs != 0.) {
+        dprintf(fd, "  NormalRecord latency ms: %.2lf\n", latencyMs);
+    } else {
+        dprintf(fd, "  NormalRecord latency ms: unavail\n");
+    }
+
     dprintf(fd, "  Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no");
     dprintf(fd, "  Fast track available: %s\n", mFastTrackAvail ? "yes" : "no");
 
@@ -7327,7 +7385,7 @@
     if (numtracks) {
         dprintf(fd, " of which %zu are active\n", numactive);
         result.append(prefix);
-        RecordTrack::appendDumpHeader(result);
+        mTracks[0]->appendDumpHeader(result);
         for (size_t i = 0; i < numtracks ; ++i) {
             sp<RecordTrack> track = mTracks[i];
             if (track != 0) {
@@ -7347,7 +7405,7 @@
         result.append("  The following tracks are in the active list but"
                 " not in the track list\n");
         result.append(prefix);
-        RecordTrack::appendDumpHeader(result);
+        mActiveTracks[0]->appendDumpHeader(result);
         for (size_t i = 0; i < numactive; ++i) {
             sp<RecordTrack> track = mActiveTracks[i];
             if (mTracks.indexOf(track) < 0) {
@@ -7855,24 +7913,28 @@
     return status;
 }
 
-void AudioFlinger::RecordThread::addPatchRecord(const sp<PatchRecord>& record)
+void AudioFlinger::RecordThread::addPatchTrack(const sp<PatchRecord>& record)
 {
     Mutex::Autolock _l(mLock);
     mTracks.add(record);
 }
 
-void AudioFlinger::RecordThread::deletePatchRecord(const sp<PatchRecord>& record)
+void AudioFlinger::RecordThread::deletePatchTrack(const sp<PatchRecord>& record)
 {
     Mutex::Autolock _l(mLock);
     destroyTrack_l(record);
 }
 
-void AudioFlinger::RecordThread::getAudioPortConfig(struct audio_port_config *config)
+void AudioFlinger::RecordThread::toAudioPortConfig(struct audio_port_config *config)
 {
-    ThreadBase::getAudioPortConfig(config);
+    ThreadBase::toAudioPortConfig(config);
     config->role = AUDIO_PORT_ROLE_SINK;
     config->ext.mix.hw_module = mInput->audioHwDev->handle();
     config->ext.mix.usecase.source = mAudioSource;
+    if (mInput && mInput->flags != AUDIO_INPUT_FLAG_NONE) {
+        config->config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+        config->flags.input = mInput->flags;
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -8454,9 +8516,9 @@
     return status;
 }
 
-void AudioFlinger::MmapThread::getAudioPortConfig(struct audio_port_config *config)
+void AudioFlinger::MmapThread::toAudioPortConfig(struct audio_port_config *config)
 {
-    ThreadBase::getAudioPortConfig(config);
+    ThreadBase::toAudioPortConfig(config);
     if (isOutput()) {
         config->role = AUDIO_PORT_ROLE_SOURCE;
         config->ext.mix.hw_module = mAudioHwDev->handle();
@@ -8840,6 +8902,15 @@
     }
 }
 
+void AudioFlinger::MmapPlaybackThread::toAudioPortConfig(struct audio_port_config *config)
+{
+    MmapThread::toAudioPortConfig(config);
+    if (mOutput && mOutput->flags != AUDIO_OUTPUT_FLAG_NONE) {
+        config->config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+        config->flags.output = mOutput->flags;
+    }
+}
+
 void AudioFlinger::MmapPlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
 {
     MmapThread::dumpInternals(fd, args);
@@ -8930,4 +9001,13 @@
     }
 }
 
+void AudioFlinger::MmapCaptureThread::toAudioPortConfig(struct audio_port_config *config)
+{
+    MmapThread::toAudioPortConfig(config);
+    if (mInput && mInput->flags != AUDIO_INPUT_FLAG_NONE) {
+        config->config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+        config->flags.input = mInput->flags;
+    }
+}
+
 } // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 28d4482..b691ca9 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -281,7 +281,7 @@
     virtual     status_t    createAudioPatch_l(const struct audio_patch *patch,
                                                audio_patch_handle_t *handle) = 0;
     virtual     status_t    releaseAudioPatch_l(const audio_patch_handle_t handle) = 0;
-    virtual     void        getAudioPortConfig(struct audio_port_config *config) = 0;
+    virtual     void        toAudioPortConfig(struct audio_port_config *config) = 0;
 
 
                 // see note at declaration of mStandby, mOutDevice and mInDevice
@@ -434,6 +434,12 @@
     virtual     void        setMasterMono_l(bool mono __unused) { }
     virtual     bool        requireMonoBlend() { return false; }
 
+                            // called within the threadLoop to obtain timestamp from the HAL.
+    virtual     status_t    threadloop_getHalTimestamp_l(
+                                    ExtendedTimestamp *timestamp __unused) const {
+                                return INVALID_OPERATION;
+                            }
+
     friend class AudioFlinger;      // for mEffectChains
 
                 const type_t            mType;
@@ -497,6 +503,9 @@
                 // we must not wait for async write callback in the thread loop before evaluating it
                 bool                    mSignalPending;
 
+#ifdef TEE_SINK
+                NBAIO_Tee               mTee;
+#endif
                 // ActiveTracks is a sorted vector of track type T representing the
                 // active tracks of threadLoop() to be considered by the locked prepare portion.
                 // ActiveTracks should be accessed with the ThreadBase lock held.
@@ -782,7 +791,7 @@
                 void        addPatchTrack(const sp<PatchTrack>& track);
                 void        deletePatchTrack(const sp<PatchTrack>& track);
 
-    virtual     void        getAudioPortConfig(struct audio_port_config *config);
+    virtual     void        toAudioPortConfig(struct audio_port_config *config);
 
                 // Return the asynchronous signal wait time.
     virtual     int64_t     computeWaitTimeNs_l() const { return INT64_MAX; }
@@ -1054,11 +1063,6 @@
     sp<NBAIO_Sink>          mPipeSink;
     // The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink
     sp<NBAIO_Sink>          mNormalSink;
-#ifdef TEE_SINK
-    // For dumpsys
-    sp<NBAIO_Sink>          mTeeSink;
-    sp<NBAIO_Source>        mTeeSource;
-#endif
     uint32_t                mScreenState;   // cached copy of gScreenState
     // TODO: add comment and adjust size as needed
     static const size_t     kFastMixerLogSize = 8 * 1024;
@@ -1152,6 +1156,14 @@
                               return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
                             }
 
+                status_t    threadloop_getHalTimestamp_l(
+                                    ExtendedTimestamp *timestamp) const override {
+                                if (mNormalSink.get() != nullptr) {
+                                    return mNormalSink->getTimestamp(*timestamp);
+                                }
+                                return INVALID_OPERATION;
+                            }
+
 protected:
     virtual     void       setMasterMono_l(bool mono) {
                                mMasterMono.store(mono);
@@ -1316,6 +1328,22 @@
     SortedVector < sp<OutputTrack> >  mOutputTracks;
 public:
     virtual     bool        hasFastMixer() const { return false; }
+                status_t    threadloop_getHalTimestamp_l(
+                                    ExtendedTimestamp *timestamp) const override {
+        if (mOutputTracks.size() > 0) {
+            // forward the first OutputTrack's kernel information for timestamp.
+            const ExtendedTimestamp trackTimestamp =
+                    mOutputTracks[0]->getClientProxyTimestamp();
+            if (trackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] > 0) {
+                timestamp->mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
+                        trackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
+                timestamp->mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
+                        trackTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
+                return OK;  // discard server timestamp - that's ignored.
+            }
+        }
+        return INVALID_OPERATION;
+    }
 };
 
 // record thread
@@ -1374,9 +1402,6 @@
                     audio_devices_t outDevice,
                     audio_devices_t inDevice,
                     bool systemReady
-#ifdef TEE_SINK
-                    , const sp<NBAIO_Sink>& teeSink
-#endif
                     );
             virtual     ~RecordThread();
 
@@ -1437,8 +1462,8 @@
                                            audio_patch_handle_t *handle);
     virtual status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
 
-            void        addPatchRecord(const sp<PatchRecord>& record);
-            void        deletePatchRecord(const sp<PatchRecord>& record);
+            void        addPatchTrack(const sp<PatchRecord>& record);
+            void        deletePatchTrack(const sp<PatchRecord>& record);
 
             void        readInputParameters_l();
     virtual uint32_t    getInputFramesLost();
@@ -1459,7 +1484,7 @@
 
     virtual size_t      frameCount() const { return mFrameCount; }
             bool        hasFastCapture() const { return mFastCapture != 0; }
-    virtual void        getAudioPortConfig(struct audio_port_config *config);
+    virtual void        toAudioPortConfig(struct audio_port_config *config);
 
     virtual status_t    checkEffectCompatibility_l(const effect_descriptor_t *desc,
                                                    audio_session_t sessionId);
@@ -1506,8 +1531,6 @@
             int32_t                             mRsmpInRear;    // last filled frame + 1
 
             // For dumpsys
-            const sp<NBAIO_Sink>                mTeeSink;
-
             const sp<MemoryDealer>              mReadOnlyHeap;
 
             // one-time initialization, no locks required
@@ -1602,7 +1625,7 @@
     virtual     status_t    createAudioPatch_l(const struct audio_patch *patch,
                                                audio_patch_handle_t *handle);
     virtual     status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
-    virtual     void        getAudioPortConfig(struct audio_port_config *config);
+    virtual     void        toAudioPortConfig(struct audio_port_config *config);
 
     virtual     sp<StreamHalInterface> stream() const { return mHalStream; }
     virtual     status_t    addEffectChain_l(const sp<EffectChain>& chain);
@@ -1686,6 +1709,8 @@
 
                 void        updateMetadata_l() override;
 
+    virtual     void        toAudioPortConfig(struct audio_port_config *config);
+
 protected:
 
                 audio_stream_type_t         mStreamType;
@@ -1714,6 +1739,8 @@
                 void           processVolume_l() override;
                 void           setRecordSilenced(uid_t uid, bool silenced) override;
 
+    virtual     void           toAudioPortConfig(struct audio_port_config *config);
+
 protected:
 
                 AudioStreamIn*  mInput;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index ccfb69f..dafba1e 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -100,6 +100,92 @@
 
     audio_attributes_t  attributes() const { return mAttr; }
 
+#ifdef TEE_SINK
+           void         dumpTee(int fd, const std::string &reason) const {
+                                mTee.dump(fd, reason);
+                        }
+#endif
+
+            /** returns the buffer contents size converted to time in milliseconds
+             * for PCM Playback or Record streaming tracks. The return value is zero for
+             * PCM static tracks and not defined for non-PCM tracks.
+             *
+             * This may be called without the thread lock.
+             */
+    virtual double      bufferLatencyMs() const {
+                            return mServerProxy->framesReadySafe() * 1000 / sampleRate();
+                        }
+
+            /** returns whether the track supports server latency computation.
+             * This is set in the constructor and constant throughout the track lifetime.
+             */
+
+            bool        isServerLatencySupported() const { return mServerLatencySupported; }
+
+            /** computes the server latency for PCM Playback or Record track
+             * to the device sink/source.  This is the time for the next frame in the track buffer
+             * written or read from the server thread to the device source or sink.
+             *
+             * This may be called without the thread lock, but latencyMs and fromTrack
+             * may be not be synchronized. For example PatchPanel may not obtain the
+             * thread lock before calling.
+             *
+             * \param latencyMs on success is set to the latency in milliseconds of the
+             *        next frame written/read by the server thread to/from the track buffer
+             *        from the device source/sink.
+             * \param fromTrack on success is set to true if latency was computed directly
+             *        from the track timestamp; otherwise set to false if latency was
+             *        estimated from the server timestamp.
+             *        fromTrack may be nullptr or omitted if not required.
+             *
+             * \returns OK or INVALID_OPERATION on failure.
+             */
+            status_t    getServerLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const {
+                            if (!isServerLatencySupported()) {
+                                return INVALID_OPERATION;
+                            }
+
+                            // if no thread lock is acquired, these atomics are not
+                            // synchronized with each other, considered a benign race.
+
+                            const double serverLatencyMs = mServerLatencyMs.load();
+                            if (serverLatencyMs == 0.) {
+                                return INVALID_OPERATION;
+                            }
+                            if (fromTrack != nullptr) {
+                                *fromTrack = mServerLatencyFromTrack.load();
+                            }
+                            *latencyMs = serverLatencyMs;
+                            return OK;
+                        }
+
+            /** computes the total client latency for PCM Playback or Record tracks
+             * for the next client app access to the device sink/source; i.e. the
+             * server latency plus the buffer latency.
+             *
+             * This may be called without the thread lock, but latencyMs and fromTrack
+             * may be not be synchronized. For example PatchPanel may not obtain the
+             * thread lock before calling.
+             *
+             * \param latencyMs on success is set to the latency in milliseconds of the
+             *        next frame written/read by the client app to/from the track buffer
+             *        from the device sink/source.
+             * \param fromTrack on success is set to true if latency was computed directly
+             *        from the track timestamp; otherwise set to false if latency was
+             *        estimated from the server timestamp.
+             *        fromTrack may be nullptr or omitted if not required.
+             *
+             * \returns OK or INVALID_OPERATION on failure.
+             */
+            status_t    getTrackLatencyMs(double *latencyMs, bool *fromTrack = nullptr) const {
+                            double serverLatencyMs;
+                            status_t status = getServerLatencyMs(&serverLatencyMs, fromTrack);
+                            if (status == OK) {
+                                *latencyMs = serverLatencyMs + bufferLatencyMs();
+                            }
+                            return status;
+                        }
+
 protected:
     DISALLOW_COPY_AND_ASSIGN(TrackBase);
 
@@ -208,13 +294,18 @@
     const bool          mIsOut;
     sp<ServerProxy>     mServerProxy;
     const int           mId;
-    sp<NBAIO_Sink>      mTeeSink;
-    sp<NBAIO_Source>    mTeeSource;
+#ifdef TEE_SINK
+    NBAIO_Tee           mTee;
+#endif
     bool                mTerminated;
     track_type          mType;      // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ...
     audio_io_handle_t   mThreadIoHandle; // I/O handle of the thread the track is attached to
     audio_port_handle_t mPortId; // unique ID for this track used by audio policy
     bool                mIsInvalid; // non-resettable latch, set by invalidate()
+
+    bool                mServerLatencySupported = false;
+    std::atomic<bool>   mServerLatencyFromTrack{}; // latency from track or server timestamp.
+    std::atomic<double> mServerLatencyMs{};        // last latency pushed from server thread.
 };
 
 // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index a7c4253..4fa024e 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -28,11 +28,11 @@
 #include <private/media/AudioTrackShared.h>
 
 #include "AudioFlinger.h"
-#include "ServiceUtilities.h"
 
 #include <media/nbaio/Pipe.h>
 #include <media/nbaio/PipeReader.h>
 #include <media/RecordBufferConverter.h>
+#include <mediautils/ServiceUtilities.h>
 #include <audio_utils/minifloat.h>
 
 // ----------------------------------------------------------------------------
@@ -102,7 +102,7 @@
         mIsInvalid(false)
 {
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (!isTrustedCallingUid(callingUid) || clientUid == AUDIO_UID_INVALID) {
+    if (!isAudioServerOrMediaServerUid(callingUid) || clientUid == AUDIO_UID_INVALID) {
         ALOGW_IF(clientUid != AUDIO_UID_INVALID && clientUid != callingUid,
                 "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, clientUid);
         clientUid = callingUid;
@@ -210,22 +210,7 @@
         mBufferSize = bufferSize;
 
 #ifdef TEE_SINK
-        if (mTeeSinkTrackEnabled) {
-            NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount, mFormat);
-            if (Format_isValid(pipeFormat)) {
-                Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);
-                size_t numCounterOffers = 0;
-                const NBAIO_Format offers[1] = {pipeFormat};
-                ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
-                ALOG_ASSERT(index == 0);
-                PipeReader *pipeReader = new PipeReader(*pipe);
-                numCounterOffers = 0;
-                index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
-                ALOG_ASSERT(index == 0);
-                mTeeSink = pipe;
-                mTeeSource = pipeReader;
-            }
-        }
+        mTee.set(sampleRate, mChannelCount, format, NBAIO_Tee::TEE_FLAG_TRACK);
 #endif
 
     }
@@ -244,9 +229,6 @@
 
 AudioFlinger::ThreadBase::TrackBase::~TrackBase()
 {
-#ifdef TEE_SINK
-    dumpTee(-1, mTeeSource, mId, 'T');
-#endif
     // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
     mServerProxy.clear();
     if (mCblk != NULL) {
@@ -274,9 +256,7 @@
 void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
 {
 #ifdef TEE_SINK
-    if (mTeeSink != 0) {
-        (void) mTeeSink->write(buffer->raw, buffer->frameCount);
-    }
+    mTee.write(buffer->raw, buffer->frameCount);
 #endif
 
     ServerProxy::Buffer buf;
@@ -454,6 +434,14 @@
         thread->mFastTrackAvailMask &= ~(1 << i);
     }
     mName = TRACK_NAME_PENDING;
+
+    mServerLatencySupported = thread->type() == ThreadBase::MIXER
+            || thread->type() == ThreadBase::DUPLICATING;
+#ifdef TEE_SINK
+    mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+            + "_" + std::to_string(mId) +
+            + "_PEND_T");
+#endif
 }
 
 AudioFlinger::PlaybackThread::Track::~Track()
@@ -503,13 +491,14 @@
     }
 }
 
-/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
+void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
 {
-    result.append("T Name Active Client Session S  Flags "
+    result.appendFormat("T Name Active Client Session S  Flags "
                   "  Format Chn mask  SRate "
                   "ST  L dB  R dB  VS dB "
-                  "  Server FrmCnt  FrmRdy F Underruns  Flushed "
-                  "Main Buf  Aux Buf\n");
+                  "  Server FrmCnt  FrmRdy F Underruns  Flushed"
+                  "%s\n",
+                  isServerLatencySupported() ? "   Latency" : "");
 }
 
 void AudioFlinger::PlaybackThread::Track::appendDump(String8& result, bool active)
@@ -518,7 +507,7 @@
     switch (mType) {
     case TYPE_DEFAULT:
     case TYPE_OUTPUT:
-        if (mSharedBuffer.get() != nullptr) {
+        if (isStatic()) {
             trackType = 'S'; // static
         } else {
             trackType = ' '; // normal
@@ -596,17 +585,16 @@
     result.appendFormat("%7s %6u %7u %2s 0x%03X "
                            "%08X %08X %6u "
                            "%2u %5.2g %5.2g %5.2g%c "
-                           "%08X %6zu%c %6zu %c %9u%c %7u "
-                           "%08zX %08zX\n",
+                           "%08X %6zu%c %6zu %c %9u%c %7u",
             active ? "yes" : "no",
-            (mClient == 0) ? getpid_cached : mClient->pid(),
+            (mClient == 0) ? getpid() : mClient->pid(),
             mSessionId,
             getTrackStateString(),
             mCblk->mFlags,
 
             mFormat,
             mChannelMask,
-            mAudioTrackServerProxy->getSampleRate(),
+            sampleRate(),
 
             mStreamType,
             20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))),
@@ -621,11 +609,21 @@
             fillingStatus,
             mAudioTrackServerProxy->getUnderrunFrames(),
             nowInUnderrun,
-            (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000,
-
-            (size_t)mMainBuffer, // use %zX as %p appends 0x
-            (size_t)mAuxBuffer   // use %zX as %p appends 0x
+            (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000
             );
+
+    if (isServerLatencySupported()) {
+        double latencyMs;
+        bool fromTrack;
+        if (getTrackLatencyMs(&latencyMs, &fromTrack) == OK) {
+            // Show latency in msec, followed by 't' if from track timestamp (the most accurate)
+            // or 'k' if estimated from kernel because track frames haven't been presented yet.
+            result.appendFormat(" %7.2lf %c", latencyMs, fromTrack ? 't' : 'k');
+        } else {
+            result.appendFormat("%10s", mCblk->mServer != 0 ? "unavail" : "new");
+        }
+    }
+    result.append("\n");
 }
 
 uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
@@ -682,6 +680,13 @@
     mAudioTrackServerProxy->setTimestamp(timestamp);
 
     // We do not set drained here, as FastTrack timestamp may not go to very last frame.
+
+    // Compute latency.
+    // TODO: Consider whether the server latency may be passed in by FastMixer
+    // as a constant for all active FastTracks.
+    const double latencyMs = timestamp.getOutputServerLatencyMs(sampleRate());
+    mServerLatencyFromTrack.store(true);
+    mServerLatencyMs.store(latencyMs);
 }
 
 // Don't call for fast tracks; the framesReady() could result in priority inversion
@@ -1246,7 +1251,7 @@
 //To be called with thread lock held
 void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo(
         int64_t trackFramesReleased, int64_t sinkFramesWritten,
-        const ExtendedTimestamp &timeStamp) {
+        uint32_t halSampleRate, const ExtendedTimestamp &timeStamp) {
     //update frame map
     mFrameMap.push(trackFramesReleased, sinkFramesWritten);
 
@@ -1255,6 +1260,7 @@
     // Our timestamps are only updated when the track is on the Thread active list.
     // We need to ensure that tracks are not removed before full drain.
     ExtendedTimestamp local = timeStamp;
+    bool drained = true; // default assume drained, if no server info found
     bool checked = false;
     for (int i = ExtendedTimestamp::LOCATION_MAX - 1;
             i >= ExtendedTimestamp::LOCATION_SERVER; --i) {
@@ -1263,18 +1269,25 @@
             local.mPosition[i] = mFrameMap.findX(local.mPosition[i]);
             // check drain state from the latest stage in the pipeline.
             if (!checked && i <= ExtendedTimestamp::LOCATION_KERNEL) {
-                mAudioTrackServerProxy->setDrained(
-                        local.mPosition[i] >= mAudioTrackServerProxy->framesReleased());
+                drained = local.mPosition[i] >= mAudioTrackServerProxy->framesReleased();
                 checked = true;
             }
         }
     }
-    if (!checked) { // no server info, assume drained.
-        mAudioTrackServerProxy->setDrained(true);
-    }
+
+    mAudioTrackServerProxy->setDrained(drained);
     // Set correction for flushed frames that are not accounted for in released.
     local.mFlushed = mAudioTrackServerProxy->framesFlushed();
     mServerProxy->setTimestamp(local);
+
+    // Compute latency info.
+    const bool useTrackTimestamp = !drained;
+    const double latencyMs = useTrackTimestamp
+            ? local.getOutputServerLatencyMs(sampleRate())
+            : timeStamp.getOutputServerLatencyMs(halSampleRate);
+
+    mServerLatencyFromTrack.store(useTrackTimestamp);
+    mServerLatencyMs.store(latencyMs);
 }
 
 // ----------------------------------------------------------------------------
@@ -1342,7 +1355,7 @@
     mActive = false;
 }
 
-bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames)
+ssize_t AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames)
 {
     Buffer *pInBuffer;
     Buffer inBuffer;
@@ -1431,9 +1444,12 @@
                 mBufferQueue.add(pInBuffer);
                 ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %zu", this,
                         mThread.unsafe_get(), mBufferQueue.size());
+                // audio data is consumed (stored locally); set frameCount to 0.
+                inBuffer.frameCount = 0;
             } else {
                 ALOGW("OutputTrack::write() %p thread %p no more overflow buffers",
                         mThread.unsafe_get(), this);
+                // TODO: return error for this.
             }
         }
     }
@@ -1444,7 +1460,7 @@
         stop();
     }
 
-    return outputBufferFull;
+    return frames - inBuffer.frameCount;  // number of frames consumed.
 }
 
 void AudioFlinger::PlaybackThread::OutputTrack::copyMetadataTo(MetadataInserter& backInserter) const
@@ -1509,7 +1525,7 @@
               audio_attributes_t{} /* currently unused for patch track */,
               sampleRate, format, channelMask, frameCount,
               buffer, bufferSize, nullptr /* sharedBuffer */,
-              AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH),
+              AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH),
               mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
 {
     uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) /
@@ -1694,7 +1710,15 @@
     if (flags & AUDIO_INPUT_FLAG_FAST) {
         ALOG_ASSERT(thread->mFastTrackAvail);
         thread->mFastTrackAvail = false;
+    } else {
+        // TODO: only Normal Record has timestamps (Fast Record does not).
+        mServerLatencySupported = true;
     }
+#ifdef TEE_SINK
+    mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+            + "_" + std::to_string(mId)
+            + "_R");
+#endif
 }
 
 AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -1783,19 +1807,20 @@
 }
 
 
-/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
+void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
 {
-    result.append("Active Client Session S  Flags   Format Chn mask  SRate   Server FrmCnt Sil\n");
+    result.appendFormat("Active Client Session S  Flags   Format Chn mask  SRate   Server"
+            " FrmCnt FrmRdy Sil%s\n", isServerLatencySupported() ? "   Latency" : "");
 }
 
 void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool active)
 {
     result.appendFormat("%c%5s %6u %7u %2s 0x%03X "
             "%08X %08X %6u "
-            "%08X %6zu %3c\n",
+            "%08X %6zu %6zu %3c",
             isFastTrack() ? 'F' : ' ',
             active ? "yes" : "no",
-            (mClient == 0) ? getpid_cached : mClient->pid(),
+            (mClient == 0) ? getpid() : mClient->pid(),
             mSessionId,
             getTrackStateString(),
             mCblk->mFlags,
@@ -1806,8 +1831,21 @@
 
             mCblk->mServer,
             mFrameCount,
+            mServerProxy->framesReadySafe(),
             isSilenced() ? 's' : 'n'
             );
+    if (isServerLatencySupported()) {
+        double latencyMs;
+        bool fromTrack;
+        if (getTrackLatencyMs(&latencyMs, &fromTrack) == OK) {
+            // Show latency in msec, followed by 't' if from track timestamp (the most accurate)
+            // or 'k' if estimated from kernel (usually for debugging).
+            result.appendFormat(" %7.2lf %c", latencyMs, fromTrack ? 't' : 'k');
+        } else {
+            result.appendFormat("%10s", mCblk->mServer != 0 ? "unavail" : "new");
+        }
+    }
+    result.append("\n");
 }
 
 void AudioFlinger::RecordThread::RecordTrack::handleSyncStartEvent(const sp<SyncEvent>& event)
@@ -1850,6 +1888,15 @@
         }
     }
     mServerProxy->setTimestamp(local);
+
+    // Compute latency info.
+    const bool useTrackTimestamp = true; // use track unless debugging.
+    const double latencyMs = - (useTrackTimestamp
+            ? local.getOutputServerLatencyMs(sampleRate())
+            : timestamp.getOutputServerLatencyMs(halSampleRate));
+
+    mServerLatencyFromTrack.store(useTrackTimestamp);
+    mServerLatencyMs.store(latencyMs);
 }
 
 status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones(
@@ -1875,7 +1922,8 @@
     :   RecordTrack(recordThread, NULL,
                 audio_attributes_t{} /* currently unused for patch track */,
                 sampleRate, format, channelMask, frameCount,
-                buffer, bufferSize, AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH),
+                buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER,
+                flags, TYPE_PATCH),
                 mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true))
 {
     uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) /
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index d29cae1..b75e957 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -13,7 +13,6 @@
     $(call include-path-for, audio-utils) \
     frameworks/av/services/audiopolicy/common/include \
     frameworks/av/services/audiopolicy/engine/interface \
-    frameworks/av/services/audiopolicy/utilities
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
@@ -22,10 +21,10 @@
     libbinder \
     libaudioclient \
     libhardware_legacy \
-    libserviceutility \
     libaudiopolicymanager \
     libmedia_helper \
     libmediametrics \
+    libmediautils \
     libeffectsconfig
 
 LOCAL_STATIC_LIBRARIES := \
@@ -74,7 +73,6 @@
 LOCAL_C_INCLUDES += \
     frameworks/av/services/audiopolicy/common/include \
     frameworks/av/services/audiopolicy/engine/interface \
-    frameworks/av/services/audiopolicy/utilities
 
 LOCAL_STATIC_LIBRARIES := \
     libaudiopolicycomponents
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index e69e687..b3611c4 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -35,8 +35,7 @@
     $(LOCAL_PATH)/include \
     frameworks/av/services/audiopolicy/common/include \
     frameworks/av/services/audiopolicy \
-    frameworks/av/services/audiopolicy/utilities \
-    system/media/audio_utils/include \
+    $(call include-path-for, audio-utils) \
 
 ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSessionInfoProvider.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
similarity index 67%
rename from services/audiopolicy/common/managerdefinitions/include/AudioSessionInfoProvider.h
rename to services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
index e0037fc..9f3fc0c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSessionInfoProvider.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
@@ -19,26 +19,27 @@
 namespace android {
 
 /**
- * Interface for input descriptors to implement so dependent audio sessions can query information
- * about their context
+ * Interface for I/O descriptors to implement so information about their context
+ * can be queried and updated.
  */
-class AudioSessionInfoProvider
+class AudioIODescriptorInterface
 {
 public:
-    virtual ~AudioSessionInfoProvider() {};
+    virtual ~AudioIODescriptorInterface() {};
 
     virtual audio_config_base_t getConfig() const = 0;
 
     virtual audio_patch_handle_t getPatchHandle() const = 0;
 
+    virtual void setPatchHandle(audio_patch_handle_t handle) = 0;
 };
 
-class AudioSessionInfoUpdateListener
+class AudioIODescriptorUpdateListener
 {
 public:
-    virtual ~AudioSessionInfoUpdateListener() {};
+    virtual ~AudioIODescriptorUpdateListener() {};
 
-    virtual void onSessionInfoUpdate() const = 0;;
+    virtual void onIODescriptorUpdate() const = 0;
 };
 
 } // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index b25d6d4..85f3b86 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -16,9 +16,9 @@
 
 #pragma once
 
+#include "AudioIODescriptorInterface.h"
 #include "AudioPort.h"
 #include "AudioSession.h"
-#include "AudioSessionInfoProvider.h"
 #include <utils/Errors.h>
 #include <system/audio.h>
 #include <utils/SortedVector.h>
@@ -31,7 +31,7 @@
 
 // descriptor for audio inputs. Used to maintain current configuration of each opened audio input
 // and keep track of the usage of this input.
-class AudioInputDescriptor: public AudioPortConfig, public AudioSessionInfoProvider
+class AudioInputDescriptor: public AudioPortConfig, public AudioIODescriptorInterface
 {
 public:
     explicit AudioInputDescriptor(const sp<IOProfile>& profile,
@@ -67,11 +67,10 @@
     size_t getAudioSessionCount(bool activeOnly) const;
     audio_source_t getHighestPrioritySource(bool activeOnly) const;
 
-    // implementation of AudioSessionInfoProvider
-    virtual audio_config_base_t getConfig() const;
-    virtual audio_patch_handle_t getPatchHandle() const;
-
-    void setPatchHandle(audio_patch_handle_t handle);
+    // implementation of AudioIODescriptorInterface
+    audio_config_base_t getConfig() const override;
+    audio_patch_handle_t getPatchHandle() const override;
+    void setPatchHandle(audio_patch_handle_t handle) override;
 
     status_t open(const audio_config_t *config,
                   audio_devices_t device,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 5e5d38b..57d1cfa 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -24,6 +24,7 @@
 #include <utils/Timers.h>
 #include <utils/KeyedVector.h>
 #include <system/audio.h>
+#include "AudioIODescriptorInterface.h"
 #include "AudioSourceDescriptor.h"
 
 namespace android {
@@ -35,7 +36,7 @@
 
 // descriptor for audio outputs. Used to maintain current configuration of each opened audio output
 // and keep track of the usage of this output by each audio stream type.
-class AudioOutputDescriptor: public AudioPortConfig
+class AudioOutputDescriptor: public AudioPortConfig, public AudioIODescriptorInterface
 {
 public:
     AudioOutputDescriptor(const sp<AudioPort>& port,
@@ -73,8 +74,10 @@
 
     audio_module_handle_t getModuleHandle() const;
 
-    audio_patch_handle_t getPatchHandle() const { return mPatchHandle; };
-    void setPatchHandle(audio_patch_handle_t handle) { mPatchHandle = handle; };
+    // implementation of AudioIODescriptorInterface
+    audio_config_base_t getConfig() const override;
+    audio_patch_handle_t getPatchHandle() const override;
+    void setPatchHandle(audio_patch_handle_t handle) override;
 
     sp<AudioPort>       mPort;
     audio_devices_t mDevice;                   // current device this output is routed to
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index 43f6ed6..f747c36 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -48,6 +48,14 @@
           mIsSpeakerDrcEnabled(false)
     {}
 
+    const std::string& getSource() const {
+        return mSource;
+    }
+
+    void setSource(const std::string& file) {
+        mSource = file;
+    }
+
     void setVolumes(const VolumeCurvesCollection &volumes)
     {
         if (mVolumeCurves != nullptr) {
@@ -107,6 +115,7 @@
 
     void setDefault(void)
     {
+        mSource = "AudioPolicyConfig::setDefault";
         mDefaultOutputDevices = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
         sp<HwModule> module;
         sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
@@ -136,6 +145,7 @@
     }
 
 private:
+    std::string mSource;
     HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
     DeviceVector &mAvailableOutputDevices;
     DeviceVector &mAvailableInputDevices;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index 09a86dd..bd7517f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -153,9 +153,6 @@
 class AudioPortConfig : public virtual RefBase
 {
 public:
-    AudioPortConfig();
-    virtual ~AudioPortConfig() {}
-
     status_t applyAudioPortConfig(const struct audio_port_config *config,
                                   struct audio_port_config *backupConfig = NULL);
     virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
@@ -165,10 +162,11 @@
         return (other != 0) &&
                 (other->getAudioPort()->getModuleHandle() == getAudioPort()->getModuleHandle());
     }
-    uint32_t mSamplingRate;
-    audio_format_t mFormat;
-    audio_channel_mask_t mChannelMask;
-    struct audio_gain_config mGain;
+    unsigned int mSamplingRate = 0u;
+    audio_format_t mFormat = AUDIO_FORMAT_INVALID;
+    audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE;
+    struct audio_gain_config mGain = { .index = -1 };
+    union audio_io_flags mFlags = { AUDIO_INPUT_FLAG_NONE };
 };
 
 } // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
index dd5247d..53e6ec9 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
@@ -23,13 +23,13 @@
 #include <utils/KeyedVector.h>
 #include <media/AudioPolicy.h>
 #include <media/IAudioPolicyServiceClient.h>
-#include "AudioSessionInfoProvider.h"
+#include "AudioIODescriptorInterface.h"
 
 namespace android {
 
 class AudioPolicyClientInterface;
 
-class AudioSession : public RefBase, public AudioSessionInfoUpdateListener
+class AudioSession : public RefBase, public AudioIODescriptorUpdateListener
 {
 public:
     AudioSession(audio_session_t session,
@@ -63,9 +63,9 @@
     uint32_t changeOpenCount(int delta);
     uint32_t changeActiveCount(int delta);
 
-    void setInfoProvider(AudioSessionInfoProvider *provider);
-    // implementation of AudioSessionInfoUpdateListener
-    virtual void onSessionInfoUpdate() const;
+    void setInfoProvider(AudioIODescriptorInterface *provider);
+    // implementation of AudioIODescriptorUpdateListener
+    virtual void onIODescriptorUpdate() const;
 
 private:
     record_client_info_t mRecordClientInfo;
@@ -77,17 +77,17 @@
     uint32_t  mActiveCount;
     AudioMix* mPolicyMix; // non NULL when used by a dynamic policy
     AudioPolicyClientInterface* mClientInterface;
-    const AudioSessionInfoProvider* mInfoProvider;
+    const AudioIODescriptorInterface* mInfoProvider;
 };
 
 class AudioSessionCollection :
     public DefaultKeyedVector<audio_session_t, sp<AudioSession> >,
-    public AudioSessionInfoUpdateListener
+    public AudioIODescriptorUpdateListener
 {
 public:
     status_t addSession(audio_session_t session,
                              const sp<AudioSession>& audioSession,
-                             AudioSessionInfoProvider *provider);
+                             AudioIODescriptorInterface *provider);
 
     status_t removeSession(audio_session_t session);
 
@@ -99,8 +99,8 @@
     bool isSourceActive(audio_source_t source) const;
     audio_source_t getHighestPrioritySource(bool activeOnly) const;
 
-    // implementation of AudioSessionInfoUpdateListener
-    virtual void onSessionInfoUpdate() const;
+    // implementation of AudioIODescriptorUpdateListener
+    virtual void onIODescriptorUpdate() const;
 
     status_t dump(int fd, int spaces) const;
 };
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 92a4c3e..2325e4f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -76,11 +76,12 @@
 
     audio_devices_t types() const { return mDeviceTypes; }
 
-    sp<DeviceDescriptor> getDevice(audio_devices_t type, const String8& address) const;
-    DeviceVector getDevicesFromType(audio_devices_t types) const;
+    // If 'address' is empty, a device with a non-empty address may be returned
+    // if there is no device with the specified 'type' and empty address.
+    sp<DeviceDescriptor> getDevice(audio_devices_t type, const String8 &address) const;
+    DeviceVector getDevicesFromTypeMask(audio_devices_t types) const;
     sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
     sp<DeviceDescriptor> getDeviceFromTagName(const String8 &tagName) const;
-    DeviceVector getDevicesFromTypeAddr(audio_devices_t type, const String8& address) const;
 
     audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const;
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
index cb9f49e..05cfc31 100644
--- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h
+++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
@@ -107,7 +107,7 @@
     sp<DeviceDescriptor> getDeviceDescriptor(const audio_devices_t device,
                                              const char *device_address,
                                              const char *device_name,
-                                             bool matchAdress = true) const;
+                                             bool matchAddress = true) const;
 
     status_t dump(int fd) const;
 };
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 92332fb..f0144db 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -164,7 +164,7 @@
 
 status_t AudioInputDescriptor::addAudioSession(audio_session_t session,
                          const sp<AudioSession>& audioSession) {
-    return mSessions.addSession(session, audioSession, /*AudioSessionInfoProvider*/this);
+    return mSessions.addSession(session, audioSession, /*AudioIODescriptorInterface*/this);
 }
 
 status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) {
@@ -179,7 +179,7 @@
 void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle)
 {
     mPatchHandle = handle;
-    mSessions.onSessionInfoUpdate();
+    mSessions.onIODescriptorUpdate();
 }
 
 audio_config_base_t AudioInputDescriptor::getConfig() const
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 294a2a6..3c69de5 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -55,11 +55,28 @@
     }
 }
 
+audio_config_base_t AudioOutputDescriptor::getConfig() const
+{
+    const audio_config_base_t config = { .sample_rate = mSamplingRate, .channel_mask = mChannelMask,
+            .format = mFormat };
+    return config;
+}
+
 audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const
 {
     return mPort.get() != nullptr ? mPort->getModuleHandle() : AUDIO_MODULE_HANDLE_NONE;
 }
 
+audio_patch_handle_t AudioOutputDescriptor::getPatchHandle() const
+{
+    return mPatchHandle;
+}
+
+void AudioOutputDescriptor::setPatchHandle(audio_patch_handle_t handle)
+{
+    mPatchHandle = handle;
+}
+
 audio_port_handle_t AudioOutputDescriptor::getId() const
 {
     return mId;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
index a9fe48d..e78e121 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
@@ -34,51 +34,32 @@
 {
 }
 
+static String8 dumpPatchEndpoints(
+        int spaces, const char *prefix, int count, const audio_port_config *cfgs)
+{
+    String8 result;
+    for (int i = 0; i < count; ++i) {
+        const audio_port_config &cfg = cfgs[i];
+        result.appendFormat("%*s  [%s %d] ", spaces, "", prefix, i + 1);
+        if (cfg.type == AUDIO_PORT_TYPE_DEVICE) {
+            std::string device;
+            deviceToString(cfg.ext.device.type, device);
+            result.appendFormat("Device ID %d %s", cfg.id, device.c_str());
+        } else {
+            result.appendFormat("Mix ID %d I/O handle %d", cfg.id, cfg.ext.mix.handle);
+        }
+        result.append("\n");
+    }
+    return result;
+}
+
 status_t AudioPatch::dump(int fd, int spaces, int index) const
 {
-    const size_t SIZE = 256;
-    char buffer[SIZE];
     String8 result;
-
-    snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid);
-    result.append(buffer);
-    snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources);
-    result.append(buffer);
-    for (size_t i = 0; i < mPatch.num_sources; i++) {
-        if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) {
-            std::string device;
-            deviceToString(mPatch.sources[i].ext.device.type, device);
-            snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
-                     mPatch.sources[i].id,
-                     device.c_str());
-        } else {
-            snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
-                     mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle);
-        }
-        result.append(buffer);
-    }
-    snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks);
-    result.append(buffer);
-    for (size_t i = 0; i < mPatch.num_sinks; i++) {
-        if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) {
-            std::string device;
-            deviceToString(mPatch.sinks[i].ext.device.type, device);
-            snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
-                     mPatch.sinks[i].id,
-                     device.c_str());
-        } else {
-            snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
-                     mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle);
-        }
-        result.append(buffer);
-    }
-
+    result.appendFormat("%*sPatch %d: owner uid %4d, handle %2d, af handle %2d\n",
+            spaces, "", index + 1, mUid, mHandle, mAfPatchHandle);
+    result.append(dumpPatchEndpoints(spaces, "src ", mPatch.num_sources, mPatch.sources));
+    result.append(dumpPatchEndpoints(spaces, "sink", mPatch.num_sinks, mPatch.sinks));
     write(fd, result.string(), result.size());
     return NO_ERROR;
 }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index d85562e..3fe37ab 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -386,22 +386,12 @@
 
 // --- AudioPortConfig class implementation
 
-AudioPortConfig::AudioPortConfig()
-{
-    mSamplingRate = 0;
-    mChannelMask = AUDIO_CHANNEL_NONE;
-    mFormat = AUDIO_FORMAT_INVALID;
-    memset(&mGain, 0, sizeof(struct audio_gain_config));
-    mGain.index = -1;
-}
-
 status_t AudioPortConfig::applyAudioPortConfig(const struct audio_port_config *config,
                                                struct audio_port_config *backupConfig)
 {
-    struct audio_port_config localBackupConfig;
+    struct audio_port_config localBackupConfig = { .config_mask = config->config_mask };
     status_t status = NO_ERROR;
 
-    localBackupConfig.config_mask = config->config_mask;
     toAudioPortConfig(&localBackupConfig);
 
     sp<AudioPort> audioport = getAudioPort();
@@ -425,6 +415,9 @@
     if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) {
         mGain = config->gain;
     }
+    if (config->config_mask & AUDIO_PORT_CONFIG_FLAGS) {
+        mFlags = config->flags;
+    }
 
 exit:
     if (status != NO_ERROR) {
@@ -436,33 +429,38 @@
     return status;
 }
 
+namespace {
+
+template<typename T>
+void updateField(
+        const T& portConfigField, T audio_port_config::*port_config_field,
+        struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig,
+        unsigned int configMask, T defaultValue)
+{
+    if (dstConfig->config_mask & configMask) {
+        if ((srcConfig != nullptr) && (srcConfig->config_mask & configMask)) {
+            dstConfig->*port_config_field = srcConfig->*port_config_field;
+        } else {
+            dstConfig->*port_config_field = portConfigField;
+        }
+    } else {
+        dstConfig->*port_config_field = defaultValue;
+    }
+}
+
+} // namespace
+
 void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig,
                                         const struct audio_port_config *srcConfig) const
 {
-    if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
-        dstConfig->sample_rate = mSamplingRate;
-        if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) {
-            dstConfig->sample_rate = srcConfig->sample_rate;
-        }
-    } else {
-        dstConfig->sample_rate = 0;
-    }
-    if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
-        dstConfig->channel_mask = mChannelMask;
-        if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) {
-            dstConfig->channel_mask = srcConfig->channel_mask;
-        }
-    } else {
-        dstConfig->channel_mask = AUDIO_CHANNEL_NONE;
-    }
-    if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
-        dstConfig->format = mFormat;
-        if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) {
-            dstConfig->format = srcConfig->format;
-        }
-    } else {
-        dstConfig->format = AUDIO_FORMAT_INVALID;
-    }
+    updateField(mSamplingRate, &audio_port_config::sample_rate,
+            dstConfig, srcConfig, AUDIO_PORT_CONFIG_SAMPLE_RATE, 0u);
+    updateField(mChannelMask, &audio_port_config::channel_mask,
+            dstConfig, srcConfig, AUDIO_PORT_CONFIG_CHANNEL_MASK,
+            (audio_channel_mask_t)AUDIO_CHANNEL_NONE);
+    updateField(mFormat, &audio_port_config::format,
+            dstConfig, srcConfig, AUDIO_PORT_CONFIG_FORMAT, AUDIO_FORMAT_INVALID);
+
     sp<AudioPort> audioport = getAudioPort();
     if ((dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) && audioport != NULL) {
         dstConfig->gain = mGain;
@@ -478,6 +476,9 @@
     } else {
         dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN;
     }
+
+    updateField(mFlags, &audio_port_config::flags,
+            dstConfig, srcConfig, AUDIO_PORT_CONFIG_FLAGS, { AUDIO_INPUT_FLAG_NONE });
 }
 
 } // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
index 7cda46b..91dee35 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
@@ -86,7 +86,7 @@
         }
 
         // Recording configuration callback:
-        const AudioSessionInfoProvider* provider = mInfoProvider;
+        const AudioIODescriptorInterface* provider = mInfoProvider;
         const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() :
                 AUDIO_CONFIG_BASE_INITIALIZER;
         const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() :
@@ -114,16 +114,16 @@
     return false;
 }
 
-void AudioSession::setInfoProvider(AudioSessionInfoProvider *provider)
+void AudioSession::setInfoProvider(AudioIODescriptorInterface *provider)
 {
     mInfoProvider = provider;
 }
 
-void AudioSession::onSessionInfoUpdate() const
+void AudioSession::onIODescriptorUpdate() const
 {
     if (mActiveCount > 0) {
         // resend the callback after requerying the informations from the info provider
-        const AudioSessionInfoProvider* provider = mInfoProvider;
+        const AudioIODescriptorInterface* provider = mInfoProvider;
         const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() :
                 AUDIO_CONFIG_BASE_INITIALIZER;
         const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() :
@@ -170,7 +170,7 @@
 
 status_t AudioSessionCollection::addSession(audio_session_t session,
                                          const sp<AudioSession>& audioSession,
-                                         AudioSessionInfoProvider *provider)
+                                         AudioIODescriptorInterface *provider)
 {
     ssize_t index = indexOfKey(session);
 
@@ -271,10 +271,10 @@
     return source;
 }
 
-void AudioSessionCollection::onSessionInfoUpdate() const
+void AudioSessionCollection::onIODescriptorUpdate() const
 {
     for (size_t i = 0; i < size(); i++) {
-        valueAt(i)->onSessionInfoUpdate();
+        valueAt(i)->onIODescriptorUpdate();
     }
 }
 
diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
index 1e105f5..19eac26 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
@@ -412,6 +412,7 @@
     free(data);
 
     ALOGI("loadAudioPolicyConfig() loaded %s\n", path);
+    config.setSource(path);
 
     return NO_ERROR;
 }
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 19c2062..d3cc8b9 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -145,8 +145,8 @@
             }
         }
     }
-    ALOGV("DeviceVector::getDevice() for type %08x address %s found %p",
-          type, address.string(), device.get());
+    ALOGV("DeviceVector::%s() for type %08x address \"%s\" found %p",
+            __func__, type, address.string(), device.get());
     return device;
 }
 
@@ -160,7 +160,7 @@
     return nullptr;
 }
 
-DeviceVector DeviceVector::getDevicesFromType(audio_devices_t type) const
+DeviceVector DeviceVector::getDevicesFromTypeMask(audio_devices_t type) const
 {
     DeviceVector devices;
     bool isOutput = audio_is_output_devices(type);
@@ -171,20 +171,8 @@
         if ((isOutput == curIsOutput) && ((type & curType) != 0)) {
             devices.add(itemAt(i));
             type &= ~curType;
-            ALOGV("DeviceVector::getDevicesFromType() for type %x found %p",
-                  itemAt(i)->type(), itemAt(i).get());
-        }
-    }
-    return devices;
-}
-
-DeviceVector DeviceVector::getDevicesFromTypeAddr(
-        audio_devices_t type, const String8& address) const
-{
-    DeviceVector devices;
-    for (const auto& device : *this) {
-        if (device->type() == type && device->mAddress == address) {
-            devices.add(device);
+            ALOGV("DeviceVector::%s() for type %08x found %p",
+                    __func__, itemAt(i)->type(), itemAt(i).get());
         }
     }
     return devices;
@@ -253,7 +241,7 @@
 
 void DeviceDescriptor::toAudioPort(struct audio_port *port) const
 {
-    ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType);
+    ALOGV("DeviceDescriptor::toAudioPort() handle %d type %08x", mId, mDeviceType);
     AudioPort::toAudioPort(port);
     port->id = mId;
     toAudioPortConfig(&port->active_config);
@@ -305,7 +293,7 @@
 {
     std::string device;
     deviceToString(mDeviceType, device);
-    ALOGI("Device id:%d type:0x%X:%s, addr:%s", mId,  mDeviceType, device.c_str(),
+    ALOGI("Device id:%d type:0x%08X:%s, addr:%s", mId,  mDeviceType, device.c_str(),
           mAddress.string());
 
     AudioPort::log("  ");
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index aef7dbe..dcc0ec8 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -278,9 +278,10 @@
 sp<DeviceDescriptor> HwModuleCollection::getDeviceDescriptor(const audio_devices_t device,
                                                              const char *device_address,
                                                              const char *device_name,
-                                                             bool matchAdress) const
+                                                             bool matchAddress) const
 {
-    String8 address = (device_address == nullptr) ? String8("") : String8(device_address);
+    String8 address = (device_address == nullptr || !matchAddress) ?
+            String8("") : String8(device_address);
     // handle legacy remote submix case where the address was not always specified
     if (device_distinguishes_on_address(device) && (address.length() == 0)) {
         address = String8("0");
@@ -288,15 +289,9 @@
 
     for (const auto& hwModule : *this) {
         DeviceVector declaredDevices = hwModule->getDeclaredDevices();
-        DeviceVector deviceList = declaredDevices.getDevicesFromTypeAddr(device, address);
-        if (!deviceList.isEmpty()) {
-            return deviceList.itemAt(0);
-        }
-        if (!matchAdress) {
-            deviceList = declaredDevices.getDevicesFromType(device);
-            if (!deviceList.isEmpty()) {
-                return deviceList.itemAt(0);
-            }
+        sp<DeviceDescriptor> deviceDesc = declaredDevices.getDevice(device, address);
+        if (deviceDesc) {
+            return deviceDesc;
         }
     }
 
diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
index d34214b..38ab560 100644
--- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
@@ -129,7 +129,7 @@
         if (streamType == route->mStreamType && route->isActiveOrChanged()
                 && route->mDeviceDescriptor != 0) {
             device = route->mDeviceDescriptor->type();
-            if (!availableDevices.getDevicesFromType(device).isEmpty()) {
+            if (!availableDevices.getDevicesFromTypeMask(device).isEmpty()) {
                 break;
             }
         }
diff --git a/services/audiopolicy/config/audio_policy_configuration.xml b/services/audiopolicy/config/audio_policy_configuration.xml
index a75f1cb..9381f1f 100644
--- a/services/audiopolicy/config/audio_policy_configuration.xml
+++ b/services/audiopolicy/config/audio_policy_configuration.xml
@@ -185,6 +185,9 @@
         <!-- Hearing aid Audio HAL -->
         <xi:include href="hearing_aid_audio_policy_configuration.xml"/>
 
+        <!-- MSD Audio HAL (optional) -->
+        <xi:include href="msd_audio_policy_configuration.xml"/>
+
     </modules>
     <!-- End of Modules section -->
 
diff --git a/services/audiopolicy/config/msd_audio_policy_configuration.xml b/services/audiopolicy/config/msd_audio_policy_configuration.xml
new file mode 100644
index 0000000..a84117e
--- /dev/null
+++ b/services/audiopolicy/config/msd_audio_policy_configuration.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017-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.
+-->
+<!-- Multi Stream Decoder Audio Policy Configuration file -->
+<module name="msd" halVersion="2.0">
+    <attachedDevices>
+        <item>MS12 Input</item>
+        <item>MS12 Output</item>
+    </attachedDevices>
+    <mixPorts>
+        <mixPort name="ms12 input" role="source">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+        </mixPort>
+        <mixPort name="ms12 compressed input" role="source"
+                flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING">
+            <profile name="" format="AUDIO_FORMAT_AC3"
+                     samplingRates="32000,44100,48000"
+                     channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1"/>
+            <profile name="" format="AUDIO_FORMAT_E_AC3"
+                     samplingRates="32000,44100,48000"
+                     channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
+            <profile name="" format="AUDIO_FORMAT_AC4"
+                     samplingRates="32000,44100,48000"
+                     channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
+        </mixPort>
+        <mixPort name="ms12 output" role="sink">
+            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                     samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+            <profile name="" format="AUDIO_FORMAT_AC3"
+                     samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_5POINT1"/>
+            <profile name="" format="AUDIO_FORMAT_E_AC3"
+                     samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_5POINT1"/>
+        </mixPort>
+   </mixPorts>
+   <devicePorts>
+       <devicePort tagName="MS12 Input" type="AUDIO_DEVICE_OUT_BUS"  role="sink">
+           <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                    samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+       </devicePort>
+       <devicePort tagName="MS12 Output" type="AUDIO_DEVICE_IN_BUS"  role="source">
+           <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                    samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+        </devicePort>
+    </devicePorts>
+    <routes>
+        <route type="mix" sink="MS12 Input" sources="ms12 input,ms12 compressed input"/>
+        <route type="mix" sink="ms12 output" sources="MS12 Output"/>
+    </routes>
+</module>
diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk
index 36e0f42..b128a38 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk
+++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk
@@ -10,7 +10,6 @@
     $(LOCAL_PATH)/include \
     frameworks/av/services/audiopolicy/engineconfigurable/include \
     frameworks/av/services/audiopolicy/engineconfigurable/interface \
-    frameworks/av/services/audiopolicy/utilities/convert \
 
 LOCAL_SRC_FILES:= ParameterManagerWrapper.cpp
 
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 3e13e50..007eea0 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -482,7 +482,7 @@
                 }
             }
             availableOutputDevices =
-                    availableOutputDevices.getDevicesFromType(availableOutputDevicesType);
+                    availableOutputDevices.getDevicesFromTypeMask(availableOutputDevicesType);
             if (outputs.isStreamActive(AUDIO_STREAM_RING) ||
                     outputs.isStreamActive(AUDIO_STREAM_ALARM)) {
                 return getDeviceForStrategyInt(
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0318ffe..0a8bed1 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -38,6 +38,8 @@
 #include <utils/Log.h>
 #include <media/AudioParameter.h>
 #include <media/AudioPolicyHelper.h>
+#include <media/PatchBuilder.h>
+#include <private/android_filesystem_config.h>
 #include <soundtrigger/SoundTrigger.h>
 #include <system/audio.h>
 #include <audio_policy_conf.h>
@@ -476,20 +478,15 @@
 
 sp<AudioPatch> AudioPolicyManager::createTelephonyPatch(
         bool isRx, audio_devices_t device, uint32_t delayMs) {
-    struct audio_patch patch;
-    patch.num_sources = 1;
-    patch.num_sinks = 1;
+    PatchBuilder patchBuilder;
 
     sp<DeviceDescriptor> txSourceDeviceDesc;
     if (isRx) {
-        fillAudioPortConfigForDevice(mAvailableOutputDevices, device, &patch.sinks[0]);
-        fillAudioPortConfigForDevice(
-                mAvailableInputDevices, AUDIO_DEVICE_IN_TELEPHONY_RX, &patch.sources[0]);
+        patchBuilder.addSink(findDevice(mAvailableOutputDevices, device)).
+                addSource(findDevice(mAvailableInputDevices, AUDIO_DEVICE_IN_TELEPHONY_RX));
     } else {
-        txSourceDeviceDesc = fillAudioPortConfigForDevice(
-                mAvailableInputDevices, device, &patch.sources[0]);
-        fillAudioPortConfigForDevice(
-                mAvailableOutputDevices, AUDIO_DEVICE_OUT_TELEPHONY_TX, &patch.sinks[0]);
+        patchBuilder.addSource(txSourceDeviceDesc = findDevice(mAvailableInputDevices, device)).
+                addSink(findDevice(mAvailableOutputDevices, AUDIO_DEVICE_OUT_TELEPHONY_TX));
     }
 
     audio_devices_t outputDevice = isRx ? device : AUDIO_DEVICE_OUT_TELEPHONY_TX;
@@ -500,9 +497,7 @@
         sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
         ALOG_ASSERT(!outputDesc->isDuplicated(),
                 "%s() %#x device output %d is duplicated", __func__, outputDevice, output);
-        outputDesc->toAudioPortConfig(&patch.sources[1]);
-        patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
-        patch.num_sources = 2;
+        patchBuilder.addSource(outputDesc, { .stream = AUDIO_STREAM_PATCH });
     }
 
     if (!isRx) {
@@ -524,26 +519,25 @@
     }
 
     audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-    status_t status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, delayMs);
+    status_t status = mpClientInterface->createAudioPatch(
+            patchBuilder.patch(), &afPatchHandle, delayMs);
     ALOGW_IF(status != NO_ERROR,
             "%s() error %d creating %s audio patch", __func__, status, isRx ? "RX" : "TX");
     sp<AudioPatch> audioPatch;
     if (status == NO_ERROR) {
-        audioPatch = new AudioPatch(&patch, mUidCached);
+        audioPatch = new AudioPatch(patchBuilder.patch(), mUidCached);
         audioPatch->mAfPatchHandle = afPatchHandle;
         audioPatch->mUid = mUidCached;
     }
     return audioPatch;
 }
 
-sp<DeviceDescriptor> AudioPolicyManager::fillAudioPortConfigForDevice(
-        const DeviceVector& devices, audio_devices_t device, audio_port_config *config) {
-    DeviceVector deviceList = devices.getDevicesFromType(device);
+sp<DeviceDescriptor> AudioPolicyManager::findDevice(
+        const DeviceVector& devices, audio_devices_t device) {
+    DeviceVector deviceList = devices.getDevicesFromTypeMask(device);
     ALOG_ASSERT(!deviceList.isEmpty(),
             "%s() selected device type %#x is not in devices list", __func__, device);
-    sp<DeviceDescriptor> deviceDesc = deviceList.itemAt(0);
-    deviceDesc->toAudioPortConfig(config);
-    return deviceDesc;
+    return deviceList.itemAt(0);
 }
 
 void AudioPolicyManager::setPhoneState(audio_mode_t state)
@@ -871,7 +865,7 @@
         return INVALID_OPERATION;
     }
 
-    DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device);
+    DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device);
     *selectedDeviceId = outputDevices.size() > 0 ? outputDevices.itemAt(0)->getId()
             : AUDIO_PORT_HANDLE_NONE;
 
@@ -976,7 +970,7 @@
         sp<SwAudioOutputDescriptor> outputDesc =
                 new SwAudioOutputDescriptor(profile, mpClientInterface);
 
-        DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device);
+        DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(device);
         String8 address = outputDevices.size() > 0 ? outputDevices.itemAt(0)->mAddress
                 : String8("");
 
@@ -1545,7 +1539,7 @@
         if (*portId == AUDIO_PORT_HANDLE_NONE) {
             *portId = AudioPort::getNextUniqueId();
         }
-        inputDevices = mAvailableInputDevices.getDevicesFromType(inputDesc->mDevice);
+        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);
@@ -1612,7 +1606,7 @@
         goto error;
     }
 
-    inputDevices = mAvailableInputDevices.getDevicesFromType(device);
+    inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device);
     *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
             : AUDIO_PORT_HANDLE_NONE;
 
@@ -1780,7 +1774,7 @@
     lConfig.format = profileFormat;
 
     if (address == "") {
-        DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device);
+        DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device);
         // the inputs vector must be of size >= 1, but we don't want to crash here
         address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8("");
     }
@@ -2627,42 +2621,24 @@
 
 status_t AudioPolicyManager::dump(int fd)
 {
-    const size_t SIZE = 256;
-    char buffer[SIZE];
     String8 result;
-
-    snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
-    result.append(buffer);
-
-    snprintf(buffer, SIZE, " Primary Output: %d\n",
+    result.appendFormat("\nAudioPolicyManager Dump: %p\n", this);
+    result.appendFormat(" Primary Output: %d\n",
              hasPrimaryOutput() ? mPrimaryOutput->mIoHandle : AUDIO_IO_HANDLE_NONE);
-    result.append(buffer);
     std::string stateLiteral;
     AudioModeConverter::toString(mEngine->getPhoneState(), stateLiteral);
-    snprintf(buffer, SIZE, " Phone state: %s\n", stateLiteral.c_str());
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for communications %d\n",
-             mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for media %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for record %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for dock %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for system %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for hdmi system audio %d\n",
-            mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for encoded surround output %d\n",
-            mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND));
-    result.append(buffer);
-    snprintf(buffer, SIZE, " TTS output %s\n", mTtsOutputAvailable ? "available" : "not available");
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Master mono: %s\n", mMasterMono ? "on" : "off");
-    result.append(buffer);
-
+    result.appendFormat(" Phone state: %s\n", stateLiteral.c_str());
+    const char* forceUses[AUDIO_POLICY_FORCE_USE_CNT] = {
+        "communications", "media", "record", "dock", "system",
+        "HDMI system audio", "encoded surround output", "vibrate ringing" };
+    for (audio_policy_force_use_t i = AUDIO_POLICY_FORCE_FOR_COMMUNICATION;
+         i < AUDIO_POLICY_FORCE_USE_CNT; i = (audio_policy_force_use_t)((int)i + 1)) {
+        result.appendFormat(" Force use for %s: %d\n",
+                forceUses[i], mEngine->getForceUse(i));
+    }
+    result.appendFormat(" TTS output %savailable\n", mTtsOutputAvailable ? "" : "not ");
+    result.appendFormat(" Master mono: %s\n", mMasterMono ? "on" : "off");
+    result.appendFormat(" Config source: %s\n", getConfig().getSource().c_str());
     write(fd, result.string(), result.size());
 
     mAvailableOutputDevices.dump(fd, String8("Available output"));
@@ -3078,28 +3054,8 @@
             }
             // TODO: check from routing capabilities in config file and other conflicting patches
 
-            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-            if (index >= 0) {
-                afPatchHandle = patchDesc->mAfPatchHandle;
-            }
-
-            status_t status = mpClientInterface->createAudioPatch(&newPatch,
-                                                                  &afPatchHandle,
-                                                                  0);
-            ALOGV("createAudioPatch() patch panel returned %d patchHandle %d",
-                                                                  status, afPatchHandle);
-            if (status == NO_ERROR) {
-                if (index < 0) {
-                    patchDesc = new AudioPatch(&newPatch, uid);
-                    addAudioPatch(patchDesc->mHandle, patchDesc);
-                } else {
-                    patchDesc->mPatch = newPatch;
-                }
-                patchDesc->mAfPatchHandle = afPatchHandle;
-                *handle = patchDesc->mHandle;
-                nextAudioPortGeneration();
-                mpClientInterface->onAudioPatchListUpdate();
-            } else {
+            status_t status = installPatch(__func__, index, handle, &newPatch, 0, uid, &patchDesc);
+            if (status != NO_ERROR) {
                 ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
                 status);
                 return INVALID_OPERATION;
@@ -3232,10 +3188,10 @@
         return BAD_VALUE;
     }
 
-    struct audio_port_config backupConfig;
+    struct audio_port_config backupConfig = {};
     status_t status = audioPortConfig->applyAudioPortConfig(config, &backupConfig);
     if (status == NO_ERROR) {
-        struct audio_port_config newConfig;
+        struct audio_port_config newConfig = {};
         audioPortConfig->toAudioPortConfig(&newConfig, config);
         status = mpClientInterface->setAudioPortConfig(&newConfig, 0);
     }
@@ -3383,7 +3339,7 @@
     sp<AudioSourceDescriptor> sourceDesc =
             new AudioSourceDescriptor(srcDeviceDesc, attributes, uid);
 
-    struct audio_patch dummyPatch;
+    struct audio_patch dummyPatch = {};
     sp<AudioPatch> patchDesc = new AudioPatch(&dummyPatch, uid);
     sourceDesc->mPatchDesc = patchDesc;
 
@@ -3411,7 +3367,6 @@
             mAvailableOutputDevices.getDevice(sinkDevice, String8(""));
 
     audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-    struct audio_patch *patch = &sourceDesc->mPatchDesc->mPatch;
 
     if (srcDeviceDesc->getAudioPort()->mModule->getHandle() ==
             sinkDeviceDesc->getAudioPort()->mModule->getHandle() &&
@@ -3443,16 +3398,14 @@
         // be connected as well as the stream type for volume control
         // - the sink is defined by whatever output device is currently selected for the output
         // though which this patch is routed.
-        patch->num_sinks = 0;
-        patch->num_sources = 2;
-        srcDeviceDesc->toAudioPortConfig(&patch->sources[0], NULL);
-        outputDesc->toAudioPortConfig(&patch->sources[1], NULL);
-        patch->sources[1].ext.mix.usecase.stream = stream;
-        status = mpClientInterface->createAudioPatch(patch,
+        PatchBuilder patchBuilder;
+        patchBuilder.addSource(srcDeviceDesc).addSource(outputDesc, { .stream = stream });
+        status = mpClientInterface->createAudioPatch(patchBuilder.patch(),
                                                               &afPatchHandle,
                                                               0);
         ALOGV("%s patch panel returned %d patchHandle %d", __FUNCTION__,
                                                               status, afPatchHandle);
+        sourceDesc->mPatchDesc->mPatch = *patchBuilder.patch();
         if (status != NO_ERROR) {
             ALOGW("%s patch panel could not connect device patch, error %d",
                   __FUNCTION__, status);
@@ -3615,7 +3568,7 @@
             }
         }
         // Open an output to query dynamic parameters.
-        DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromType(
+        DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(
                 AUDIO_DEVICE_OUT_HDMI);
         for (size_t i = 0; i < hdmiOutputDevices.size(); i++) {
             String8 address = hdmiOutputDevices[i]->mAddress;
@@ -3741,7 +3694,7 @@
 
     sp<SwAudioOutputDescriptor> outputDesc;
     bool profileUpdated = false;
-    DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromType(
+    DeviceVector hdmiOutputDevices = mAvailableOutputDevices.getDevicesFromTypeMask(
             AUDIO_DEVICE_OUT_HDMI);
     for (size_t i = 0; i < hdmiOutputDevices.size(); i++) {
         // Simulate reconnection to update enabled surround sound formats.
@@ -3760,7 +3713,7 @@
                                              name.c_str());
         profileUpdated |= (status == NO_ERROR);
     }
-    DeviceVector hdmiInputDevices = mAvailableInputDevices.getDevicesFromType(
+    DeviceVector hdmiInputDevices = mAvailableInputDevices.getDevicesFromTypeMask(
                 AUDIO_DEVICE_IN_HDMI);
     for (size_t i = 0; i < hdmiInputDevices.size(); i++) {
         // Simulate reconnection to update enabled surround sound formats.
@@ -3904,6 +3857,7 @@
                      "%s/%s", kConfigLocationList[i], fileName);
             ret = serializer.deserialize(audioPolicyXmlConfigFile, config);
             if (ret == NO_ERROR) {
+                config.setSource(audioPolicyXmlConfigFile);
                 return ret;
             }
         }
@@ -3915,7 +3869,7 @@
 AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface,
                                        bool /*forTesting*/)
     :
-    mUidCached(getuid()),
+    mUidCached(AID_AUDIOSERVER), // no need to call getuid(), there's only one of us running.
     mpClientInterface(clientInterface),
     mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
     mA2dpSuspended(false),
@@ -4026,7 +3980,8 @@
             sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
                                                                                  mpClientInterface);
             const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
-            const DeviceVector &devicesForType = supportedDevices.getDevicesFromType(profileType);
+            const DeviceVector &devicesForType = supportedDevices.getDevicesFromTypeMask(
+                    profileType);
             String8 address = devicesForType.size() > 0 ? devicesForType.itemAt(0)->mAddress
                     : String8("");
             audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
@@ -4080,7 +4035,7 @@
             sp<AudioInputDescriptor> inputDesc =
                     new AudioInputDescriptor(inProfile, mpClientInterface);
 
-            DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(profileType);
+            DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(profileType);
             //   the inputs vector must be of size >= 1, but we don't want to crash here
             String8 address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress
                     : String8("");
@@ -5245,54 +5200,20 @@
     } else {
         DeviceVector deviceList;
         if ((address == NULL) || (strlen(address) == 0)) {
-            deviceList = mAvailableOutputDevices.getDevicesFromType(device);
+            deviceList = mAvailableOutputDevices.getDevicesFromTypeMask(device);
         } else {
-            deviceList = mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address));
+            sp<DeviceDescriptor> deviceDesc = mAvailableOutputDevices.getDevice(
+                    device, String8(address));
+            if (deviceDesc) deviceList.add(deviceDesc);
         }
 
         if (!deviceList.isEmpty()) {
-            struct audio_patch patch;
-            outputDesc->toAudioPortConfig(&patch.sources[0]);
-            patch.num_sources = 1;
-            patch.num_sinks = 0;
+            PatchBuilder patchBuilder;
+            patchBuilder.addSource(outputDesc);
             for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) {
-                deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);
-                patch.num_sinks++;
+                patchBuilder.addSink(deviceList.itemAt(i));
             }
-            ssize_t index;
-            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
-                index = mAudioPatches.indexOfKey(*patchHandle);
-            } else {
-                index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
-            }
-            sp< AudioPatch> patchDesc;
-            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-            if (index >= 0) {
-                patchDesc = mAudioPatches.valueAt(index);
-                afPatchHandle = patchDesc->mAfPatchHandle;
-            }
-
-            status_t status = mpClientInterface->createAudioPatch(&patch,
-                                                                   &afPatchHandle,
-                                                                   delayMs);
-            ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d"
-                    "num_sources %d num_sinks %d",
-                                       status, afPatchHandle, patch.num_sources, patch.num_sinks);
-            if (status == NO_ERROR) {
-                if (index < 0) {
-                    patchDesc = new AudioPatch(&patch, mUidCached);
-                    addAudioPatch(patchDesc->mHandle, patchDesc);
-                } else {
-                    patchDesc->mPatch = patch;
-                }
-                patchDesc->mAfPatchHandle = afPatchHandle;
-                if (patchHandle) {
-                    *patchHandle = patchDesc->mHandle;
-                }
-                outputDesc->setPatchHandle(patchDesc->mHandle);
-                nextAudioPortGeneration();
-                mpClientInterface->onAudioPatchListUpdate();
-            }
+            installPatch(__func__, patchHandle, outputDesc.get(), patchBuilder.patch(), delayMs);
         }
 
         // inform all input as well
@@ -5350,53 +5271,21 @@
     if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) {
         inputDesc->mDevice = device;
 
-        DeviceVector deviceList = mAvailableInputDevices.getDevicesFromType(device);
+        DeviceVector deviceList = mAvailableInputDevices.getDevicesFromTypeMask(device);
         if (!deviceList.isEmpty()) {
-            struct audio_patch patch;
-            inputDesc->toAudioPortConfig(&patch.sinks[0]);
+            PatchBuilder patchBuilder;
+            patchBuilder.addSink(inputDesc,
             // AUDIO_SOURCE_HOTWORD is for internal use only:
             // handled as AUDIO_SOURCE_VOICE_RECOGNITION by the audio HAL
-            if (patch.sinks[0].ext.mix.usecase.source == AUDIO_SOURCE_HOTWORD &&
-                    !inputDesc->isSoundTrigger()) {
-                patch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_VOICE_RECOGNITION;
-            }
-            patch.num_sinks = 1;
+                    [inputDesc](const PatchBuilder::mix_usecase_t& usecase) {
+                        auto result = usecase;
+                        if (result.source == AUDIO_SOURCE_HOTWORD && !inputDesc->isSoundTrigger()) {
+                            result.source = AUDIO_SOURCE_VOICE_RECOGNITION;
+                        }
+                        return result; }).
             //only one input device for now
-            deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]);
-            patch.num_sources = 1;
-            ssize_t index;
-            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
-                index = mAudioPatches.indexOfKey(*patchHandle);
-            } else {
-                index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
-            }
-            sp< AudioPatch> patchDesc;
-            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
-            if (index >= 0) {
-                patchDesc = mAudioPatches.valueAt(index);
-                afPatchHandle = patchDesc->mAfPatchHandle;
-            }
-
-            status_t status = mpClientInterface->createAudioPatch(&patch,
-                                                                  &afPatchHandle,
-                                                                  0);
-            ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d",
-                                                                          status, afPatchHandle);
-            if (status == NO_ERROR) {
-                if (index < 0) {
-                    patchDesc = new AudioPatch(&patch, mUidCached);
-                    addAudioPatch(patchDesc->mHandle, patchDesc);
-                } else {
-                    patchDesc->mPatch = patch;
-                }
-                patchDesc->mAfPatchHandle = afPatchHandle;
-                if (patchHandle) {
-                    *patchHandle = patchDesc->mHandle;
-                }
-                inputDesc->setPatchHandle(patchDesc->mHandle);
-                nextAudioPortGeneration();
-                mpClientInterface->onAudioPatchListUpdate();
-            }
+                    addSource(deviceList.itemAt(0));
+            status = installPatch(__func__, patchHandle, inputDesc.get(), patchBuilder.patch(), 0);
         }
     }
     return status;
@@ -6152,4 +6041,58 @@
     }
 }
 
+status_t AudioPolicyManager::installPatch(const char *caller,
+                                          audio_patch_handle_t *patchHandle,
+                                          AudioIODescriptorInterface *ioDescriptor,
+                                          const struct audio_patch *patch,
+                                          int delayMs)
+{
+    ssize_t index = mAudioPatches.indexOfKey(
+            patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE ?
+            *patchHandle : ioDescriptor->getPatchHandle());
+    sp<AudioPatch> patchDesc;
+    status_t status = installPatch(
+            caller, index, patchHandle, patch, delayMs, mUidCached, &patchDesc);
+    if (status == NO_ERROR) {
+        ioDescriptor->setPatchHandle(patchDesc->mHandle);
+    }
+    return status;
+}
+
+status_t AudioPolicyManager::installPatch(const char *caller,
+                                          ssize_t index,
+                                          audio_patch_handle_t *patchHandle,
+                                          const struct audio_patch *patch,
+                                          int delayMs,
+                                          uid_t uid,
+                                          sp<AudioPatch> *patchDescPtr)
+{
+    sp<AudioPatch> patchDesc;
+    audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+    if (index >= 0) {
+        patchDesc = mAudioPatches.valueAt(index);
+        afPatchHandle = patchDesc->mAfPatchHandle;
+    }
+
+    status_t status = mpClientInterface->createAudioPatch(patch, &afPatchHandle, delayMs);
+    ALOGV("%s() AF::createAudioPatch returned %d patchHandle %d num_sources %d num_sinks %d",
+            caller, status, afPatchHandle, patch->num_sources, patch->num_sinks);
+    if (status == NO_ERROR) {
+        if (index < 0) {
+            patchDesc = new AudioPatch(patch, uid);
+            addAudioPatch(patchDesc->mHandle, patchDesc);
+        } else {
+            patchDesc->mPatch = *patch;
+        }
+        patchDesc->mAfPatchHandle = afPatchHandle;
+        if (patchHandle) {
+            *patchHandle = patchDesc->mHandle;
+        }
+        nextAudioPortGeneration();
+        mpClientInterface->onAudioPatchListUpdate();
+    }
+    if (patchDescPtr) *patchDescPtr = patchDesc;
+    return status;
+}
+
 } // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index b954714..008e1ca 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -502,8 +502,8 @@
 
         uint32_t updateCallRouting(audio_devices_t rxDevice, uint32_t delayMs = 0);
         sp<AudioPatch> createTelephonyPatch(bool isRx, audio_devices_t device, uint32_t delayMs);
-        sp<DeviceDescriptor> fillAudioPortConfigForDevice(
-                const DeviceVector& devices, audio_devices_t device, audio_port_config *config);
+        sp<DeviceDescriptor> findDevice(
+                const DeviceVector& devices, audio_devices_t device);
 
         // if argument "device" is different from AUDIO_DEVICE_NONE,  startSource() will force
         // the re-evaluation of the output device.
@@ -540,7 +540,7 @@
         static bool streamsMatchForvolume(audio_stream_type_t stream1,
                                           audio_stream_type_t stream2);
 
-        uid_t mUidCached;
+        const uid_t mUidCached;                         // AID_AUDIOSERVER
         AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
         sp<SwAudioOutputDescriptor> mPrimaryOutput;     // primary output descriptor
         // list of descriptors for outputs currently opened
@@ -680,6 +680,18 @@
             param.addInt(String8(AudioParameter::keyMonoOutput), (int)mMasterMono);
             mpClientInterface->setParameters(output, param.toString());
         }
+        status_t installPatch(const char *caller,
+                audio_patch_handle_t *patchHandle,
+                AudioIODescriptorInterface *ioDescriptor,
+                const struct audio_patch *patch,
+                int delayMs);
+        status_t installPatch(const char *caller,
+                ssize_t index,
+                audio_patch_handle_t *patchHandle,
+                const struct audio_patch *patch,
+                int delayMs,
+                uid_t uid,
+                sp<AudioPatch> *patchDescPtr);
 
         bool soundTriggerSupportsConcurrentCapture();
         bool mSoundTriggerSupportsConcurrentCapture;
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index c7dfe0f..fdae23b 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -24,6 +24,7 @@
 #include <cutils/misc.h>
 #include <media/AudioEffect.h>
 #include <media/EffectsConfig.h>
+#include <mediautils/ServiceUtilities.h>
 #include <system/audio.h>
 #include <system/audio_effects/audio_effects_conf.h>
 #include <utils/Vector.h>
@@ -31,7 +32,6 @@
 #include <cutils/config_utils.h>
 #include <binder/IPCThreadState.h>
 #include "AudioPolicyEffects.h"
-#include "ServiceUtilities.h"
 
 namespace android {
 
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7337f04..d3a1188 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -21,7 +21,7 @@
 #include <media/MediaAnalyticsItem.h>
 
 #include "AudioPolicyService.h"
-#include "ServiceUtilities.h"
+#include <mediautils/ServiceUtilities.h>
 #include "TypeConverter.h"
 
 namespace android {
@@ -183,7 +183,7 @@
     Mutex::Autolock _l(mLock);
 
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (!isTrustedCallingUid(callingUid) || uid == (uid_t)-1) {
+    if (!isAudioServerOrMediaServerUid(callingUid) || uid == (uid_t)-1) {
         ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
                 "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
         uid = callingUid;
@@ -320,7 +320,7 @@
 
     bool updatePid = (pid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (!isTrustedCallingUid(callingUid)) {
+    if (!isAudioServerOrMediaServerUid(callingUid)) {
         ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
                 "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
         uid = callingUid;
@@ -528,10 +528,13 @@
 
             item->setCString(kAudioPolicyRqstSrc,
                              audioSourceString(client->attributes.source).c_str());
-            item->setCString(kAudioPolicyRqstPkg,
-                             std::string(String8(client->opPackageName).string()).c_str());
             item->setInt32(kAudioPolicyRqstSession, client->session);
-
+            if (client->opPackageName.size() != 0) {
+                item->setCString(kAudioPolicyRqstPkg,
+                                 std::string(String8(client->opPackageName).string()).c_str());
+            } else {
+                item->setCString(kAudioPolicyRqstPkg, to_string(client->uid).c_str());
+            }
             item->setCString(
                     kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str());
 
@@ -550,9 +553,13 @@
                         // keeps the last of the clients marked active
                         item->setCString(kAudioPolicyActiveSrc,
                                          audioSourceString(other->attributes.source).c_str());
-                        item->setCString(kAudioPolicyActivePkg,
-                                     std::string(String8(other->opPackageName).string()).c_str());
                         item->setInt32(kAudioPolicyActiveSession, other->session);
+                        if (other->opPackageName.size() != 0) {
+                            item->setCString(kAudioPolicyActivePkg,
+                                 std::string(String8(other->opPackageName).string()).c_str());
+                        } else {
+                            item->setCString(kAudioPolicyRqstPkg, to_string(other->uid).c_str());
+                        }
                         item->setCString(kAudioPolicyActiveDevice,
                                          getDeviceTypeStrForPortId(other->deviceId).c_str());
                     }
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index f3cddc3..1379223 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -26,7 +26,6 @@
 #include <sys/time.h>
 #include <binder/IServiceManager.h>
 #include <utils/Log.h>
-#include <cutils/multiuser.h>
 #include <cutils/properties.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ActivityManager.h>
@@ -35,16 +34,14 @@
 #include <utils/String16.h>
 #include <utils/threads.h>
 #include "AudioPolicyService.h"
-#include "ServiceUtilities.h"
 #include <hardware_legacy/power.h>
 #include <media/AudioEffect.h>
 #include <media/AudioParameter.h>
+#include <mediautils/ServiceUtilities.h>
 
 #include <system/audio.h>
 #include <system/audio_policy.h>
 
-#include <private/android_filesystem_config.h>
-
 namespace android {
 
 static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
@@ -275,7 +272,7 @@
 void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate(
         const String8& regId, int32_t state)
 {
-    if (mAudioPolicyServiceClient != 0 && multiuser_get_app_id(mUid) < AID_APP_START) {
+    if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
         mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
     }
 }
@@ -285,7 +282,7 @@
         const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
         audio_patch_handle_t patchHandle)
 {
-    if (mAudioPolicyServiceClient != 0 && multiuser_get_app_id(mUid) < AID_APP_START) {
+    if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
         mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo,
                 clientConfig, deviceConfig, patchHandle);
     }
@@ -577,10 +574,6 @@
     updateUidCache(uid, false, true);
 }
 
-bool AudioPolicyService::UidPolicy::isServiceUid(uid_t uid) const {
-    return multiuser_get_app_id(uid) < AID_APP_START;
-}
-
 void AudioPolicyService::UidPolicy::notifyService(uid_t uid, bool active) {
     sp<AudioPolicyService> service = mService.promote();
     if (service != nullptr) {
@@ -1212,6 +1205,7 @@
                 patch = ((CreateAudioPatchData *)command->mParam.get())->mPatch;
             } else {
                 handle = ((ReleaseAudioPatchData *)command->mParam.get())->mHandle;
+                memset(&patch, 0, sizeof(patch));
             }
             audio_patch_handle_t handle2;
             struct audio_patch patch2;
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 407d7a5..02019a2 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -293,7 +293,6 @@
         void removeOverrideUid(uid_t uid) { updateOverrideUid(uid, false, false); }
 
     private:
-        bool isServiceUid(uid_t uid) const;
         void notifyService(uid_t uid, bool active);
         void updateOverrideUid(uid_t uid, bool active, bool insert);
         void updateUidCache(uid_t uid, bool active, bool insert);
diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk
index a43daea..cfa9ab1 100644
--- a/services/audiopolicy/tests/Android.mk
+++ b/services/audiopolicy/tests/Android.mk
@@ -6,7 +6,6 @@
   frameworks/av/services/audiopolicy \
   frameworks/av/services/audiopolicy/common/include \
   frameworks/av/services/audiopolicy/engine/interface \
-  frameworks/av/services/audiopolicy/utilities
 
 LOCAL_SHARED_LIBRARIES := \
   libaudiopolicymanagerdefault \
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index a9593b8..2d9260e 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -19,6 +19,8 @@
 
 #include <gtest/gtest.h>
 
+#include <media/PatchBuilder.h>
+
 #include "AudioPolicyTestClient.h"
 #include "AudioPolicyTestManager.h"
 
@@ -166,29 +168,14 @@
 }
 
 TEST_F(AudioPolicyManagerTest, CreateAudioPatchFromMix) {
-    audio_patch patch{};
     audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
     uid_t uid = 42;
     const size_t patchCountBefore = mClient->getActivePatchesCount();
-    patch.num_sources = 1;
-    {
-        auto& src = patch.sources[0];
-        src.role = AUDIO_PORT_ROLE_SOURCE;
-        src.type = AUDIO_PORT_TYPE_MIX;
-        src.id = mManager->getConfig().getAvailableInputDevices()[0]->getId();
-        // Note: these are the parameters of the output device.
-        src.sample_rate = 44100;
-        src.format = AUDIO_FORMAT_PCM_16_BIT;
-        src.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-    }
-    patch.num_sinks = 1;
-    {
-        auto& sink = patch.sinks[0];
-        sink.role = AUDIO_PORT_ROLE_SINK;
-        sink.type = AUDIO_PORT_TYPE_DEVICE;
-        sink.id = mManager->getConfig().getDefaultOutputDevice()->getId();
-    }
-    ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(&patch, &handle, uid));
+    ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty());
+    PatchBuilder patchBuilder;
+    patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]).
+            addSink(mManager->getConfig().getDefaultOutputDevice());
+    ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid));
     ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle);
     ASSERT_EQ(patchCountBefore + 1, mClient->getActivePatchesCount());
 }
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
index 37d6cc9..73c9535 100644
--- a/services/mediaextractor/Android.mk
+++ b/services/mediaextractor/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_REQUIRED_MODULES_arm := crash_dump.policy mediaextractor.policy
 LOCAL_REQUIRED_MODULES_arm64 := crash_dump.policy mediaextractor.policy
 LOCAL_REQUIRED_MODULES_x86 := crash_dump.policy mediaextractor.policy
+LOCAL_REQUIRED_MODULES_x86_64 := crash_dump.policy mediaextractor.policy
 
 # extractor libraries
 LOCAL_REQUIRED_MODULES += \
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
old mode 100755
new mode 100644
index 63c7780..6d9ed6f
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86_64.policy
@@ -21,6 +21,7 @@
 getuid: 1
 setpriority: 1
 sigaltstack: 1
+fstatfs: 1
 newfstatat: 1
 restart_syscall: 1
 exit: 1
@@ -30,28 +31,21 @@
 sched_setscheduler: 1
 getrlimit: 1
 nanosleep: 1
+getrandom: 1
+
+# for dynamically loading extractors
+getdents64: 1
+readlinkat: 1
+pread64: 1
+mremap: 1
 
 # for FileSource
 readlinkat: 1
 
-# for attaching to debuggerd on process crash
-tgkill: 1
-socket: arg0 == 1
-connect: 1
-fcntl: 1
-rt_sigprocmask: 1
-rt_sigaction: 1
-rt_tgsigqueueinfo: 1
-geteuid: 1
-getgid: 1
-getegid: 1
-getgroups: 1
-getdents64: 1
-pipe2: 1
-ppoll: 1
-
 # Required by AddressSanitizer
 gettid: 1
 sched_yield: 1
 getpid: 1
 gettid: 1
+
+@include /system/etc/seccomp_policy/crash_dump.x86_64.policy
diff --git a/services/medialog/Android.bp b/services/medialog/Android.bp
index 29e6dfc..ca96f62 100644
--- a/services/medialog/Android.bp
+++ b/services/medialog/Android.bp
@@ -9,7 +9,9 @@
     shared_libs: [
         "libaudioutils",
         "libbinder",
+        "libcutils",
         "liblog",
+        "libmediautils",
         "libnbaio",
         "libnblog",
         "libutils",
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 1be5544..e58dff7 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -21,7 +21,7 @@
 #include <utils/Log.h>
 #include <binder/PermissionCache.h>
 #include <media/nblog/NBLog.h>
-#include <private/android_filesystem_config.h>
+#include <mediautils/ServiceUtilities.h>
 #include "MediaLogService.h"
 
 namespace android {
@@ -53,7 +53,7 @@
 
 void MediaLogService::registerWriter(const sp<IMemory>& shared, size_t size, const char *name)
 {
-    if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0 ||
+    if (!isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) || shared == 0 ||
             size < kMinSize || size > kMaxSize || name == NULL ||
             shared->size() < NBLog::Timeline::sharedSize(size)) {
         return;
@@ -67,7 +67,7 @@
 
 void MediaLogService::unregisterWriter(const sp<IMemory>& shared)
 {
-    if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0) {
+    if (!isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) || shared == 0) {
         return;
     }
     Mutex::Autolock _l(mLock);
@@ -95,10 +95,8 @@
 
 status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused)
 {
-    // FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp
-    static const String16 sDump("android.permission.DUMP");
-    if (!(IPCThreadState::self()->getCallingUid() == AID_AUDIOSERVER ||
-            PermissionCache::checkCallingPermission(sDump))) {
+    if (!(isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid())
+            || dumpAllowed())) {
         dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
                 IPCThreadState::self()->getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 6a72e5b..94440b1 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -24,6 +24,7 @@
 
 #include <aaudio/AAudio.h>
 #include <mediautils/SchedulingPolicyService.h>
+#include <mediautils/ServiceUtilities.h>
 #include <utils/String16.h>
 
 #include "binding/AAudioServiceMessage.h"
@@ -33,7 +34,6 @@
 #include "AAudioServiceStreamMMAP.h"
 #include "AAudioServiceStreamShared.h"
 #include "binding/IAAudioService.h"
-#include "ServiceUtilities.h"
 
 using namespace android;
 using namespace aaudio;
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index 584b2ef..3d5f140 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -53,7 +53,6 @@
     libbinder \
     libcutils \
     libmediautils \
-    libserviceutility \
     libutils \
     liblog
 
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
index ad3666e..3c7d29d 100644
--- a/services/soundtrigger/Android.mk
+++ b/services/soundtrigger/Android.mk
@@ -34,8 +34,7 @@
     libhardware \
     libsoundtrigger \
     libaudioclient \
-    libserviceutility
-
+    libmediautils \
 
 ifeq ($(USE_LEGACY_LOCAL_AUDIO_HAL),true)
 # libhardware configuration
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index a7d6e83..eb9cd1d 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -27,13 +27,13 @@
 #include <cutils/properties.h>
 #include <hardware/hardware.h>
 #include <media/AudioSystem.h>
+#include <mediautils/ServiceUtilities.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
 #include <system/sound_trigger.h>
-#include <ServiceUtilities.h>
 #include "SoundTriggerHwService.h"
 
 #ifdef SOUND_TRIGGER_USE_STUB_MODULE
@@ -562,10 +562,7 @@
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (modelMemory == 0 || modelMemory->pointer() == NULL) {
-        ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()");
-        return BAD_VALUE;
-    }
+
     struct sound_trigger_sound_model *sound_model =
             (struct sound_trigger_sound_model *)modelMemory->pointer();
 
@@ -659,11 +656,6 @@
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (dataMemory == 0 || dataMemory->pointer() == NULL) {
-        ALOGE("startRecognition() dataMemory is 0 or has NULL pointer()");
-        return BAD_VALUE;
-
-    }
 
     struct sound_trigger_recognition_config *config =
             (struct sound_trigger_recognition_config *)dataMemory->pointer();
@@ -966,6 +958,9 @@
                                IPCThreadState::self()->getCallingUid())) {
         return PERMISSION_DENIED;
     }
+    if (checkIMemory(modelMemory) != NO_ERROR) {
+        return BAD_VALUE;
+    }
 
     sp<Module> module = mModule.promote();
     if (module == 0) {
@@ -997,6 +992,9 @@
                                IPCThreadState::self()->getCallingUid())) {
         return PERMISSION_DENIED;
     }
+    if (checkIMemory(dataMemory) != NO_ERROR) {
+        return BAD_VALUE;
+    }
 
     sp<Module> module = mModule.promote();
     if (module == 0) {