Merge "camera: Update camera service logging and validation."
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 9b786c5..851ad2c 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -56,7 +56,7 @@
         return true;
     }
 
-    status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin)
+    status_t MockDrmFactory::createDrmPlugin(const uint8_t /* uuid */[16], DrmPlugin **plugin)
     {
         *plugin = new MockDrmPlugin();
         return OK;
@@ -68,8 +68,9 @@
         return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
     }
 
-    status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
-                                             size_t size, CryptoPlugin **plugin)
+    status_t MockCryptoFactory::createPlugin(const uint8_t /* uuid */[16],
+                                             const void * /* data */,
+                                             size_t /* size */, CryptoPlugin **plugin)
     {
         *plugin = new MockCryptoPlugin();
         return OK;
@@ -150,7 +151,7 @@
         // Properties used in mock test, set by cts test app returned from mock plugin
         //   byte[] mock-request       -> request
         //   string mock-default-url   -> defaultUrl
-        //   string mock-key-request-type -> keyRequestType
+        //   string mock-keyRequestType -> keyRequestType
 
         index = mByteArrayProperties.indexOfKey(String8("mock-request"));
         if (index < 0) {
@@ -266,8 +267,8 @@
         return OK;
     }
 
-    status_t MockDrmPlugin::getProvisionRequest(String8 const &certType,
-                                                String8 const &certAuthority,
+    status_t MockDrmPlugin::getProvisionRequest(String8 const & /* certType */,
+                                                String8 const & /* certAuthority */,
                                                 Vector<uint8_t> &request,
                                                 String8 &defaultUrl)
     {
@@ -297,8 +298,8 @@
     }
 
     status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response,
-                                                     Vector<uint8_t> &certificate,
-                                                     Vector<uint8_t> &wrappedKey)
+                                                     Vector<uint8_t> & /* certificate */,
+                                                     Vector<uint8_t> & /* wrappedKey */)
     {
         Mutex::Autolock lock(mLock);
         ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
@@ -317,7 +318,8 @@
         return OK;
     }
 
-    status_t MockDrmPlugin::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop)
+    status_t MockDrmPlugin::getSecureStop(Vector<uint8_t> const & /* ssid */,
+                                          Vector<uint8_t> & secureStop)
     {
         Mutex::Autolock lock(mLock);
         ALOGD("MockDrmPlugin::getSecureStop()");
@@ -439,6 +441,63 @@
                   pData ? vectorToString(*pData) : "{}");
 
             sendEvent(eventType, extra, pSessionId, pData);
+        } else if (name == "mock-send-expiration-update") {
+            int64_t expiryTimeMS;
+            sscanf(value.string(), "%jd", &expiryTimeMS);
+
+            Vector<uint8_t> const *pSessionId = NULL;
+            ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+            if (index >= 0) {
+                pSessionId = &mByteArrayProperties[index];
+            }
+
+            ALOGD("sending expiration-update from mock drm plugin: %jd %s",
+                  expiryTimeMS, pSessionId ? vectorToString(*pSessionId) : "{}");
+
+            sendExpirationUpdate(pSessionId, expiryTimeMS);
+        } else if (name == "mock-send-keys-change") {
+            Vector<uint8_t> const *pSessionId = NULL;
+            ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+            if (index >= 0) {
+                pSessionId = &mByteArrayProperties[index];
+            }
+
+            ALOGD("sending keys-change from mock drm plugin: %s",
+                  pSessionId ? vectorToString(*pSessionId) : "{}");
+
+            Vector<DrmPlugin::KeyStatus> keyStatusList;
+            DrmPlugin::KeyStatus keyStatus;
+            uint8_t keyId1[] = {'k', 'e', 'y', '1'};
+            keyStatus.mKeyId.clear();
+            keyStatus.mKeyId.appendArray(keyId1, sizeof(keyId1));
+            keyStatus.mType = DrmPlugin::kKeyStatusType_Usable;
+            keyStatusList.add(keyStatus);
+
+            uint8_t keyId2[] = {'k', 'e', 'y', '2'};
+            keyStatus.mKeyId.clear();
+            keyStatus.mKeyId.appendArray(keyId2, sizeof(keyId2));
+            keyStatus.mType = DrmPlugin::kKeyStatusType_Expired;
+            keyStatusList.add(keyStatus);
+
+            uint8_t keyId3[] = {'k', 'e', 'y', '3'};
+            keyStatus.mKeyId.clear();
+            keyStatus.mKeyId.appendArray(keyId3, sizeof(keyId3));
+            keyStatus.mType = DrmPlugin::kKeyStatusType_OutputNotAllowed;
+            keyStatusList.add(keyStatus);
+
+            uint8_t keyId4[] = {'k', 'e', 'y', '4'};
+            keyStatus.mKeyId.clear();
+            keyStatus.mKeyId.appendArray(keyId4, sizeof(keyId4));
+            keyStatus.mType = DrmPlugin::kKeyStatusType_StatusPending;
+            keyStatusList.add(keyStatus);
+
+            uint8_t keyId5[] = {'k', 'e', 'y', '5'};
+            keyStatus.mKeyId.clear();
+            keyStatus.mKeyId.appendArray(keyId5, sizeof(keyId5));
+            keyStatus.mType = DrmPlugin::kKeyStatusType_InternalError;
+            keyStatusList.add(keyStatus);
+
+            sendKeysChange(pSessionId, &keyStatusList, true);
         } else {
             mStringProperties.add(name, value);
         }
@@ -740,7 +799,7 @@
     ssize_t
     MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16],
                               Mode mode, const void *srcPtr, const SubSample *subSamples,
-                              size_t numSubSamples, void *dstPtr, AString *errorDetailMsg)
+                              size_t numSubSamples, void *dstPtr, AString * /* errorDetailMsg */)
     {
         ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, "
               "subSamples=%s, dst=%p)",
@@ -769,7 +828,7 @@
     {
         String8 result;
         for (size_t i = 0; i < numSubSamples; i++) {
-            result.appendFormat("[%zu] {clear:%zu, encrypted:%zu} ", i,
+            result.appendFormat("[%zu] {clear:%u, encrypted:%u} ", i,
                                 subSamples[i].mNumBytesOfClearData,
                                 subSamples[i].mNumBytesOfEncryptedData);
         }
diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h
index b705efa..07d946d 100644
--- a/include/media/AudioResamplerPublic.h
+++ b/include/media/AudioResamplerPublic.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_AUDIO_RESAMPLER_PUBLIC_H
 #define ANDROID_AUDIO_RESAMPLER_PUBLIC_H
 
+#include <stdint.h>
+
 // AUDIO_RESAMPLER_DOWN_RATIO_MAX is the maximum ratio between the original
 // audio sample rate and the target rate when downsampling,
 // as permitted in the audio framework, e.g. AudioTrack and AudioFlinger.
@@ -26,6 +28,22 @@
 // TODO: replace with an API
 #define AUDIO_RESAMPLER_DOWN_RATIO_MAX 256
 
+// AUDIO_RESAMPLER_UP_RATIO_MAX is the maximum suggested ratio between the original
+// audio sample rate and the target rate when upsampling.  It is loosely enforced by
+// the system. One issue with large upsampling ratios is the approximation by
+// an int32_t of the phase increments, making the resulting sample rate inexact.
+#define AUDIO_RESAMPLER_UP_RATIO_MAX 65536
+
+#define AUDIO_TIMESTRETCH_SPEED_MIN    0.5f
+#define AUDIO_TIMESTRETCH_SPEED_MAX    2.0f
+#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f
+
+#define AUDIO_TIMESTRETCH_PITCH_MIN    0.5f
+#define AUDIO_TIMESTRETCH_PITCH_MAX    2.0f
+#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f
+
+// TODO: Consider putting these inlines into a class scope
+
 // Returns the source frames needed to resample to destination frames.  This is not a precise
 // value and depends on the resampler (and possibly how it handles rounding internally).
 // Nevertheless, this should be an upper bound on the requirements of the resampler.
@@ -39,4 +57,24 @@
             size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1);
 }
 
+// An upper bound for the number of destination frames possible from srcFrames
+// after sample rate conversion.  This may be used for buffer sizing.
+static inline size_t destinationFramesPossible(size_t srcFrames, uint32_t srcSampleRate,
+        uint32_t dstSampleRate) {
+    if (srcSampleRate == dstSampleRate) {
+        return srcFrames;
+    }
+    uint64_t dstFrames = (uint64_t)srcFrames * dstSampleRate / srcSampleRate;
+    return dstFrames > 2 ? dstFrames - 2 : 0;
+}
+
+static inline size_t sourceFramesNeededWithTimestretch(
+        uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
+        float speed) {
+    // required is the number of input frames the resampler needs
+    size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
+    // to deliver this, the time stretcher requires:
+    return required * (double)speed + 1 + 1; // accounting for rounding dependencies
+}
+
 #endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f5db1bb..3b6db8c 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -221,14 +221,15 @@
                                         audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                         const audio_offload_info_t *offloadInfo = NULL);
     static status_t getOutputForAttr(const audio_attributes_t *attr,
-                                        audio_io_handle_t *output,
-                                        audio_session_t session,
-                                        audio_stream_type_t *stream,
-                                        uint32_t samplingRate = 0,
-                                        audio_format_t format = AUDIO_FORMAT_DEFAULT,
-                                        audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
-                                        audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
-                                        const audio_offload_info_t *offloadInfo = NULL);
+                                     audio_io_handle_t *output,
+                                     audio_session_t session,
+                                     audio_stream_type_t *stream,
+                                     uint32_t samplingRate = 0,
+                                     audio_format_t format = AUDIO_FORMAT_DEFAULT,
+                                     audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
+                                     audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+                                     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+                                     const audio_offload_info_t *offloadInfo = NULL);
     static status_t startOutput(audio_io_handle_t output,
                                 audio_stream_type_t stream,
                                 audio_session_t session);
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index d9b7057..a06197f 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -359,6 +359,21 @@
     /* Return current source sample rate in Hz */
             uint32_t    getSampleRate() const;
 
+    /* Set source playback rate for timestretch
+     * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
+     * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
+     *
+     * AUDIO_TIMESTRETCH_SPEED_MIN <= speed <= AUDIO_TIMESTRETCH_SPEED_MAX
+     * AUDIO_TIMESTRETCH_PITCH_MIN <= pitch <= AUDIO_TIMESTRETCH_PITCH_MAX
+     *
+     * Speed increases the playback rate of media, but does not alter pitch.
+     * Pitch increases the "tonal frequency" of media, but does not affect the playback rate.
+     */
+            status_t    setPlaybackRate(float speed, float pitch);
+
+    /* Return current playback rate */
+            void        getPlaybackRate(float *speed, float *pitch) const;
+
     /* Enables looping and sets the start and end points of looping.
      * Only supported for static buffer mode.
      *
@@ -477,6 +492,26 @@
             audio_io_handle_t    getOutput() const;
 public:
 
+    /* Selects the audio device to use for output of this AudioTrack. A value of
+     * AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+     *
+     * Parameters:
+     *  The device ID of the selected device (as returned by the AudioDevicesManager API).
+     *
+     * Returned value:
+     *  - NO_ERROR: successful operation
+     *    TODO: what else can happen here?
+     */
+            status_t    setOutputDevice(audio_port_handle_t deviceId);
+
+    /* Returns the ID of the audio device used for output of this AudioTrack.
+     * A value of AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+     *
+     * Parameters:
+     *  none.
+     */
+     audio_port_handle_t getOutputDevice();
+
     /* Returns the unique session ID associated with this track.
      *
      * Parameters:
@@ -699,6 +734,9 @@
             // increment mPosition by the delta of mServer, and return new value of mPosition
             uint32_t updateAndGetPosition_l();
 
+            // check sample rate and speed is compatible with AudioTrack
+            bool     isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;
+
     // Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
     sp<IAudioTrack>         mAudioTrack;
     sp<IMemory>             mCblkMemory;
@@ -710,6 +748,8 @@
     float                   mVolume[2];
     float                   mSendLevel;
     mutable uint32_t        mSampleRate;            // mutable because getSampleRate() can update it
+    float                   mSpeed;                 // timestretch: 1.0f for normal speed.
+    float                   mPitch;                 // timestretch: 1.0f for normal pitch.
     size_t                  mFrameCount;            // corresponds to current IAudioTrack, value is
                                                     // reported back by AudioFlinger to the client
     size_t                  mReqFrameCount;         // frame count to request the first or next time
@@ -817,6 +857,10 @@
     bool                    mInUnderrun;            // whether track is currently in underrun state
     uint32_t                mPausedPosition;
 
+    // For Device Selection API
+    //  a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
+    int                     mSelectedDeviceId;
+
 private:
     class DeathNotifier : public IBinder::DeathRecipient {
     public:
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index fecc6f1..7506153 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -66,6 +66,7 @@
                                         audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                         audio_channel_mask_t channelMask = 0,
                                         audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+                                        audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                                         const audio_offload_info_t *offloadInfo = NULL) = 0;
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
index 07742ca..aa04dbe 100644
--- a/include/media/ICrypto.h
+++ b/include/media/ICrypto.h
@@ -25,6 +25,7 @@
 namespace android {
 
 struct AString;
+struct IMemory;
 
 struct ICrypto : public IInterface {
     DECLARE_META_INTERFACE(Crypto);
@@ -43,12 +44,14 @@
 
     virtual void notifyResolution(uint32_t width, uint32_t height) = 0;
 
+    virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
+
     virtual ssize_t decrypt(
             bool secure,
             const uint8_t key[16],
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
-            const void *srcPtr,
+            const sp<IMemory> &sharedBuffer, size_t offset,
             const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
             void *dstPtr,
             AString *errorDetailMsg) = 0;
@@ -61,6 +64,9 @@
     virtual status_t onTransact(
             uint32_t code, const Parcel &data, Parcel *reply,
             uint32_t flags = 0);
+private:
+    void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
+    void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
 };
 
 }  // namespace android
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 677119b..149bd49 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -81,6 +81,13 @@
     // with the next PTS occuring in the stream. The value is of type int64_t.
     static const char *const kKeyMediaTimeUs;
 
+    // Optionally signalled as part of a discontinuity that includes
+    // DISCONTINUITY_TIME. It indicates the media time (in us) of a recent
+    // sample from the same content, and is used as a hint for the parser to
+    // handle PTS wraparound. This is required when a new parser is created
+    // to continue parsing content from the same timeline.
+    static const char *const kKeyRecentMediaTimeUs;
+
     virtual void issueCommand(
             Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0;
 };
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index c1483f3..a8d0fcb 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -300,6 +300,7 @@
             OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
 
     status_t setPriority(int32_t priority);
+    status_t setOperatingRate(float rateFloat, bool isVideo);
 
     status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
 
diff --git a/include/media/stagefright/MediaClock.h b/include/media/stagefright/MediaClock.h
index e9c09a1..dd1a809 100644
--- a/include/media/stagefright/MediaClock.h
+++ b/include/media/stagefright/MediaClock.h
@@ -42,6 +42,7 @@
     void updateMaxTimeMedia(int64_t maxTimeMediaUs);
 
     void setPlaybackRate(float rate);
+    float getPlaybackRate() const;
 
     // query media time corresponding to real time |realUs|, and save the
     // result in |outMediaUs|.
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 8241e19..0786fb9 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -30,8 +30,10 @@
 struct AReplyToken;
 struct AString;
 struct CodecBase;
-struct ICrypto;
 struct IBatteryStats;
+struct ICrypto;
+struct IMemory;
+struct MemoryDealer;
 struct SoftwareRenderer;
 struct Surface;
 
@@ -51,6 +53,13 @@
         CB_OUTPUT_AVAILABLE = 2,
         CB_ERROR = 3,
         CB_OUTPUT_FORMAT_CHANGED = 4,
+        CB_CODEC_RELEASED = 5,
+    };
+
+    // used by CB_CODEC_RELEASED to tell the upper layer the cause of the release.
+    enum ReleaseReason {
+        REASON_UNKNOWN = 0,
+        REASON_RECLAIMED,  // resources reclaimed by resource manager
     };
 
     struct BatteryNotifier;
@@ -213,6 +222,7 @@
         uint32_t mBufferID;
         sp<ABuffer> mData;
         sp<ABuffer> mEncryptedData;
+        sp<IMemory> mSharedEncryptedBuffer;
         sp<AMessage> mNotify;
         sp<AMessage> mFormat;
         bool mOwnedByClient;
@@ -231,6 +241,7 @@
     sp<AMessage> mOutputFormat;
     sp<AMessage> mInputFormat;
     sp<AMessage> mCallback;
+    sp<MemoryDealer> mDealer;
 
     bool mBatteryStatNotified;
     bool mIsVideo;
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
index a795c80..ec3a10e 100644
--- a/include/media/stagefright/Utils.h
+++ b/include/media/stagefright/Utils.h
@@ -65,6 +65,17 @@
 
 AString uriDebugString(const AString &uri, bool incognito = false);
 
+struct HLSTime {
+    int32_t mSeq;
+    int64_t mTimeUs;
+    sp<AMessage> mMeta;
+
+    HLSTime(const sp<AMessage> &meta = NULL);
+    int64_t getSegmentTimeUs(bool midpoint = false) const;
+};
+
+bool operator <(const HLSTime &t0, const HLSTime &t1);
+
 }  // namespace android
 
 #endif  // UTILS_H_
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 5644428..6cc2e2b 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -25,6 +25,7 @@
 #include <utils/Log.h>
 #include <utils/RefBase.h>
 #include <audio_utils/roundup.h>
+#include <media/AudioResamplerPublic.h>
 #include <media/SingleStateQueue.h>
 
 namespace android {
@@ -113,6 +114,14 @@
                     mPosLoopQueue;
 };
 
+
+struct AudioTrackPlaybackRate {
+    float mSpeed;
+    float mPitch;
+};
+
+typedef SingleStateQueue<AudioTrackPlaybackRate> AudioTrackPlaybackRateQueue;
+
 // ----------------------------------------------------------------------------
 
 // Important: do not add any virtual methods, including ~
@@ -159,6 +168,8 @@
                 uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                             // or 0 == default. Write-only client, read-only server.
 
+                AudioTrackPlaybackRateQueue::Shared mPlaybackRateQueue;
+
                 // client write-only, server read-only
                 uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
 
@@ -313,7 +324,8 @@
     AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
             size_t frameSize, bool clientInServer = false)
         : ClientProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/,
-          clientInServer) { }
+          clientInServer),
+          mPlaybackRateMutator(&cblk->mPlaybackRateQueue) { }
     virtual ~AudioTrackClientProxy() { }
 
     // No barriers on the following operations, so the ordering of loads/stores
@@ -333,6 +345,13 @@
         mCblk->mSampleRate = sampleRate;
     }
 
+    void        setPlaybackRate(float speed, float pitch) {
+        AudioTrackPlaybackRate playbackRate;
+        playbackRate.mSpeed = speed;
+        playbackRate.mPitch = pitch;
+        mPlaybackRateMutator.push(playbackRate);
+    }
+
     virtual void flush();
 
     virtual uint32_t    getUnderrunFrames() const {
@@ -344,6 +363,9 @@
     bool        getStreamEndDone() const;
 
     status_t    waitStreamEndDone(const struct timespec *requested);
+
+private:
+    AudioTrackPlaybackRateQueue::Mutator   mPlaybackRateMutator;
 };
 
 class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -458,8 +480,11 @@
 public:
     AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
             size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
-        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) {
+        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
+          mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
         mCblk->mSampleRate = sampleRate;
+        mPlaybackRate.mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+        mPlaybackRate.mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
     }
 protected:
     virtual ~AudioTrackServerProxy() { }
@@ -493,6 +518,13 @@
 
     // Return the total number of frames that AudioFlinger has obtained and released
     virtual size_t      framesReleased() const { return mCblk->mServer; }
+
+    // Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
+    void                getPlaybackRate(float *speed, float *pitch);
+
+private:
+    AudioTrackPlaybackRate                  mPlaybackRate;  // last observed playback rate
+    AudioTrackPlaybackRateQueue::Observer   mPlaybackRateObserver;
 };
 
 class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 100a914..f4cdde2 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -189,13 +189,9 @@
     }
 
     // validate parameters
-    if (!audio_is_valid_format(format)) {
-        ALOGE("Invalid format %#x", format);
-        return BAD_VALUE;
-    }
-    // Temporary restriction: AudioFlinger currently supports 16-bit PCM only
-    if (format != AUDIO_FORMAT_PCM_16_BIT) {
-        ALOGE("Format %#x is not supported", format);
+    // AudioFlinger capture only supports linear PCM
+    if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
+        ALOGE("Format %#x is not linear pcm", format);
         return BAD_VALUE;
     }
     mFormat = format;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 9150a94..8db72ee 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -658,13 +658,14 @@
                                         audio_format_t format,
                                         audio_channel_mask_t channelMask,
                                         audio_output_flags_t flags,
+                                        audio_port_handle_t selectedDeviceId,
                                         const audio_offload_info_t *offloadInfo)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return NO_INIT;
     return aps->getOutputForAttr(attr, output, session, stream,
                                  samplingRate, format, channelMask,
-                                 flags, offloadInfo);
+                                 flags, selectedDeviceId, offloadInfo);
 }
 
 status_t AudioSystem::startOutput(audio_io_handle_t output,
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index ce30c62..89138e2 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -56,6 +56,24 @@
     return convertTimespecToUs(tv);
 }
 
+// Must match similar computation in createTrack_l in Threads.cpp.
+// TODO: Move to a common library
+static size_t calculateMinFrameCount(
+        uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
+        uint32_t sampleRate, float speed)
+{
+    // Ensure that buffer depth covers at least audio hardware latency
+    uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
+    if (minBufCount < 2) {
+        minBufCount = 2;
+    }
+    ALOGV("calculateMinFrameCount afLatency %u  afFrameCount %u  afSampleRate %u  "
+            "sampleRate %u  speed %f  minBufCount: %u",
+            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
+    return minBufCount * sourceFramesNeededWithTimestretch(
+            sampleRate, afFrameCount, afSampleRate, speed);
+}
+
 // static
 status_t AudioTrack::getMinFrameCount(
         size_t* frameCount,
@@ -94,13 +112,10 @@
         return status;
     }
 
-    // Ensure that buffer depth covers at least audio hardware latency
-    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
-    if (minBufCount < 2) {
-        minBufCount = 2;
-    }
+    // When called from createTrack, speed is 1.0f (normal speed).
+    // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
+    *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);
 
-    *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate);
     // The formula above should always produce a non-zero value under normal circumstances:
     // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
     // Return error in the unlikely event that it does not, as that's part of the API contract.
@@ -109,8 +124,8 @@
                 streamType, sampleRate);
         return BAD_VALUE;
     }
-    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u",
-            *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
+    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
+            *frameCount, afFrameCount, afSampleRate, afLatency);
     return NO_ERROR;
 }
 
@@ -121,7 +136,8 @@
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
-      mPausedPosition(0)
+      mPausedPosition(0),
+      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
 {
     mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
     mAttributes.usage = AUDIO_USAGE_UNKNOWN;
@@ -149,7 +165,8 @@
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
-      mPausedPosition(0)
+      mPausedPosition(0),
+      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
 {
     mStatus = set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
@@ -177,7 +194,8 @@
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
-      mPausedPosition(0)
+      mPausedPosition(0),
+      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
 {
     mStatus = set(streamType, sampleRate, format, channelMask,
             0 /*frameCount*/, flags, cbf, user, notificationFrames,
@@ -357,6 +375,8 @@
         return BAD_VALUE;
     }
     mSampleRate = sampleRate;
+    mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+    mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
 
     // Make copy of input parameter offloadInfo so that in the future:
     //  (a) createTrack_l doesn't need it as an input parameter
@@ -686,6 +706,7 @@
     if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
         return BAD_VALUE;
     }
+    // TODO: Should we also check if the buffer size is compatible?
 
     mSampleRate = rate;
     mProxy->setSampleRate(rate);
@@ -716,6 +737,42 @@
     return mSampleRate;
 }
 
+status_t AudioTrack::setPlaybackRate(float speed, float pitch)
+{
+    if (speed < AUDIO_TIMESTRETCH_SPEED_MIN
+            || speed > AUDIO_TIMESTRETCH_SPEED_MAX
+            || pitch < AUDIO_TIMESTRETCH_PITCH_MIN
+            || pitch > AUDIO_TIMESTRETCH_PITCH_MAX) {
+        return BAD_VALUE;
+    }
+    AutoMutex lock(mLock);
+    if (speed == mSpeed && pitch == mPitch) {
+        return NO_ERROR;
+    }
+    if (mIsTimed || isOffloadedOrDirect_l()) {
+        return INVALID_OPERATION;
+    }
+    if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+        return INVALID_OPERATION;
+    }
+    // Check if the buffer size is compatible.
+    if (!isSampleRateSpeedAllowed_l(mSampleRate, speed)) {
+        ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch);
+        return BAD_VALUE;
+    }
+    mSpeed = speed;
+    mPitch = pitch;
+    mProxy->setPlaybackRate(speed, pitch);
+    return NO_ERROR;
+}
+
+void AudioTrack::getPlaybackRate(float *speed, float *pitch) const
+{
+    AutoMutex lock(mLock);
+    *speed = mSpeed;
+    *pitch = mPitch;
+}
+
 status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
 {
     if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -928,6 +985,21 @@
     return mOutput;
 }
 
+status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+    AutoMutex lock(mLock);
+    if (mSelectedDeviceId != deviceId) {
+        mSelectedDeviceId = deviceId;
+        return restoreTrack_l("setOutputDevice() restart");
+    } else {
+        return NO_ERROR;
+    }
+}
+
+audio_port_handle_t AudioTrack::getOutputDevice() {
+    AutoMutex lock(mLock);
+    return mSelectedDeviceId;
+}
+
 status_t AudioTrack::attachAuxEffect(int effectId)
 {
     AutoMutex lock(mLock);
@@ -960,11 +1032,12 @@
     audio_io_handle_t output;
     audio_stream_type_t streamType = mStreamType;
     audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;
-    status_t status = AudioSystem::getOutputForAttr(attr, &output,
-                                                    (audio_session_t)mSessionId, &streamType,
-                                                    mSampleRate, mFormat, mChannelMask,
-                                                    mFlags, mOffloadInfo);
 
+    status_t status;
+    status = AudioSystem::getOutputForAttr(attr, &output,
+                                           (audio_session_t)mSessionId, &streamType,
+                                           mSampleRate, mFormat, mChannelMask,
+                                           mFlags, mSelectedDeviceId, mOffloadInfo);
 
     if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) {
         ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x,"
@@ -1067,8 +1140,16 @@
         // there _is_ a frameCount parameter.  We silently ignore it.
         frameCount = mSharedBuffer->size() / mFrameSize;
     } else {
-        // For fast and normal streaming tracks,
-        // the frame count calculations and checks are done by server
+        // For fast tracks the frame count calculations and checks are done by server
+
+        if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+            // for normal tracks precompute the frame count based on speed.
+            const size_t minFrameCount = calculateMinFrameCount(
+                    afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed);
+            if (frameCount < minFrameCount) {
+                frameCount = minFrameCount;
+            }
+        }
     }
 
     IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -1211,6 +1292,7 @@
     }
 
     mAudioTrack->attachAuxEffect(mAuxEffectId);
+    // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
     // FIXME don't believe this lie
     mLatency = afLatency + (1000*frameCount) / mSampleRate;
 
@@ -1236,6 +1318,7 @@
 
     mProxy->setSendLevel(mSendLevel);
     mProxy->setSampleRate(mSampleRate);
+    mProxy->setPlaybackRate(mSpeed, mPitch);
     mProxy->setMinimum(mNotificationFramesAct);
 
     mDeathNotifier = new DeathNotifier(this);
@@ -1598,6 +1681,7 @@
 
     // Cache other fields that will be needed soon
     uint32_t sampleRate = mSampleRate;
+    float speed = mSpeed;
     uint32_t notificationFrames = mNotificationFramesAct;
     if (mRefreshRemaining) {
         mRefreshRemaining = false;
@@ -1726,7 +1810,7 @@
     if (minFrames != (uint32_t) ~0) {
         // This "fudge factor" avoids soaking CPU, and compensates for late progress by server
         static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
-        ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
+        ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs;
     }
 
     // If not supplying data by EVENT_MORE_DATA, then we're done
@@ -1767,7 +1851,8 @@
         if (mRetryOnPartialBuffer && !isOffloaded()) {
             mRetryOnPartialBuffer = false;
             if (avail < mRemainingFrames) {
-                int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
+                int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000)
+                        / ((double)sampleRate * speed);
                 if (ns < 0 || myns < ns) {
                     ns = myns;
                 }
@@ -1822,7 +1907,7 @@
         // that total to a sum == notificationFrames.
         if (0 < misalignment && misalignment <= mRemainingFrames) {
             mRemainingFrames = misalignment;
-            return (mRemainingFrames * 1100000000LL) / sampleRate;
+            return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed);
         }
 #endif
 
@@ -1917,6 +2002,41 @@
     return mPosition += (uint32_t) delta;
 }
 
+bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
+{
+    // applicable for mixing tracks only (not offloaded or direct)
+    if (mStaticProxy != 0) {
+        return true; // static tracks do not have issues with buffer sizing.
+    }
+    status_t status;
+    uint32_t afLatency;
+    status = AudioSystem::getLatency(mOutput, &afLatency);
+    if (status != NO_ERROR) {
+        ALOGE("getLatency(%d) failed status %d", mOutput, status);
+        return false;
+    }
+
+    size_t afFrameCount;
+    status = AudioSystem::getFrameCount(mOutput, &afFrameCount);
+    if (status != NO_ERROR) {
+        ALOGE("getFrameCount(output=%d) status %d", mOutput, status);
+        return false;
+    }
+
+    uint32_t afSampleRate;
+    status = AudioSystem::getSamplingRate(mOutput, &afSampleRate);
+    if (status != NO_ERROR) {
+        ALOGE("getSamplingRate(output=%d) status %d", mOutput, status);
+        return false;
+    }
+
+    const size_t minFrameCount =
+            calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed);
+    ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu  minFrameCount %zu",
+            mFrameCount, minFrameCount);
+    return mFrameCount >= minFrameCount;
+}
+
 status_t AudioTrack::setParameters(const String8& keyValuePairs)
 {
     AutoMutex lock(mLock);
@@ -1982,7 +2102,8 @@
                     return WOULD_BLOCK;  // stale timestamp time, occurs before start.
                 }
                 const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
-                const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate;
+                const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
+                        / ((double)mSampleRate * mSpeed);
 
                 if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
                     // Verify that the counter can't count faster than the sample rate
@@ -2069,7 +2190,8 @@
     snprintf(buffer, 255, "  format(%d), channel count(%d), frame count(%zu)\n", mFormat,
             mChannelCount, mFrameCount);
     result.append(buffer);
-    snprintf(buffer, 255, "  sample rate(%u), status(%d)\n", mSampleRate, mStatus);
+    snprintf(buffer, 255, "  sample rate(%u), speed(%f), status(%d)\n",
+            mSampleRate, mSpeed, mStatus);
     result.append(buffer);
     snprintf(buffer, 255, "  state(%d), latency (%d)\n", mState, mLatency);
     result.append(buffer);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 6d5f1af..ba67b40 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -793,6 +793,16 @@
     (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
 }
 
+void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch)
+{   // do not call from multiple threads without holding lock
+    AudioTrackPlaybackRate playbackRate;
+    if (mPlaybackRateObserver.poll(playbackRate)) {
+        mPlaybackRate = playbackRate;
+    }
+    *speed = mPlaybackRate.mSpeed;
+    *pitch = mPlaybackRate.mPitch;
+}
+
 // ---------------------------------------------------------------------------
 
 StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 39374d8..4b86532 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -173,6 +173,7 @@
                                         audio_format_t format,
                                         audio_channel_mask_t channelMask,
                                         audio_output_flags_t flags,
+                                        audio_port_handle_t selectedDeviceId,
                                         const audio_offload_info_t *offloadInfo)
         {
             Parcel data, reply;
@@ -208,6 +209,7 @@
             data.writeInt32(static_cast <uint32_t>(format));
             data.writeInt32(channelMask);
             data.writeInt32(static_cast <uint32_t>(flags));
+            data.writeInt32(selectedDeviceId);
             // hasOffloadInfo
             if (offloadInfo == NULL) {
                 data.writeInt32(0);
@@ -815,6 +817,7 @@
             audio_channel_mask_t channelMask = data.readInt32();
             audio_output_flags_t flags =
                     static_cast <audio_output_flags_t>(data.readInt32());
+            audio_port_handle_t selectedDeviceId = data.readInt32();
             bool hasOffloadInfo = data.readInt32() != 0;
             audio_offload_info_t offloadInfo;
             if (hasOffloadInfo) {
@@ -824,7 +827,7 @@
             status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
                     &output, session, &stream,
                     samplingRate, format, channelMask,
-                    flags, hasOffloadInfo ? &offloadInfo : NULL);
+                    flags, selectedDeviceId, hasOffloadInfo ? &offloadInfo : NULL);
             reply->writeInt32(status);
             reply->writeInt32(output);
             reply->writeInt32(stream);
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index c26c5bf..9246a7c 100644
--- a/media/libmedia/ICrypto.cpp
+++ b/media/libmedia/ICrypto.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include <binder/Parcel.h>
+#include <binder/IMemory.h>
 #include <media/ICrypto.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -34,6 +35,7 @@
     REQUIRES_SECURE_COMPONENT,
     DECRYPT,
     NOTIFY_RESOLUTION,
+    SET_MEDIADRM_SESSION,
 };
 
 struct BpCrypto : public BpInterface<ICrypto> {
@@ -97,7 +99,7 @@
             const uint8_t key[16],
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
-            const void *srcPtr,
+            const sp<IMemory> &sharedBuffer, size_t offset,
             const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
             void *dstPtr,
             AString *errorDetailMsg) {
@@ -126,7 +128,8 @@
         }
 
         data.writeInt32(totalSize);
-        data.write(srcPtr, totalSize);
+        data.writeStrongBinder(IInterface::asBinder(sharedBuffer));
+        data.writeInt32(offset);
 
         data.writeInt32(numSubSamples);
         data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples);
@@ -159,7 +162,28 @@
         remote()->transact(NOTIFY_RESOLUTION, data, &reply);
     }
 
+    virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        remote()->transact(SET_MEDIADRM_SESSION, data, &reply);
+
+        return reply.readInt32();
+    }
+
 private:
+    void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
+        uint32_t size = reply.readInt32();
+        vector.insertAt((size_t)0, size);
+        reply.read(vector.editArray(), size);
+    }
+
+    void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
+        data.writeInt32(vector.size());
+        data.write(vector.array(), vector.size());
+    }
+
     DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
 };
 
@@ -167,6 +191,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
+    uint32_t size = data.readInt32();
+    vector.insertAt((size_t)0, size);
+    data.read(vector.editArray(), size);
+}
+
+void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
+    reply->writeInt32(vector.size());
+    reply->write(vector.array(), vector.size());
+}
+
 status_t BnCrypto::onTransact(
     uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
     switch (code) {
@@ -245,8 +280,9 @@
             data.read(iv, sizeof(iv));
 
             size_t totalSize = data.readInt32();
-            void *srcData = malloc(totalSize);
-            data.read(srcData, totalSize);
+            sp<IMemory> sharedBuffer =
+                interface_cast<IMemory>(data.readStrongBinder());
+            int32_t offset = data.readInt32();
 
             int32_t numSubSamples = data.readInt32();
 
@@ -265,15 +301,21 @@
             }
 
             AString errorDetailMsg;
-            ssize_t result = decrypt(
+            ssize_t result;
+
+            if (offset + totalSize > sharedBuffer->size()) {
+                result = -EINVAL;
+            } else {
+                result = decrypt(
                     secure,
                     key,
                     iv,
                     mode,
-                    srcData,
+                    sharedBuffer, offset,
                     subSamples, numSubSamples,
                     dstPtr,
                     &errorDetailMsg);
+            }
 
             reply->writeInt32(result);
 
@@ -294,9 +336,6 @@
             delete[] subSamples;
             subSamples = NULL;
 
-            free(srcData);
-            srcData = NULL;
-
             return OK;
         }
 
@@ -311,6 +350,15 @@
             return OK;
         }
 
+        case SET_MEDIADRM_SESSION:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId;
+            readVector(data, sessionId);
+            reply->writeInt32(setMediaDrmSession(sessionId));
+            return OK;
+        }
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp
index d480aef..840e453 100644
--- a/media/libmedia/IStreamSource.cpp
+++ b/media/libmedia/IStreamSource.cpp
@@ -35,6 +35,9 @@
 // static
 const char *const IStreamListener::kKeyMediaTimeUs = "media-time-us";
 
+// static
+const char *const IStreamListener::kKeyRecentMediaTimeUs = "recent-media-time-us";
+
 enum {
     // IStreamSource
     SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
index 8ee7c0b..f639193 100644
--- a/media/libmediaplayerservice/Crypto.cpp
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -22,6 +22,7 @@
 
 #include "Crypto.h"
 
+#include <binder/IMemory.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
@@ -238,7 +239,7 @@
         const uint8_t key[16],
         const uint8_t iv[16],
         CryptoPlugin::Mode mode,
-        const void *srcPtr,
+        const sp<IMemory> &sharedBuffer, size_t offset,
         const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
         void *dstPtr,
         AString *errorDetailMsg) {
@@ -252,6 +253,8 @@
         return -EINVAL;
     }
 
+    const void *srcPtr = static_cast<uint8_t *>(sharedBuffer->pointer()) + offset;
+
     return mPlugin->decrypt(
             secure, key, iv, mode, srcPtr, subSamples, numSubSamples, dstPtr,
             errorDetailMsg);
@@ -265,4 +268,14 @@
     }
 }
 
+status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+    Mutex::Autolock autoLock(mLock);
+
+    status_t result = NO_INIT;
+    if (mInitCheck == OK && mPlugin != NULL) {
+        result = mPlugin->setMediaDrmSession(sessionId);
+    }
+    return result;
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h
index 0037c2e..99ea95d 100644
--- a/media/libmediaplayerservice/Crypto.h
+++ b/media/libmediaplayerservice/Crypto.h
@@ -47,12 +47,14 @@
 
     virtual void notifyResolution(uint32_t width, uint32_t height);
 
+    virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
+
     virtual ssize_t decrypt(
             bool secure,
             const uint8_t key[16],
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
-            const void *srcPtr,
+            const sp<IMemory> &sharedBuffer, size_t offset,
             const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
             void *dstPtr,
             AString *errorDetailMsg);
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 49e01d1..62cf3e5 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -136,25 +136,57 @@
 
     if (listener != NULL) {
         Parcel obj;
-        if (sessionId && sessionId->size()) {
-            obj.writeInt32(sessionId->size());
-            obj.write(sessionId->array(), sessionId->size());
-        } else {
-            obj.writeInt32(0);
-        }
-
-        if (data && data->size()) {
-            obj.writeInt32(data->size());
-            obj.write(data->array(), data->size());
-        } else {
-            obj.writeInt32(0);
-        }
+        writeByteArray(obj, sessionId);
+        writeByteArray(obj, data);
 
         Mutex::Autolock lock(mNotifyLock);
         listener->notify(eventType, extra, &obj);
     }
 }
 
+void Drm::sendExpirationUpdate(Vector<uint8_t> const *sessionId,
+                               int64_t expiryTimeInMS)
+{
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Parcel obj;
+        writeByteArray(obj, sessionId);
+        obj.writeInt64(expiryTimeInMS);
+
+        Mutex::Autolock lock(mNotifyLock);
+        listener->notify(DrmPlugin::kDrmPluginEventExpirationUpdate, 0, &obj);
+    }
+}
+
+void Drm::sendKeysChange(Vector<uint8_t> const *sessionId,
+                         Vector<DrmPlugin::KeyStatus> const *keyStatusList,
+                         bool hasNewUsableKey)
+{
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Parcel obj;
+        writeByteArray(obj, sessionId);
+
+        size_t nkeys = keyStatusList->size();
+        obj.writeInt32(keyStatusList->size());
+        for (size_t i = 0; i < nkeys; ++i) {
+            const DrmPlugin::KeyStatus *keyStatus = &keyStatusList->itemAt(i);
+            writeByteArray(obj, &keyStatus->mKeyId);
+            obj.writeInt32(keyStatus->mType);
+        }
+        obj.writeInt32(hasNewUsableKey);
+
+        Mutex::Autolock lock(mNotifyLock);
+        listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj);
+    }
+}
+
 /*
  * Search the plugins directory for a plugin that supports the scheme
  * specified by uuid
@@ -756,4 +788,14 @@
     closeFactory();
 }
 
+void Drm::writeByteArray(Parcel &obj, Vector<uint8_t> const *array)
+{
+    if (array && array->size()) {
+        obj.writeInt32(array->size());
+        obj.write(array->array(), array->size());
+    } else {
+        obj.writeInt32(0);
+    }
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 7e8f246..1591738 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -133,6 +133,13 @@
                            Vector<uint8_t> const *sessionId,
                            Vector<uint8_t> const *data);
 
+    virtual void sendExpirationUpdate(Vector<uint8_t> const *sessionId,
+                                      int64_t expiryTimeInMS);
+
+    virtual void sendKeysChange(Vector<uint8_t> const *sessionId,
+                                Vector<DrmPlugin::KeyStatus> const *keyStatusList,
+                                bool hasNewUsableKey);
+
     virtual void binderDied(const wp<IBinder> &the_late_who);
 
 private:
@@ -157,7 +164,7 @@
     void findFactoryForScheme(const uint8_t uuid[16]);
     bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]);
     void closeFactory();
-
+    void writeByteArray(Parcel &obj, Vector<uint8_t> const *array);
 
     DISALLOW_EVIL_CONSTRUCTORS(Drm);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index cfa5258..02d9f32 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1257,6 +1257,8 @@
         notify->setInt32("generation", mAudioDecoderGeneration);
 
         if (mOffloadAudio) {
+            const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL);
+            format->setInt32("has-video", hasVideo);
             *decoder = new DecoderPassThrough(notify, mSource, mRenderer);
         } else {
             *decoder = new Decoder(notify, mSource, mRenderer);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 29b4c26..563de5e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -74,11 +74,14 @@
 
     onRequestInputBuffers();
 
+    int32_t hasVideo = 0;
+    format->findInt32("has-video", &hasVideo);
+
     // The audio sink is already opened before the PassThrough decoder is created.
     // Opening again might be relevant if decoder is instantiated after shutdown and
     // format is different.
     status_t err = mRenderer->openAudioSink(
-            format, true /* offloadOnly */, false /* hasVideo */,
+            format, true /* offloadOnly */, hasVideo,
             AUDIO_OUTPUT_FLAG_NONE /* flags */, NULL /* isOffloaded */);
     if (err != OK) {
         handleError(err);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 97f3e20..45f6339 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1685,6 +1685,16 @@
         err = setPriority(priority);
     }
 
+    int32_t rateInt = -1;
+    float rateFloat = -1;
+    if (!msg->findFloat("operating-rate", &rateFloat)) {
+        msg->findInt32("operating-rate", &rateInt);
+        rateFloat = (float)rateInt;  // 16MHz (FLINTMAX) is OK for upper bound.
+    }
+    if (rateFloat > 0) {
+        err = setOperatingRate(rateFloat, video);
+    }
+
     mBaseOutputFormat = outputFormat;
 
     CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK);
@@ -1711,6 +1721,34 @@
     return OK;
 }
 
+status_t ACodec::setOperatingRate(float rateFloat, bool isVideo) {
+    if (rateFloat < 0) {
+        return BAD_VALUE;
+    }
+    OMX_U32 rate;
+    if (isVideo) {
+        if (rateFloat > 65535) {
+            return BAD_VALUE;
+        }
+        rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f);
+    } else {
+        if (rateFloat > UINT_MAX) {
+            return BAD_VALUE;
+        }
+        rate = (OMX_U32)(rateFloat);
+    }
+    OMX_PARAM_U32TYPE config;
+    InitOMXParams(&config);
+    config.nU32 = rate;
+    status_t err = mOMX->setConfig(
+            mNode, (OMX_INDEXTYPE)OMX_IndexConfigOperatingRate,
+            &config, sizeof(config));
+    if (err != OK) {
+        ALOGI("codec does not support config operating rate (err %d)", err);
+    }
+    return OK;
+}
+
 status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
     OMX_PARAM_PORTDEFINITIONTYPE def;
     InitOMXParams(&def);
@@ -4902,6 +4940,7 @@
     sp<CodecObserver> observer = new CodecObserver;
     IOMX::node_id node = NULL;
 
+    status_t err = OMX_ErrorComponentNotFound;
     for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
             ++matchIndex) {
         componentName = matchingCodecs.itemAt(matchIndex).mName.string();
@@ -4910,7 +4949,7 @@
         pid_t tid = gettid();
         int prevPriority = androidGetThreadPriority(tid);
         androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
-        status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+        err = omx->allocateNode(componentName.c_str(), observer, &node);
         androidSetThreadPriority(tid, prevPriority);
 
         if (err == OK) {
@@ -4924,13 +4963,13 @@
 
     if (node == NULL) {
         if (!mime.empty()) {
-            ALOGE("Unable to instantiate a %scoder for type '%s'.",
-                    encoder ? "en" : "de", mime.c_str());
+            ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.",
+                    encoder ? "en" : "de", mime.c_str(), err);
         } else {
-            ALOGE("Unable to instantiate codec '%s'.", componentName.c_str());
+            ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);
         }
 
-        mCodec->signalError(OMX_ErrorComponentNotFound);
+        mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));
         return false;
     }
 
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
index 427bf7b..8fbb57c 100644
--- a/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/ESDS.cpp
@@ -136,6 +136,8 @@
     --size;
 
     if (streamDependenceFlag) {
+        if (size < 2)
+            return ERROR_MALFORMED;
         offset += 2;
         size -= 2;
     }
@@ -145,11 +147,15 @@
             return ERROR_MALFORMED;
         }
         unsigned URLlength = mData[offset];
+        if (URLlength >= size)
+            return ERROR_MALFORMED;
         offset += URLlength + 1;
         size -= URLlength + 1;
     }
 
     if (OCRstreamFlag) {
+        if (size < 2)
+            return ERROR_MALFORMED;
         offset += 2;
         size -= 2;
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d0f42cc..f7fa2b6 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -874,6 +874,9 @@
                     }
                 }
 
+                if (mLastTrack == NULL)
+                    return ERROR_MALFORMED;
+
                 mLastTrack->sampleTable = new SampleTable(mDataSource);
             }
 
@@ -1028,6 +1031,10 @@
             }
             original_fourcc = ntohl(original_fourcc);
             ALOGV("read original format: %d", original_fourcc);
+
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
             uint32_t num_channels = 0;
             uint32_t sample_rate = 0;
@@ -1083,6 +1090,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
             mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
             mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
@@ -1168,6 +1178,11 @@
                 return ERROR_IO;
             }
 
+            if (!timescale) {
+                ALOGE("timescale should not be ZERO.");
+                return ERROR_MALFORMED;
+            }
+
             mLastTrack->timescale = ntohl(timescale);
 
             // 14496-12 says all ones means indeterminate, but some files seem to use
@@ -1193,7 +1208,7 @@
                     duration = ntohl(duration32);
                 }
             }
-            if (duration != 0) {
+            if (duration != 0 && mLastTrack->timescale != 0) {
                 mLastTrack->meta->setInt64(
                         kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
             }
@@ -1257,6 +1272,10 @@
                 // display the timed text.
                 // For encrypted files, there may also be more than one entry.
                 const char *mime;
+
+                if (mLastTrack == NULL)
+                    return ERROR_MALFORMED;
+
                 CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
                 if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
                         strcasecmp(mime, "application/octet-stream")) {
@@ -1303,6 +1322,9 @@
             uint16_t sample_size = U16_AT(&buffer[18]);
             uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
                 // if the chunk type is enca, we'll get the type from the sinf/frma box later
                 mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1364,6 +1386,9 @@
             // printf("*** coding='%s' width=%d height=%d\n",
             //        chunk, width, height);
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
                 // if the chunk type is encv, we'll get the type from the sinf/frma box later
                 mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1389,6 +1414,9 @@
         case FOURCC('s', 't', 'c', 'o'):
         case FOURCC('c', 'o', '6', '4'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             status_t err =
                 mLastTrack->sampleTable->setChunkOffsetParams(
                         chunk_type, data_offset, chunk_data_size);
@@ -1404,6 +1432,9 @@
 
         case FOURCC('s', 't', 's', 'c'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             status_t err =
                 mLastTrack->sampleTable->setSampleToChunkParams(
                         data_offset, chunk_data_size);
@@ -1420,6 +1451,9 @@
         case FOURCC('s', 't', 's', 'z'):
         case FOURCC('s', 't', 'z', '2'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             status_t err =
                 mLastTrack->sampleTable->setSampleSizeParams(
                         chunk_type, data_offset, chunk_data_size);
@@ -1489,6 +1523,9 @@
 
         case FOURCC('s', 't', 't', 's'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             *offset += chunk_size;
 
             status_t err =
@@ -1504,6 +1541,9 @@
 
         case FOURCC('c', 't', 't', 's'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             *offset += chunk_size;
 
             status_t err =
@@ -1519,6 +1559,9 @@
 
         case FOURCC('s', 't', 's', 's'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             *offset += chunk_size;
 
             status_t err =
@@ -1591,6 +1634,9 @@
                 return ERROR_MALFORMED;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(
                     kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
 
@@ -1623,6 +1669,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(
                     kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
 
@@ -1637,6 +1686,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(
                     kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);
 
@@ -1670,6 +1722,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
 
             break;
@@ -1767,7 +1822,7 @@
                 }
                 duration = d32;
             }
-            if (duration != 0) {
+            if (duration != 0 && mHeaderTimescale != 0) {
                 mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
             }
 
@@ -1816,7 +1871,7 @@
                 return ERROR_MALFORMED;
             }
 
-            if (duration != 0) {
+            if (duration != 0 && mHeaderTimescale != 0) {
                 mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
             }
 
@@ -1851,6 +1906,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             uint32_t type = ntohl(buffer);
             // For the 3GPP file format, the handler-type within the 'hdlr' box
             // shall be 'text'. We also want to support 'sbtl' handler type
@@ -1883,6 +1941,9 @@
 
         case FOURCC('t', 'x', '3', 'g'):
         {
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             uint32_t type;
             const void *data;
             size_t size = 0;
@@ -2024,6 +2085,8 @@
         return ERROR_MALFORMED;
     }
     ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);
+    if (timeScale == 0)
+        return ERROR_MALFORMED;
 
     uint64_t earliestPresentationTime;
     uint64_t firstOffset;
@@ -2107,6 +2170,9 @@
 
     uint64_t sidxDuration = total_duration * 1000000 / timeScale;
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     int64_t metaDuration;
     if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
         mLastTrack->meta->setInt64(kKeyDuration, sidxDuration);
@@ -2157,6 +2223,9 @@
         return ERROR_UNSUPPORTED;
     }
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     mLastTrack->meta->setInt32(kKeyTrackID, id);
 
     size_t matrixOffset = dynSize + 16;
@@ -2339,6 +2408,9 @@
                     int32_t delay, padding;
                     if (sscanf(mLastCommentData,
                                " %*x %x %x %*x", &delay, &padding) == 2) {
+                        if (mLastTrack == NULL)
+                            return ERROR_MALFORMED;
+
                         mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
                         mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
                     }
@@ -2635,6 +2707,11 @@
         return ERROR_MALFORMED;
     }
 
+    if (track->timescale == 0) {
+        ALOGE("timescale invalid.");
+        return ERROR_MALFORMED;
+    }
+
     return OK;
 }
 
@@ -2701,6 +2778,9 @@
 
     if (objectTypeIndication == 0xe1) {
         // This isn't MPEG4 audio at all, it's QCELP 14k...
+        if (mLastTrack == NULL)
+            return ERROR_MALFORMED;
+
         mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
         return OK;
     }
@@ -2749,6 +2829,9 @@
         objectType = 32 + br.getBits(6);
     }
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     //keep AOT type
     mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
 
@@ -2919,6 +3002,9 @@
         return ERROR_UNSUPPORTED;
     }
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     int32_t prevSampleRate;
     CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
 
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 433f555..2641e4e 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -92,6 +92,11 @@
     mPlaybackRate = rate;
 }
 
+float MediaClock::getPlaybackRate() const {
+    Mutex::Autolock autoLock(mLock);
+    return mPlaybackRate;
+}
+
 status_t MediaClock::getMediaTime(
         int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
     if (outMediaUs == NULL) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 0597f1d..8186f63 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -22,7 +22,9 @@
 #include "include/SoftwareRenderer.h"
 
 #include <binder/IBatteryStats.h>
+#include <binder/IMemory.h>
 #include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -969,6 +971,17 @@
 
                     size_t numBuffers = portDesc->countBuffers();
 
+                    size_t totalSize = 0;
+                    for (size_t i = 0; i < numBuffers; ++i) {
+                        if (portIndex == kPortIndexInput && mCrypto != NULL) {
+                            totalSize += portDesc->bufferAt(i)->capacity();
+                        }
+                    }
+
+                    if (totalSize) {
+                        mDealer = new MemoryDealer(totalSize, "MediaCodec");
+                    }
+
                     for (size_t i = 0; i < numBuffers; ++i) {
                         BufferInfo info;
                         info.mBufferID = portDesc->bufferIDAt(i);
@@ -976,8 +989,10 @@
                         info.mData = portDesc->bufferAt(i);
 
                         if (portIndex == kPortIndexInput && mCrypto != NULL) {
+                            sp<IMemory> mem = mDealer->allocate(info.mData->capacity());
                             info.mEncryptedData =
-                                new ABuffer(info.mData->capacity());
+                                new ABuffer(mem->pointer(), info.mData->capacity());
+                            info.mSharedEncryptedBuffer = mem;
                         }
 
                         buffers->push_back(info);
@@ -1953,7 +1968,8 @@
                 key,
                 iv,
                 mode,
-                info->mEncryptedData->base() + offset,
+                info->mSharedEncryptedBuffer,
+                offset,
                 subSamples,
                 numSubSamples,
                 info->mData->base(),
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index bdd6d56..aba64d5 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -230,8 +230,13 @@
         return ERROR_MALFORMED;
     }
 
+    if (SIZE_MAX / sizeof(SampleToChunkEntry) <= mNumSampleToChunkOffsets)
+        return ERROR_OUT_OF_RANGE;
+
     mSampleToChunkEntries =
-        new SampleToChunkEntry[mNumSampleToChunkOffsets];
+        new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets];
+    if (!mSampleToChunkEntries)
+        return ERROR_OUT_OF_RANGE;
 
     for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
         uint8_t buffer[12];
@@ -330,11 +335,13 @@
     }
 
     mTimeToSampleCount = U32_AT(&header[4]);
-    uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
+    uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
-    mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+    mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2];
+    if (!mTimeToSample)
+        return ERROR_OUT_OF_RANGE;
 
     size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
     if (mDataSource->readAt(
@@ -376,12 +383,14 @@
     }
 
     mNumCompositionTimeDeltaEntries = numEntries;
-    uint64_t allocSize = numEntries * 2 * sizeof(uint32_t);
+    uint64_t allocSize = numEntries * 2 * (uint64_t)sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
 
-    mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries];
+    mCompositionTimeDeltaEntries = new (std::nothrow) uint32_t[2 * numEntries];
+    if (!mCompositionTimeDeltaEntries)
+        return ERROR_OUT_OF_RANGE;
 
     if (mDataSource->readAt(
                 data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
@@ -426,12 +435,15 @@
         ALOGV("Table of sync samples is empty or has only a single entry!");
     }
 
-    uint64_t allocSize = mNumSyncSamples * sizeof(uint32_t);
+    uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
 
-    mSyncSamples = new uint32_t[mNumSyncSamples];
+    mSyncSamples = new (std::nothrow) uint32_t[mNumSyncSamples];
+    if (!mSyncSamples)
+        return ERROR_OUT_OF_RANGE;
+
     size_t size = mNumSyncSamples * sizeof(uint32_t);
     if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
             != (ssize_t)size) {
@@ -499,7 +511,9 @@
         return;
     }
 
-    mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+    mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
+    if (!mSampleTimeEntries)
+        return;
 
     uint32_t sampleIndex = 0;
     uint32_t sampleTime = 0;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index c0be136..8506e37 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -822,5 +822,36 @@
     return AString("<no-scheme URI suppressed>");
 }
 
+HLSTime::HLSTime(const sp<AMessage>& meta) :
+    mSeq(-1),
+    mTimeUs(-1ll),
+    mMeta(meta) {
+    if (meta != NULL) {
+        CHECK(meta->findInt32("discontinuitySeq", &mSeq));
+        CHECK(meta->findInt64("timeUs", &mTimeUs));
+    }
+}
+
+int64_t HLSTime::getSegmentTimeUs(bool midpoint) const {
+    int64_t segmentStartTimeUs = -1ll;
+    if (mMeta != NULL) {
+        CHECK(mMeta->findInt64("segmentStartTimeUs", &segmentStartTimeUs));
+        if (midpoint) {
+            int64_t durationUs;
+            CHECK(mMeta->findInt64("segmentDurationUs", &durationUs));
+            segmentStartTimeUs += durationUs / 2;
+        }
+    }
+    return segmentStartTimeUs;
+}
+
+bool operator <(const HLSTime &t0, const HLSTime &t1) {
+    // we can only compare discontinuity sequence and timestamp.
+    // (mSegmentTimeUs is not reliable in live streaming case, it's the
+    // time starting from beginning of playlist but playlist could change.)
+    return t0.mSeq < t1.mSeq
+            || (t0.mSeq == t1.mSeq && t0.mTimeUs < t1.mTimeUs);
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 118c174..74f58e9 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -52,9 +52,10 @@
 
 // static
 // Bandwidth Switch Mark Defaults
-const int64_t LiveSession::kUpSwitchMarkUs = 25000000ll;
-const int64_t LiveSession::kDownSwitchMarkUs = 18000000ll;
+const int64_t LiveSession::kUpSwitchMarkUs = 15000000ll;
+const int64_t LiveSession::kDownSwitchMarkUs = 20000000ll;
 const int64_t LiveSession::kUpSwitchMarginUs = 5000000ll;
+const int64_t LiveSession::kResumeThresholdUs = 100000ll;
 
 // Buffer Prepare/Ready/Underflow Marks
 const int64_t LiveSession::kReadyMarkUs = 5000000ll;
@@ -70,7 +71,7 @@
 private:
     // Bandwidth estimation parameters
     static const int32_t kMaxBandwidthHistoryItems = 20;
-    static const int64_t kMaxBandwidthHistoryWindowUs = 3000000ll; // 3 sec
+    static const int64_t kMaxBandwidthHistoryWindowUs = 5000000ll; // 5 sec
 
     struct BandwidthEntry {
         int64_t mDelayUs;
@@ -140,6 +141,21 @@
     return NULL;
 }
 
+//static
+const char *LiveSession::getNameForStream(StreamType type) {
+    switch (type) {
+        case STREAMTYPE_VIDEO:
+            return "video";
+        case STREAMTYPE_AUDIO:
+            return "audio";
+        case STREAMTYPE_SUBTITLES:
+            return "subs";
+        default:
+            break;
+    }
+    return "unknown";
+}
+
 LiveSession::LiveSession(
         const sp<AMessage> &notify, uint32_t flags,
         const sp<IMediaHTTPService> &httpService)
@@ -191,7 +207,11 @@
     status_t finalResult = OK;
     sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
 
-    ssize_t idx = typeToIndex(stream);
+    ssize_t streamIdx = typeToIndex(stream);
+    if (streamIdx < 0) {
+        return INVALID_VALUE;
+    }
+    const char *streamStr = getNameForStream(stream);
     // Do not let client pull data if we don't have data packets yet.
     // We might only have a format discontinuity queued without data.
     // When NuPlayerDecoder dequeues the format discontinuity, it will
@@ -199,6 +219,9 @@
     // thinks it can do seamless change, so will not shutdown decoder.
     // When the actual format arrives, it can't handle it and get stuck.
     if (!packetSource->hasDataBufferAvailable(&finalResult)) {
+        ALOGV("[%s] dequeueAccessUnit: no buffer available (finalResult=%d)",
+                streamStr, finalResult);
+
         if (finalResult == OK) {
             return -EAGAIN;
         } else {
@@ -211,25 +234,6 @@
 
     status_t err = packetSource->dequeueAccessUnit(accessUnit);
 
-    size_t streamIdx;
-    const char *streamStr;
-    switch (stream) {
-        case STREAMTYPE_AUDIO:
-            streamIdx = kAudioIndex;
-            streamStr = "audio";
-            break;
-        case STREAMTYPE_VIDEO:
-            streamIdx = kVideoIndex;
-            streamStr = "video";
-            break;
-        case STREAMTYPE_SUBTITLES:
-            streamIdx = kSubtitleIndex;
-            streamStr = "subs";
-            break;
-        default:
-            TRESPASS();
-    }
-
     StreamItem& strm = mStreams[streamIdx];
     if (err == INFO_DISCONTINUITY) {
         // adaptive streaming, discontinuities in the playlist
@@ -248,9 +252,10 @@
     } else if (err == OK) {
 
         if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
-            int64_t timeUs;
+            int64_t timeUs, originalTimeUs;
             int32_t discontinuitySeq = 0;
             CHECK((*accessUnit)->meta()->findInt64("timeUs",  &timeUs));
+            originalTimeUs = timeUs;
             (*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq);
             if (discontinuitySeq > (int32_t) strm.mCurDiscontinuitySeq) {
                 int64_t offsetTimeUs;
@@ -302,7 +307,8 @@
                 timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
             }
 
-            ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs);
+            ALOGV("[%s] dequeueAccessUnit: time %lld us, original %lld us",
+                    streamStr, (long long)timeUs, (long long)originalTimeUs);
             (*accessUnit)->meta()->setInt64("timeUs",  timeUs);
             mLastDequeuedTimeUs = timeUs;
             mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
@@ -405,26 +411,30 @@
         sp<AMessage> lastDequeueMeta, lastEnqueueMeta;
         if (delayUs > 0) {
             lastDequeueMeta = source->getMetaAfterLastDequeued(delayUs);
+            if (lastDequeueMeta == NULL) {
+                // this means we don't have enough cushion, try again later
+                ALOGV("[%s] up switching failed due to insufficient buffer",
+                        getNameForStream(stream));
+                return false;
+            }
         } else {
+            // It's okay for lastDequeueMeta to be NULL here, it means the
+            // decoder hasn't even started dequeueing
             lastDequeueMeta = source->getLatestDequeuedMeta();
         }
         // Then, trim off packets at beginning of mPacketSources2 that's before
         // the latest dequeued time. These samples are definitely too late.
-        int64_t lastTimeUs, startTimeUs;
-        int32_t lastSeq, startSeq;
-        if (lastDequeueMeta != NULL) {
-            CHECK(lastDequeueMeta->findInt64("timeUs", &lastTimeUs));
-            CHECK(lastDequeueMeta->findInt32("discontinuitySeq", &lastSeq));
-            firstNewMeta[i] = mPacketSources2.editValueAt(i)
-                    ->trimBuffersBeforeTimeUs(lastSeq, lastTimeUs);
-        }
+        firstNewMeta[i] = mPacketSources2.editValueAt(i)
+                            ->trimBuffersBeforeMeta(lastDequeueMeta);
+
         // Now firstNewMeta[i] is the first sample after the trim.
         // If it's NULL, we failed because dequeue already past all samples
         // in mPacketSource2, we have to try again.
         if (firstNewMeta[i] == NULL) {
+            HLSTime dequeueTime(lastDequeueMeta);
             ALOGV("[%s] dequeue time (%d, %lld) past start time",
-                    stream == STREAMTYPE_AUDIO ? "audio" : "video",
-                            lastSeq, (long long) lastTimeUs);
+                    getNameForStream(stream),
+                    dequeueTime.mSeq, (long long) dequeueTime.mTimeUs);
             return false;
         }
 
@@ -434,20 +444,16 @@
         // lastEnqueueMeta == NULL means old fetcher stopped at a discontinuity
         // boundary, no need to resume as the content will look different anyways
         if (lastEnqueueMeta != NULL) {
-            CHECK(lastEnqueueMeta->findInt64("timeUs", &lastTimeUs));
-            CHECK(lastEnqueueMeta->findInt32("discontinuitySeq", &lastSeq));
-            CHECK(firstNewMeta[i]->findInt64("timeUs", &startTimeUs));
-            CHECK(firstNewMeta[i]->findInt32("discontinuitySeq", &startSeq));
+            HLSTime lastTime(lastEnqueueMeta), startTime(firstNewMeta[i]);
 
             // no need to resume old fetcher if new fetcher started in different
             // discontinuity sequence, as the content will look different.
-            *needResumeUntil |=
-                    (startSeq == lastSeq
-                            && startTimeUs - lastTimeUs > 100000ll);
+            *needResumeUntil |= (startTime.mSeq == lastTime.mSeq
+                    && startTime.mTimeUs - lastTime.mTimeUs > kResumeThresholdUs);
 
-            // update the stopTime for resumeUntil, as we might have removed some
-            // packets from the head in mPacketSource2
-            stopParams->setInt64(getKeyForStream(stream), startTimeUs);
+            // update the stopTime for resumeUntil
+            stopParams->setInt32("discontinuitySeq", startTime.mSeq);
+            stopParams->setInt64(getKeyForStream(stream), startTime.mTimeUs);
         }
     }
 
@@ -457,18 +463,11 @@
     for (size_t i = 0; i < kMaxStreams; ++i) {
         StreamType stream = indexToType(i);
         if (!(mSwapMask & mNewStreamMask & stream)
-            || (newUri != mStreams[i].mNewUri)) {
+            || (newUri != mStreams[i].mNewUri)
+            || stream == STREAMTYPE_SUBTITLES) {
             continue;
         }
-        if (stream == STREAMTYPE_SUBTITLES) {
-            continue;
-        }
-        int64_t startTimeUs;
-        int32_t startSeq;
-        CHECK(firstNewMeta[i] != NULL);
-        CHECK(firstNewMeta[i]->findInt64("timeUs", &startTimeUs));
-        CHECK(firstNewMeta[i]->findInt32("discontinuitySeq", &startSeq));
-        mPacketSources.valueFor(stream)->trimBuffersAfterTimeUs(startSeq, startTimeUs);
+        mPacketSources.valueFor(stream)->trimBuffersAfterMeta(firstNewMeta[i]);
     }
 
     // no resumeUntil if already underflow
@@ -499,16 +498,15 @@
 
         case kWhatSeek:
         {
-            sp<AReplyToken> seekReplyID;
-            CHECK(msg->senderAwaitsResponse(&seekReplyID));
-            mSeekReplyID = seekReplyID;
+            if (mReconfigurationInProgress) {
+                msg->post(50000);
+                break;
+            }
+
+            CHECK(msg->senderAwaitsResponse(&mSeekReplyID));
             mSeekReply = new AMessage;
 
-            status_t err = onSeek(msg);
-
-            if (err != OK) {
-                msg->post(50000);
-            }
+            onSeek(msg);
             break;
         }
 
@@ -531,6 +529,11 @@
                         break;
                     }
 
+                    ALOGV("fetcher-%d %s",
+                            mFetcherInfos[index].mFetcher->getFetcherID(),
+                            what == PlaylistFetcher::kWhatPaused ?
+                                    "paused" : "stopped");
+
                     if (what == PlaylistFetcher::kWhatStopped) {
                         mFetcherLooper->unregisterHandler(
                                 mFetcherInfos[index].mFetcher->id());
@@ -550,6 +553,7 @@
                         if (--mContinuationCounter == 0) {
                             mContinuation->post();
                         }
+                        ALOGV("%zu fetcher(s) left", mContinuationCounter);
                     }
                     break;
                 }
@@ -574,7 +578,7 @@
                 {
                     int64_t targetDurationUs;
                     CHECK(msg->findInt64("targetDurationUs", &targetDurationUs));
-                    mUpSwitchMark = min(kUpSwitchMarkUs, targetDurationUs * 3);
+                    mUpSwitchMark = min(kUpSwitchMarkUs, targetDurationUs * 7 / 4);
                     mDownSwitchMark = min(kDownSwitchMarkUs, targetDurationUs * 9 / 4);
                     mUpSwitchMargin = min(kUpSwitchMarginUs, targetDurationUs);
                     break;
@@ -625,15 +629,15 @@
                 {
                     ALOGV("kWhatStopReached");
 
-                    AString uri;
-                    CHECK(msg->findString("uri", &uri));
+                    AString oldUri;
+                    CHECK(msg->findString("uri", &oldUri));
 
-                    ssize_t index = mFetcherInfos.indexOfKey(uri);
+                    ssize_t index = mFetcherInfos.indexOfKey(oldUri);
                     if (index < 0) {
                         break;
                     }
 
-                    tryToFinishBandwidthSwitch(uri);
+                    tryToFinishBandwidthSwitch(oldUri);
                     break;
                 }
 
@@ -642,6 +646,9 @@
                     int32_t switchGeneration;
                     CHECK(msg->findInt32("switchGeneration", &switchGeneration));
 
+                    ALOGV("kWhatStartedAt: switchGen=%d, mSwitchGen=%d",
+                            switchGeneration, mSwitchGeneration);
+
                     if (switchGeneration != mSwitchGeneration) {
                         break;
                     }
@@ -673,6 +680,7 @@
                     if (checkSwitchProgress(stopParams, delayUs, &needResumeUntil)) {
                         // playback time hasn't passed startAt time
                         if (!needResumeUntil) {
+                            ALOGV("finish switch");
                             for (size_t i = 0; i < kMaxStreams; ++i) {
                                 if ((mSwapMask & indexToType(i))
                                         && uri == mStreams[i].mNewUri) {
@@ -688,6 +696,7 @@
                             // Resume fetcher for the original variant; the resumed fetcher should
                             // continue until the timestamps found in msg, which is stored by the
                             // new fetcher to indicate where the new variant has started buffering.
+                            ALOGV("finish switch with resumeUntilAsync");
                             for (size_t i = 0; i < mFetcherInfos.size(); i++) {
                                 const FetcherInfo &info = mFetcherInfos.valueAt(i);
                                 if (info.mToBeRemoved) {
@@ -699,8 +708,10 @@
                         // playback time passed startAt time
                         if (switchUp) {
                             // if switching up, cancel and retry if condition satisfies again
+                            ALOGV("cancel up switch because we're too late");
                             cancelBandwidthSwitch(true /* resume */);
                         } else {
+                            ALOGV("retry down switch at next sample");
                             resumeFetcher(uri, mSwapMask, -1, true /* newUri */);
                         }
                     }
@@ -837,6 +848,7 @@
     size_t initialBandwidthIndex = 0;
 
     if (mPlaylist->isVariantPlaylist()) {
+        Vector<BandwidthItem> itemsWithVideo;
         for (size_t i = 0; i < mPlaylist->size(); ++i) {
             BandwidthItem item;
 
@@ -848,14 +860,22 @@
 
             CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
 
-            if (initialBandwidth == 0) {
-                initialBandwidth = item.mBandwidth;
-            }
-
             mBandwidthItems.push(item);
+            if (mPlaylist->hasType(i, "video")) {
+                itemsWithVideo.push(item);
+            }
+        }
+        // remove the audio-only variants if we have at least one with video
+        if (!itemsWithVideo.empty()
+                && itemsWithVideo.size() < mBandwidthItems.size()) {
+            mBandwidthItems.clear();
+            for (size_t i = 0; i < itemsWithVideo.size(); ++i) {
+                mBandwidthItems.push(itemsWithVideo[i]);
+            }
         }
 
         CHECK_GT(mBandwidthItems.size(), 0u);
+        initialBandwidth = mBandwidthItems[0].mBandwidth;
 
         mBandwidthItems.sort(SortByBandwidth);
 
@@ -930,7 +950,8 @@
     notify->setInt32("switchGeneration", mSwitchGeneration);
 
     FetcherInfo info;
-    info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration);
+    info.mFetcher = new PlaylistFetcher(
+            notify, this, uri, mCurBandwidthIndex, mSubtitleGeneration);
     info.mDurationUs = -1ll;
     info.mToBeRemoved = false;
     info.mToBeResumed = false;
@@ -1090,6 +1111,9 @@
     String8 actualUrl;
     ssize_t  err = fetchFile(url, &buffer, 0, -1, 0, NULL, &actualUrl);
 
+    // close off the connection after use
+    mHTTPDataSource->disconnect();
+
     if (err <= 0) {
         return NULL;
     }
@@ -1161,9 +1185,13 @@
     }
 
     if (resume) {
-        ALOGV("resuming fetcher %s, timeUs %lld", uri.c_str(), (long long)timeUs);
+        sp<PlaylistFetcher> &fetcher = mFetcherInfos.editValueAt(index).mFetcher;
         SeekMode seekMode = newUri ? kSeekModeNextSample : kSeekModeExactPosition;
-        mFetcherInfos.editValueAt(index).mFetcher->startAsync(
+
+        ALOGV("resuming fetcher-%d, timeUs=%lld, seekMode=%d",
+                fetcher->getFetcherID(), (long long)timeUs, seekMode);
+
+        fetcher->startAsync(
                 sources[kAudioIndex],
                 sources[kVideoIndex],
                 sources[kSubtitleIndex],
@@ -1333,34 +1361,20 @@
     return index;
 }
 
-int64_t LiveSession::latestMediaSegmentStartTimeUs() {
-    sp<AMessage> audioMeta = mPacketSources.valueFor(STREAMTYPE_AUDIO)->getLatestDequeuedMeta();
-    int64_t minSegmentStartTimeUs = -1, videoSegmentStartTimeUs = -1;
-    if (audioMeta != NULL) {
-        audioMeta->findInt64("segmentStartTimeUs", &minSegmentStartTimeUs);
-    }
+HLSTime LiveSession::latestMediaSegmentStartTime() const {
+    HLSTime audioTime(mPacketSources.valueFor(
+                    STREAMTYPE_AUDIO)->getLatestDequeuedMeta());
 
-    sp<AMessage> videoMeta = mPacketSources.valueFor(STREAMTYPE_VIDEO)->getLatestDequeuedMeta();
-    if (videoMeta != NULL
-            && videoMeta->findInt64("segmentStartTimeUs", &videoSegmentStartTimeUs)) {
-        if (minSegmentStartTimeUs < 0 || videoSegmentStartTimeUs < minSegmentStartTimeUs) {
-            minSegmentStartTimeUs = videoSegmentStartTimeUs;
-        }
+    HLSTime videoTime(mPacketSources.valueFor(
+                    STREAMTYPE_VIDEO)->getLatestDequeuedMeta());
 
-    }
-    return minSegmentStartTimeUs;
+    return audioTime < videoTime ? videoTime : audioTime;
 }
 
-status_t LiveSession::onSeek(const sp<AMessage> &msg) {
+void LiveSession::onSeek(const sp<AMessage> &msg) {
     int64_t timeUs;
     CHECK(msg->findInt64("timeUs", &timeUs));
-
-    if (!mReconfigurationInProgress) {
-        changeConfiguration(timeUs);
-        return OK;
-    } else {
-        return -EWOULDBLOCK;
-    }
+    changeConfiguration(timeUs);
 }
 
 status_t LiveSession::getDuration(int64_t *durationUs) const {
@@ -1408,6 +1422,9 @@
         return INVALID_OPERATION;
     }
 
+    ALOGV("selectTrack: index=%zu, select=%d, mSubtitleGen=%d++",
+            index, select, mSubtitleGeneration);
+
     ++mSubtitleGeneration;
     status_t err = mPlaylist->selectTrack(index, select);
     if (err == OK) {
@@ -1428,6 +1445,9 @@
 
 void LiveSession::changeConfiguration(
         int64_t timeUs, ssize_t bandwidthIndex, bool pickTrack) {
+    ALOGV("changeConfiguration: timeUs=%lld us, bwIndex=%zd, pickTrack=%d",
+          (long long)timeUs, bandwidthIndex, pickTrack);
+
     cancelBandwidthSwitch();
 
     CHECK(!mReconfigurationInProgress);
@@ -1435,6 +1455,10 @@
     if (bandwidthIndex >= 0) {
         mOrigBandwidthIndex = mCurBandwidthIndex;
         mCurBandwidthIndex = bandwidthIndex;
+        if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+            ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
+                    mOrigBandwidthIndex, mCurBandwidthIndex);
+        }
     }
     CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size());
     const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex);
@@ -1459,14 +1483,9 @@
         }
 
         const AString &uri = mFetcherInfos.keyAt(i);
+        sp<PlaylistFetcher> &fetcher = mFetcherInfos.editValueAt(i).mFetcher;
 
-        bool discardFetcher = true;
-
-        if (timeUs < 0ll) {
-            // delay fetcher removal if not picking tracks
-            discardFetcher = pickTrack;
-        }
-
+        bool discardFetcher = true, delayRemoval = false;
         for (size_t j = 0; j < kMaxStreams; ++j) {
             StreamType type = indexToType(j);
             if ((streamMask & type) && uri == URIs[j]) {
@@ -1475,23 +1494,32 @@
                 discardFetcher = false;
             }
         }
+        // Delay fetcher removal if not picking tracks, AND old fetcher
+        // has stream mask that overlaps new variant. (Okay to discard
+        // old fetcher now, if completely no overlap.)
+        if (discardFetcher && timeUs < 0ll && !pickTrack
+                && (fetcher->getStreamTypeMask() & streamMask)) {
+            discardFetcher = false;
+            delayRemoval = true;
+        }
 
         if (discardFetcher) {
-            mFetcherInfos.valueAt(i).mFetcher->stopAsync();
+            ALOGV("discarding fetcher-%d", fetcher->getFetcherID());
+            fetcher->stopAsync();
         } else {
             float threshold = -1.0f; // always finish fetching by default
             if (timeUs >= 0ll) {
                 // seeking, no need to finish fetching
                 threshold = 0.0f;
-            } else if (!pickTrack) {
+            } else if (delayRemoval) {
                 // adapting, abort if remaining of current segment is over threshold
                 threshold = getAbortThreshold(
                         mOrigBandwidthIndex, mCurBandwidthIndex);
             }
 
-            ALOGV("Pausing with threshold %.3f", threshold);
-
-            mFetcherInfos.valueAt(i).mFetcher->pauseAsync(threshold);
+            ALOGV("pausing fetcher-%d, threshold=%.2f",
+                    fetcher->getFetcherID(), threshold);
+            fetcher->pauseAsync(threshold);
         }
     }
 
@@ -1525,6 +1553,8 @@
 }
 
 void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
+    ALOGV("onChangeConfiguration");
+
     if (!mReconfigurationInProgress) {
         int32_t pickTrack = 0;
         msg->findInt32("pickTrack", &pickTrack);
@@ -1535,6 +1565,8 @@
 }
 
 void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
+    ALOGV("onChangeConfiguration2");
+
     mContinuation.clear();
 
     // All fetchers are either suspended or have been removed now.
@@ -1546,6 +1578,7 @@
 
     if (timeUs >= 0) {
         mLastSeekTimeUs = timeUs;
+        mLastDequeuedTimeUs = timeUs;
 
         for (size_t i = 0; i < mPacketSources.size(); i++) {
             mPacketSources.editValueAt(i)->clear();
@@ -1598,8 +1631,10 @@
             ALOGV("stream %zu changed: oldURI %s, newURI %s", i,
                     mStreams[i].mUri.c_str(), URIs[i].c_str());
             sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i));
-            source->queueDiscontinuity(
-                    ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+            if (source->getLatestDequeuedMeta() != NULL) {
+                source->queueDiscontinuity(
+                        ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+            }
         }
         // Determine which decoders to shutdown on the player side,
         // a decoder has to be shutdown if its streamtype was active
@@ -1640,23 +1675,36 @@
     CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
     CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
 
+    mNewStreamMask = streamMask | resumeMask;
+
     int64_t timeUs;
     int32_t pickTrack;
     bool switching = false;
-    bool finishSwitching = false;
     CHECK(msg->findInt64("timeUs", &timeUs));
     CHECK(msg->findInt32("pickTrack", &pickTrack));
 
     if (timeUs < 0ll) {
         if (!pickTrack) {
-            switching = true;
-            finishSwitching = (streamMask == 0);
+            // mSwapMask contains streams that are in both old and new variant,
+            // (in mNewStreamMask & mStreamMask) but with different URIs
+            // (not in resumeMask).
+            // For example, old variant has video and audio in two separate
+            // URIs, and new variant has only audio with unchanged URI. mSwapMask
+            // should be 0 as there is nothing to swap. We only need to stop video,
+            // and resume audio.
+            mSwapMask =  mNewStreamMask & mStreamMask & ~resumeMask;
+            switching = (mSwapMask != 0);
         }
         mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs;
     } else {
         mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
     }
 
+    ALOGV("onChangeConfiguration3: timeUs=%lld, switching=%d, pickTrack=%d, "
+            "mStreamMask=0x%x, mNewStreamMask=0x%x, mSwapMask=0x%x",
+            (long long)timeUs, switching, pickTrack,
+            mStreamMask, mNewStreamMask, mSwapMask);
+
     for (size_t i = 0; i < kMaxStreams; ++i) {
         if (streamMask & indexToType(i)) {
             if (switching) {
@@ -1667,11 +1715,6 @@
         }
     }
 
-    mNewStreamMask = streamMask | resumeMask;
-    if (switching) {
-        mSwapMask = mStreamMask & ~resumeMask;
-    }
-
     // Of all existing fetchers:
     // * Resume fetchers that are still needed and assign them original packet sources.
     // * Mark otherwise unneeded fetchers for removal.
@@ -1679,6 +1722,9 @@
     for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
         const AString &uri = mFetcherInfos.keyAt(i);
         if (!resumeFetcher(uri, resumeMask, timeUs)) {
+            ALOGV("marking fetcher-%d to be removed",
+                    mFetcherInfos[i].mFetcher->getFetcherID());
+
             mFetcherInfos.editValueAt(i).mToBeRemoved = true;
         }
     }
@@ -1701,14 +1747,12 @@
         sp<PlaylistFetcher> fetcher = addFetcher(uri.c_str());
         CHECK(fetcher != NULL);
 
-        int64_t startTimeUs = -1;
-        int64_t segmentStartTimeUs = -1ll;
-        int32_t discontinuitySeq = -1;
+        HLSTime startTime;
         SeekMode seekMode = kSeekModeExactPosition;
         sp<AnotherPacketSource> sources[kMaxStreams];
 
-        if (i == kSubtitleIndex) {
-            segmentStartTimeUs = latestMediaSegmentStartTimeUs();
+        if (i == kSubtitleIndex || (!pickTrack && !switching)) {
+            startTime = latestMediaSegmentStartTime();
         }
 
         // TRICKY: looping from i as earlier streams are already removed from streamMask
@@ -1718,25 +1762,15 @@
                 sources[j] = mPacketSources.valueFor(indexToType(j));
 
                 if (timeUs >= 0) {
-                    startTimeUs = timeUs;
+                    startTime.mTimeUs = timeUs;
                 } else {
                     int32_t type;
                     sp<AMessage> meta;
-                    if (pickTrack) {
-                        // selecting
-
-                        // FIXME:
-                        // This should only apply to the track that's being picked, we
-                        // need a bitmask to indicate that.
-                        //
-                        // It's possible that selectTrack() gets called during a bandwidth
-                        // switch, and we needed to fetch a new variant. The new fetcher
-                        // should start from where old fetcher left off, not where decoder
-                        // is dequeueing at.
-
+                    if (!switching) {
+                        // selecting, or adapting but no swap required
                         meta = sources[j]->getLatestDequeuedMeta();
                     } else {
-                        // adapting
+                        // adapting and swap required
                         meta = sources[j]->getLatestEnqueuedMeta();
                         if (meta != NULL && mCurBandwidthIndex > mOrigBandwidthIndex) {
                             // switching up
@@ -1744,60 +1778,28 @@
                         }
                     }
 
-                    if (j != kSubtitleIndex
-                            && meta != NULL
+                    if (j != kSubtitleIndex && meta != NULL
                             && !meta->findInt32("discontinuity", &type)) {
-                        int64_t tmpUs;
-                        int64_t tmpSegmentUs;
-                        int32_t seq;
-
-                        CHECK(meta->findInt64("timeUs", &tmpUs));
-                        CHECK(meta->findInt64("segmentStartTimeUs", &tmpSegmentUs));
-                        CHECK(meta->findInt32("discontinuitySeq", &seq));
-                        // If we're switching and looking for next sample or segment, set the target
-                        // segment start time to tmpSegmentUs + tmpDurationUs / 2, which is
-                        // the middle point of the segment where the last sample was.
-                        // This is needed if segments of the two variants are not perfectly
-                        // aligned. (If the corresponding segment in new variant starts slightly
-                        // later than that in the old variant, we still want the switching to
-                        // start in the next one, not the current one)
-                        if (mStreams[j].mSeekMode == kSeekModeNextSample
-                                || mStreams[j].mSeekMode == kSeekModeNextSegment) {
-                            int64_t tmpDurationUs;
-                            CHECK(meta->findInt64("segmentDurationUs", &tmpDurationUs));
-                            tmpSegmentUs += tmpDurationUs / 2;
-                        }
-                        if (startTimeUs < 0 || seq > discontinuitySeq
-                            || (seq == discontinuitySeq
-                            && (tmpSegmentUs > segmentStartTimeUs
-                            || (tmpSegmentUs == segmentStartTimeUs
-                            && tmpUs > startTimeUs)))) {
-                            startTimeUs = tmpUs;
-                            segmentStartTimeUs = tmpSegmentUs;
-                            discontinuitySeq = seq;
+                        HLSTime tmpTime(meta);
+                        if (startTime < tmpTime) {
+                            startTime = tmpTime;
                         }
                     }
 
-                    if (pickTrack) {
-                        // selecting track, queue discontinuities before content
+                    if (!switching) {
+                        // selecting, or adapting but no swap required
                         sources[j]->clear();
                         if (j == kSubtitleIndex) {
                             break;
                         }
 
                         ALOGV("stream[%zu]: queue format change", j);
-
                         sources[j]->queueDiscontinuity(
                                 ATSParser::DISCONTINUITY_FORMAT_ONLY, NULL, true);
                     } else {
-                        // adapting, queue discontinuities after resume
+                        // switching, queue discontinuities after resume
                         sources[j] = mPacketSources2.valueFor(indexToType(j));
                         sources[j]->clear();
-                        uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
-                        if (extraStreams & indexToType(j)) {
-                            sources[j]->queueDiscontinuity(
-                                ATSParser::DISCONTINUITY_FORMAT_ONLY, NULL, true);
-                        }
                         // the new fetcher might be providing streams that used to be
                         // provided by two different fetchers,  if one of the fetcher
                         // paused in the middle while the other somehow paused in next
@@ -1812,47 +1814,55 @@
             }
         }
 
+        ALOGV("[fetcher-%d] startAsync: startTimeUs %lld mLastSeekTimeUs %lld "
+                "segmentStartTimeUs %lld seekMode %d",
+                fetcher->getFetcherID(),
+                (long long)startTime.mTimeUs,
+                (long long)mLastSeekTimeUs,
+                (long long)startTime.getSegmentTimeUs(true /* midpoint */),
+                seekMode);
+
+        // Set the target segment start time to the middle point of the
+        // segment where the last sample was.
+        // This gives a better guess if segments of the two variants are not
+        // perfectly aligned. (If the corresponding segment in new variant
+        // starts slightly later than that in the old variant, we still want
+        // to pick that segment, not the one before)
         fetcher->startAsync(
                 sources[kAudioIndex],
                 sources[kVideoIndex],
                 sources[kSubtitleIndex],
-                startTimeUs < 0 ? mLastSeekTimeUs : startTimeUs,
-                segmentStartTimeUs,
-                discontinuitySeq,
+                startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs,
+                startTime.getSegmentTimeUs(true /* midpoint */),
+                startTime.mSeq,
                 seekMode);
     }
 
     // All fetchers have now been started, the configuration change
     // has completed.
 
-    ALOGV("XXX configuration change completed.");
     mReconfigurationInProgress = false;
     if (switching) {
         mSwitchInProgress = true;
-
-        if (finishSwitching) {
-            // Switch is finished now, no new fetchers are created.
-            // This path is hit when old variant had video and audio from
-            // two separate fetchers, while new variant has audio only,
-            // which reuses the previous audio fetcher.
-            for (size_t i = 0; i < kMaxStreams; ++i) {
-                if (mSwapMask & indexToType(i)) {
-                    tryToFinishBandwidthSwitch(mStreams[i].mUri);
-                }
-            }
-        }
     } else {
         mStreamMask = mNewStreamMask;
-        mOrigBandwidthIndex = mCurBandwidthIndex;
+        if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+            ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd",
+                    mOrigBandwidthIndex, mCurBandwidthIndex);
+            mOrigBandwidthIndex = mCurBandwidthIndex;
+        }
     }
 
+    ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x",
+            mSwitchInProgress, mStreamMask);
+
     if (mDisconnectReplyID != NULL) {
         finishDisconnect();
     }
 }
 
 void LiveSession::swapPacketSource(StreamType stream) {
-    ALOGV("swapPacketSource: stream = %d", stream);
+    ALOGV("[%s] swapPacketSource", getNameForStream(stream));
 
     // transfer packets from source2 to source
     sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream);
@@ -1900,7 +1910,7 @@
 
     mFetcherInfos.editValueAt(index).mFetcher->stopAsync(false /* clear */);
 
-    ALOGV("tryToFinishBandwidthSwitch: mSwapMask=%x", mSwapMask);
+    ALOGV("tryToFinishBandwidthSwitch: mSwapMask=0x%x", mSwapMask);
     if (mSwapMask != 0) {
         return;
     }
@@ -1940,7 +1950,6 @@
     mSwitchInProgress = false;
     mOrigBandwidthIndex = mCurBandwidthIndex;
 
-
     restartPollBuffering();
 }
 
@@ -1968,11 +1977,19 @@
 
     bool underflow, ready, down, up;
     if (checkBuffering(underflow, ready, down, up)) {
-        if (mInPreparationPhase && ready) {
-            postPrepared(OK);
+        if (mInPreparationPhase) {
+            // Allow down switch even if we're still preparing.
+            //
+            // Some streams have a high bandwidth index as default,
+            // when bandwidth is low, it takes a long time to buffer
+            // to ready mark, then it immediately pauses after start
+            // as we have to do a down switch. It's better experience
+            // to restart from a lower index, if we detect low bw.
+            if (!switchBandwidthIfNeeded(false /* up */, down) && ready) {
+                postPrepared(OK);
+            }
         }
 
-        // don't switch before we report prepared
         if (!mInPreparationPhase) {
             if (ready) {
                 stopBufferingIfNecessary();
@@ -1980,8 +1997,7 @@
                 startBufferingIfNecessary();
             }
             switchBandwidthIfNeeded(up, down);
-       }
-
+        }
     }
 
     schedulePollBuffering();
@@ -2026,7 +2042,7 @@
     }
 
     ALOGI("#### Canceled Bandwidth Switch: %zd => %zd",
-            mCurBandwidthIndex, mOrigBandwidthIndex);
+            mOrigBandwidthIndex, mCurBandwidthIndex);
 
     mSwitchGeneration++;
     mSwitchInProgress = false;
@@ -2065,13 +2081,16 @@
 
         int64_t bufferedDurationUs =
                 mPacketSources[i]->getEstimatedDurationUs();
-        ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
+        ALOGV("[%s] buffered %lld us",
+                getNameForStream(mPacketSources.keyAt(i)),
+                (long long)bufferedDurationUs);
         if (durationUs >= 0) {
             int32_t percent;
             if (mPacketSources[i]->isFinished(0 /* duration */)) {
                 percent = 100;
             } else {
-                percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
+                percent = (int32_t)(100.0 *
+                        (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
             }
             if (minBufferPercent < 0 || percent < minBufferPercent) {
                 minBufferPercent = percent;
@@ -2090,7 +2109,8 @@
             }
             if (bufferedDurationUs > mUpSwitchMark) {
                 ++upCount;
-            } else if (bufferedDurationUs < mDownSwitchMark) {
+            }
+            if (bufferedDurationUs < mDownSwitchMark) {
                 ++downCount;
             }
         }
@@ -2153,10 +2173,14 @@
     notify->post();
 }
 
-void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
+/*
+ * returns true if a bandwidth switch is actually needed (and started),
+ * returns false otherwise
+ */
+bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
     // no need to check bandwidth if we only have 1 bandwidth settings
     if (mSwitchInProgress || mBandwidthItems.size() < 2) {
-        return;
+        return false;
     }
 
     int32_t bandwidthBps;
@@ -2165,26 +2189,35 @@
         mLastBandwidthBps = bandwidthBps;
     } else {
         ALOGV("no bandwidth estimate.");
-        return;
+        return false;
     }
 
     int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
-    bool bandwidthLow = bandwidthBps < (int32_t)curBandwidth * 8 / 10;
-    bool bandwidthHigh = bandwidthBps > (int32_t)curBandwidth * 12 / 10;
+    // canSwithDown and canSwitchUp can't both be true.
+    // we only want to switch up when measured bw is 120% higher than current variant,
+    // and we only want to switch down when measured bw is below current variant.
+    bool canSwithDown = bufferLow
+            && (bandwidthBps < (int32_t)curBandwidth);
+    bool canSwitchUp = bufferHigh
+            && (bandwidthBps > (int32_t)curBandwidth * 12 / 10);
 
-    if ((bufferHigh && bandwidthHigh) || (bufferLow && bandwidthLow)) {
+    if (canSwithDown || canSwitchUp) {
         ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
 
-        if (bandwidthIndex == mCurBandwidthIndex
-                || (bufferHigh && bandwidthIndex < mCurBandwidthIndex)
-                || (bufferLow && bandwidthIndex > mCurBandwidthIndex)) {
-            return;
+        // it's possible that we're checking for canSwitchUp case, but the returned
+        // bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70%
+        // of measured bw. In that case we don't want to do anything, since we have
+        // both enough buffer and enough bw.
+        if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+         || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) {
+            // if not yet prepared, just restart again with new bw index.
+            // this is faster and playback experience is cleaner.
+            changeConfiguration(
+                    mInPreparationPhase ? 0 : -1ll, bandwidthIndex);
+            return true;
         }
-
-        ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
-                mCurBandwidthIndex, bandwidthIndex);
-        changeConfiguration(-1, bandwidthIndex, false);
     }
+    return false;
 }
 
 void LiveSession::postError(status_t err) {
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 3d62cab..9117bb1 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -34,6 +34,7 @@
 struct LiveDataSource;
 struct M3UParser;
 struct PlaylistFetcher;
+struct HLSTime;
 
 struct LiveSession : public AHandler {
     enum Flags {
@@ -90,6 +91,7 @@
     bool hasDynamicDuration() const;
 
     static const char *getKeyForStream(StreamType type);
+    static const char *getNameForStream(StreamType type);
 
     enum {
         kWhatStreamsChanged,
@@ -125,6 +127,7 @@
     static const int64_t kUpSwitchMarkUs;
     static const int64_t kDownSwitchMarkUs;
     static const int64_t kUpSwitchMarginUs;
+    static const int64_t kResumeThresholdUs;
 
     // Buffer Prepare/Ready/Underflow Marks
     static const int64_t kReadyMarkUs;
@@ -234,7 +237,7 @@
     sp<PlaylistFetcher> addFetcher(const char *uri);
 
     void onConnect(const sp<AMessage> &msg);
-    status_t onSeek(const sp<AMessage> &msg);
+    void onSeek(const sp<AMessage> &msg);
     void onFinishDisconnect2();
 
     // If given a non-zero block_size (default 0), it is used to cap the number of
@@ -271,7 +274,7 @@
             ssize_t currentBWIndex, ssize_t targetBWIndex) const;
     void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
     size_t getBandwidthIndex(int32_t bandwidthBps);
-    int64_t latestMediaSegmentStartTimeUs();
+    HLSTime latestMediaSegmentStartTime() const;
 
     static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
     static StreamType indexToType(int idx);
@@ -289,7 +292,7 @@
     bool checkSwitchProgress(
             sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil);
 
-    void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
+    bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
 
     void schedulePollBuffering();
     void cancelPollBuffering();
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 3c5d7cf..7bb7f2c 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -395,7 +395,9 @@
 
 bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const {
     if (!mIsVariantPlaylist) {
-        *uri = mBaseURI;
+        if (uri != NULL) {
+            *uri = mBaseURI;
+        }
 
         // Assume media without any more specific attribute contains
         // audio and video, but no subtitles.
@@ -408,7 +410,9 @@
 
     AString groupID;
     if (!meta->findString(key, &groupID)) {
-        *uri = mItems.itemAt(index).mURI;
+        if (uri != NULL) {
+            *uri = mItems.itemAt(index).mURI;
+        }
 
         AString codecs;
         if (!meta->findString("codecs", &codecs)) {
@@ -434,18 +438,26 @@
         }
     }
 
-    sp<MediaGroup> group = mMediaGroups.valueFor(groupID);
-    if (!group->getActiveURI(uri)) {
-        return false;
-    }
+    // if uri == NULL, we're only checking if the type is present,
+    // don't care about the active URI (or if there is an active one)
+    if (uri != NULL) {
+        sp<MediaGroup> group = mMediaGroups.valueFor(groupID);
+        if (!group->getActiveURI(uri)) {
+            return false;
+        }
 
-    if ((*uri).empty()) {
-        *uri = mItems.itemAt(index).mURI;
+        if ((*uri).empty()) {
+            *uri = mItems.itemAt(index).mURI;
+        }
     }
 
     return true;
 }
 
+bool M3UParser::hasType(size_t index, const char *key) const {
+    return getTypeURI(index, key, NULL /* uri */);
+}
+
 static bool MakeURL(const char *baseURL, const char *url, AString *out) {
     out->clear();
 
@@ -633,7 +645,8 @@
                         || !itemMeta->findInt64("durationUs", &durationUs)) {
                     return ERROR_MALFORMED;
                 }
-                itemMeta->setInt32("discontinuity-sequence", mDiscontinuitySeq + mDiscontinuityCount);
+                itemMeta->setInt32("discontinuity-sequence",
+                        mDiscontinuitySeq + mDiscontinuityCount);
             }
 
             mItems.push();
@@ -650,6 +663,14 @@
         ++lineNo;
     }
 
+    // error checking of all fields that's required to appear once
+    // (currently only checking "target-duration")
+    int32_t targetDurationSecs;
+    if (!mIsVariantPlaylist && (mMeta == NULL || !mMeta->findInt32(
+            "target-duration", &targetDurationSecs))) {
+        return ERROR_MALFORMED;
+    }
+
     return OK;
 }
 
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index d475683..fef361f 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -50,6 +50,7 @@
     ssize_t getSelectedTrack(media_track_type /* type */) const;
 
     bool getTypeURI(size_t index, const char *key, AString *uri) const;
+    bool hasType(size_t index, const char *key) const;
 
 protected:
     virtual ~M3UParser();
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 3ace396..ce79cc2 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -45,15 +45,17 @@
 #include <openssl/aes.h>
 #include <openssl/md5.h>
 
+#define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
+#define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
+         LiveSession::getNameForStream(stream), ##__VA_ARGS__)
+
 namespace android {
 
 // static
 const int64_t PlaylistFetcher::kMinBufferedDurationUs = 30000000ll;
 const int64_t PlaylistFetcher::kMaxMonitorDelayUs = 3000000ll;
-const int64_t PlaylistFetcher::kFetcherResumeThreshold = 100000ll;
 // LCM of 188 (size of a TS packet) & 1k works well
 const int32_t PlaylistFetcher::kDownloadBlockSize = 47 * 1024;
-const int32_t PlaylistFetcher::kNumSkipFrames = 5;
 
 struct PlaylistFetcher::DownloadState : public RefBase {
     DownloadState();
@@ -145,10 +147,12 @@
         const sp<AMessage> &notify,
         const sp<LiveSession> &session,
         const char *uri,
+        int32_t id,
         int32_t subtitleGeneration)
     : mNotify(notify),
       mSession(session),
       mURI(uri),
+      mFetcherID(id),
       mStreamTypeMask(0),
       mStartTimeUs(-1ll),
       mSegmentStartTimeUs(-1ll),
@@ -178,6 +182,10 @@
 PlaylistFetcher::~PlaylistFetcher() {
 }
 
+int32_t PlaylistFetcher::getFetcherID() const {
+    return mFetcherID;
+}
+
 int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const {
     CHECK(mPlaylist != NULL);
 
@@ -438,7 +446,7 @@
         maxDelayUs = minDelayUs;
     }
     if (delayUs > maxDelayUs) {
-        ALOGV("Need to refresh playlist in %" PRId64 , maxDelayUs);
+        FLOGV("Need to refresh playlist in %lld", (long long)maxDelayUs);
         delayUs = maxDelayUs;
     }
     sp<AMessage> msg = new AMessage(kWhatMonitorQueue, this);
@@ -509,6 +517,8 @@
 }
 
 void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> &params) {
+    FLOGV("resumeUntilAsync: params=%s", params->debugString().c_str());
+
     AMessage* msg = new AMessage(kWhatResumeUntil, this);
     msg->setMessage("params", params);
     msg->post();
@@ -676,6 +686,9 @@
         }
     }
 
+    // close off the connection after use
+    mHTTPDataSource->disconnect();
+
     mDownloadState->resetState();
     mPacketSources.clear();
     mStreamTypeMask = 0;
@@ -690,40 +703,6 @@
     sp<AMessage> params;
     CHECK(msg->findMessage("params", &params));
 
-    size_t stopCount = 0;
-    for (size_t i = 0; i < mPacketSources.size(); i++) {
-        sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
-
-        LiveSession::StreamType streamType = mPacketSources.keyAt(i);
-
-        if (streamType == LiveSession::STREAMTYPE_SUBTITLES) {
-            // the subtitle track can always be stopped
-            ++stopCount;
-            continue;
-        }
-
-        const char *stopKey = LiveSession::getKeyForStream(streamType);
-
-        // check if this stream has too little data left to be resumed
-        int32_t discontinuitySeq;
-        int64_t latestTimeUs = 0, stopTimeUs = 0;
-        sp<AMessage> latestMeta = packetSource->getLatestEnqueuedMeta();
-        if (latestMeta != NULL
-                && latestMeta->findInt32("discontinuitySeq", &discontinuitySeq)
-                && discontinuitySeq == mDiscontinuitySeq
-                && latestMeta->findInt64("timeUs", &latestTimeUs)
-                && params->findInt64(stopKey, &stopTimeUs)
-                && stopTimeUs - latestTimeUs < kFetcherResumeThreshold) {
-            ++stopCount;
-        }
-    }
-
-    // Don't resume if all streams are within a resume threshold
-    if (stopCount == mPacketSources.size()) {
-        notifyStopReached();
-        return OK;
-    }
-
     mStopParams = params;
     onDownloadNext();
 
@@ -796,8 +775,9 @@
 
             int64_t bufferedStreamDurationUs =
                 mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult);
-            ALOGV("buffered %" PRId64 " for stream %d",
-                    bufferedStreamDurationUs, mPacketSources.keyAt(i));
+
+            FSLOGV(mPacketSources.keyAt(i), "buffered %lld", (long long)bufferedStreamDurationUs);
+
             if (bufferedDurationUs == -1ll
                  || bufferedStreamDurationUs < bufferedDurationUs) {
                 bufferedDurationUs = bufferedStreamDurationUs;
@@ -809,8 +789,9 @@
     }
 
     if (finalResult == OK && bufferedDurationUs < kMinBufferedDurationUs) {
-        ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "",
-                bufferedDurationUs, kMinBufferedDurationUs);
+        FLOGV("monitoring, buffered=%lld < %lld",
+                (long long)bufferedDurationUs, (long long)kMinBufferedDurationUs);
+
         // delay the next download slightly; hopefully this gives other concurrent fetchers
         // a better chance to run.
         // onDownloadNext();
@@ -825,8 +806,12 @@
         if (delayUs > targetDurationUs / 2) {
             delayUs = targetDurationUs / 2;
         }
-        ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
-                delayUs, bufferedDurationUs, kMinBufferedDurationUs);
+
+        FLOGV("pausing for %lld, buffered=%lld > %lld",
+                (long long)delayUs,
+                (long long)bufferedDurationUs,
+                (long long)kMinBufferedDurationUs);
+
         postMonitorQueue(delayUs);
     }
 }
@@ -924,6 +909,12 @@
         }
     }
     lastEnqueueUs -= mSegmentFirstPTS;
+
+    FLOGV("%spausing now, thresholdUs %lld, remaining %lld",
+            targetDurationUs - lastEnqueueUs > thresholdUs ? "" : "not ",
+            (long long)thresholdUs,
+            (long long)(targetDurationUs - lastEnqueueUs));
+
     if (targetDurationUs - lastEnqueueUs > thresholdUs) {
         return true;
     }
@@ -973,8 +964,8 @@
                 mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber);
             }
             mStartTimeUsRelative = true;
-            ALOGV("Initial sequence number for time %" PRId64 " is %d from (%d .. %d)",
-                    mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
+            FLOGV("Initial sequence number for time %lld is %d from (%d .. %d)",
+                    (long long)mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
                     lastSeqNumberInPlaylist);
         } else {
             // When adapting or track switching, mSegmentStartTimeUs (relative
@@ -982,7 +973,8 @@
             // timestamps coming from the media container) is used to determine the position
             // inside a segments.
             mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
-            if (mSeekMode == LiveSession::kSeekModeNextSegment) {
+            if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES
+                    && mSeekMode != LiveSession::kSeekModeNextSample) {
                 // avoid double fetch/decode
                 mSeqNumber += 1;
             }
@@ -998,7 +990,7 @@
             if (mSeqNumber > lastSeqNumberInPlaylist) {
                 mSeqNumber = lastSeqNumberInPlaylist;
             }
-            ALOGV("Initial sequence number for live event %d from (%d .. %d)",
+            FLOGV("Initial sequence number is %d from (%d .. %d)",
                     mSeqNumber, firstSeqNumberInPlaylist,
                     lastSeqNumberInPlaylist);
         }
@@ -1027,10 +1019,10 @@
                 if (delayUs > kMaxMonitorDelayUs) {
                     delayUs = kMaxMonitorDelayUs;
                 }
-                ALOGV("sequence number high: %d from (%d .. %d), "
-                      "monitor in %" PRId64 " (retry=%d)",
+                FLOGV("sequence number high: %d from (%d .. %d), "
+                      "monitor in %lld (retry=%d)",
                         mSeqNumber, firstSeqNumberInPlaylist,
-                        lastSeqNumberInPlaylist, delayUs, mNumRetries);
+                        lastSeqNumberInPlaylist, (long long)delayUs, mNumRetries);
                 postMonitorQueue(delayUs);
                 return false;
             }
@@ -1099,9 +1091,9 @@
         // Seek jumped to a new discontinuity sequence. We need to signal
         // a format change to decoder. Decoder needs to shutdown and be
         // created again if seamless format change is unsupported.
-        ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
+        FLOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
                 "mDiscontinuitySeq %d, mStartTimeUs %lld",
-            mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
+                mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
         discontinuity = true;
     }
     mLastDiscontinuitySeq = -1;
@@ -1166,7 +1158,7 @@
         }
     }
 
-    ALOGV("fetching segment %d from (%d .. %d)",
+    FLOGV("fetching segment %d from (%d .. %d)",
             mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
     return true;
 }
@@ -1189,7 +1181,7 @@
                 firstSeqNumberInPlaylist,
                 lastSeqNumberInPlaylist);
         connectHTTP = false;
-        ALOGV("resuming: '%s'", uri.c_str());
+        FLOGV("resuming: '%s'", uri.c_str());
     } else {
         if (!initDownloadState(
                 uri,
@@ -1198,7 +1190,7 @@
                 lastSeqNumberInPlaylist)) {
             return;
         }
-        ALOGV("fetching: '%s'", uri.c_str());
+        FLOGV("fetching: '%s'", uri.c_str());
     }
 
     int64_t range_offset, range_length;
@@ -1219,13 +1211,20 @@
                 uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize,
                 &source, NULL, connectHTTP);
 
-        // add sample for bandwidth estimation (excluding subtitles)
-        if (bytesRead > 0
+        // add sample for bandwidth estimation, excluding samples from subtitles (as
+        // its too small), or during startup/resumeUntil (when we could have more than
+        // one connection open which affects bandwidth)
+        if (!mStartup && mStopParams == NULL && bytesRead > 0
                 && (mStreamTypeMask
                         & (LiveSession::STREAMTYPE_AUDIO
                         | LiveSession::STREAMTYPE_VIDEO))) {
             int64_t delayUs = ALooper::GetNowUs() - startUs;
             mSession->addBandwidthMeasurement(bytesRead, delayUs);
+
+            if (delayUs > 2000000ll) {
+                FLOGV("bytesRead %zd took %.2f seconds - abnormal bandwidth dip",
+                        bytesRead, (double)delayUs / 1.0e6);
+            }
         }
 
         connectHTTP = false;
@@ -1381,7 +1380,7 @@
 
     // if adapting, pause after found the next starting point
     if (mSeekMode != LiveSession::kSeekModeExactPosition && startUp != mStartup) {
-        CHECK(mStartTimeUsNotify != NULL); 
+        CHECK(mStartTimeUsNotify != NULL);
         mStartTimeUsNotify->post();
         mStartTimeUsNotify.clear();
         shouldPause = true;
@@ -1424,28 +1423,22 @@
 
 int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const {
     int32_t firstSeqNumberInPlaylist;
-    if (mPlaylist->meta() == NULL
-            || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
+    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
+                "media-sequence", &firstSeqNumberInPlaylist)) {
         firstSeqNumberInPlaylist = 0;
     }
 
-    size_t curDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
-    if (discontinuitySeq < curDiscontinuitySeq) {
-        return firstSeqNumberInPlaylist <= 0 ? 0 : (firstSeqNumberInPlaylist - 1);
-    }
-
     size_t index = 0;
     while (index < mPlaylist->size()) {
         sp<AMessage> itemMeta;
         CHECK(mPlaylist->itemAt( index, NULL /* uri */, &itemMeta));
-
-        int64_t discontinuity;
-        if (itemMeta->findInt64("discontinuity", &discontinuity)) {
-            curDiscontinuitySeq++;
-        }
-
+        size_t curDiscontinuitySeq;
+        CHECK(itemMeta->findInt32("discontinuity-sequence", (int32_t *)&curDiscontinuitySeq));
+        int32_t seqNumber = firstSeqNumberInPlaylist + index;
         if (curDiscontinuitySeq == discontinuitySeq) {
-            return firstSeqNumberInPlaylist + index;
+            return seqNumber;
+        } else if (curDiscontinuitySeq > discontinuitySeq) {
+            return seqNumber <= 0 ? 0 : seqNumber - 1;
         }
 
         ++index;
@@ -1521,6 +1514,12 @@
         // ATSParser from skewing the timestamps of access units.
         extra->setInt64(IStreamListener::kKeyMediaTimeUs, 0);
 
+        // When adapting, signal a recent media time to the parser,
+        // so that PTS wrap around is handled for the new variant.
+        if (mStartTimeUs >= 0 && !mStartTimeUsRelative) {
+            extra->setInt64(IStreamListener::kKeyRecentMediaTimeUs, mStartTimeUs);
+        }
+
         mTSParser->signalDiscontinuity(
                 ATSParser::DISCONTINUITY_TIME, extra);
 
@@ -1575,10 +1574,9 @@
             int64_t timeUs;
             CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
 
-            bool seeking = mSeekMode == LiveSession::kSeekModeExactPosition;
             if (mSegmentFirstPTS < 0ll) {
                 mSegmentFirstPTS = timeUs;
-                if (!seeking) {
+                if (!mStartTimeUsRelative) {
                     int32_t firstSeqNumberInPlaylist;
                     if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
                                 "media-sequence", &firstSeqNumberInPlaylist)) {
@@ -1604,7 +1602,7 @@
                     //   timeUs to be too far ahead of mStartTimeUs because we want the old fetcher to
                     //   stop as early as possible. The definition of being "too far ahead" is
                     //   arbitrary; here we use targetDurationUs as threshold.
-                    int64_t targetDiffUs =(mSeekMode == LiveSession::kSeekModeNextSample 
+                    int64_t targetDiffUs = (mSeekMode == LiveSession::kSeekModeNextSample
                             ? 0 : targetDurationUs);
                     if (mStartup && mSegmentStartTimeUs >= 0
                             && mSeqNumber > firstSeqNumberInPlaylist
@@ -1615,6 +1613,16 @@
                         // (newSeqNumber), start at least 1 segment prior.
                         int32_t newSeqNumber = getSeqNumberWithAnchorTime(
                                 timeUs, targetDiffUs);
+
+                        FLOGV("guessed wrong seq number: timeUs=%lld, mStartTimeUs=%lld, "
+                                "targetDurationUs=%lld, mSeqNumber=%d, newSeq=%d, firstSeq=%d",
+                                (long long)timeUs,
+                                (long long)mStartTimeUs,
+                                (long long)targetDurationUs,
+                                mSeqNumber,
+                                newSeqNumber,
+                                firstSeqNumberInPlaylist);
+
                         if (newSeqNumber >= mSeqNumber) {
                             --mSeqNumber;
                         } else {
@@ -1633,26 +1641,34 @@
                     mFirstTimeUs = timeUs;
                     mFirstPTSValid = true;
                 }
+                bool startTimeReached = true;
                 if (mStartTimeUsRelative) {
+                    FLOGV("startTimeUsRelative, timeUs (%lld) - %lld = %lld",
+                            (long long)timeUs,
+                            (long long)mFirstTimeUs,
+                            (long long)(timeUs - mFirstTimeUs));
                     timeUs -= mFirstTimeUs;
                     if (timeUs < 0) {
+                        FLOGV("clamp negative timeUs to 0");
                         timeUs = 0;
                     }
+                    startTimeReached = (timeUs >= mStartTimeUs);
                 }
 
-                bool startTimeReached =
-                        seeking ? (timeUs >= mStartTimeUs) : true;
-
                 if (!startTimeReached || (isAvc && !mIDRFound)) {
                     // buffer up to the closest preceding IDR frame in the next segement,
                     // or the closest succeeding IDR frame after the exact position
+                    FSLOGV(stream, "timeUs=%lld, mStartTimeUs=%lld, mIDRFound=%d",
+                            (long long)timeUs, (long long)mStartTimeUs, mIDRFound);
                     if (isAvc) {
-                        if (IsIDR(accessUnit) && (seeking || startTimeReached)) {
+                        if (IsIDR(accessUnit)) {
                             mVideoBuffer->clear();
+                            FSLOGV(stream, "found IDR, clear mVideoBuffer");
                             mIDRFound = true;
                         }
-                        if (mIDRFound && seeking && !startTimeReached) {
+                        if (mIDRFound && mStartTimeUsRelative && !startTimeReached) {
                             mVideoBuffer->queueAccessUnit(accessUnit);
+                            FSLOGV(stream, "saving AVC video AccessUnit");
                         }
                     }
                     if (!startTimeReached || (isAvc && !mIDRFound)) {
@@ -1662,27 +1678,22 @@
             }
 
             if (mStartTimeUsNotify != NULL) {
-                int32_t seq;
-                if (!mStartTimeUsNotify->findInt32("discontinuitySeq", &seq)) {
-                    mStartTimeUsNotify->setInt32("discontinuitySeq", mDiscontinuitySeq);
-                }
-                int64_t startTimeUs;
-                if (!mStartTimeUsNotify->findInt64(key, &startTimeUs)) {
-                    mStartTimeUsNotify->setInt64(key, timeUs);
-
-                    uint32_t streamMask = 0;
-                    mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
+                uint32_t streamMask = 0;
+                mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
+                if (!(streamMask & mPacketSources.keyAt(i))) {
                     streamMask |= mPacketSources.keyAt(i);
                     mStartTimeUsNotify->setInt32("streamMask", streamMask);
+                    FSLOGV(stream, "found start point, timeUs=%lld, streamMask becomes %x",
+                            (long long)timeUs, streamMask);
 
                     if (streamMask == mStreamTypeMask) {
+                        FLOGV("found start point for all streams");
                         mStartup = false;
                     }
                 }
             }
 
             if (mStopParams != NULL) {
-                // Queue discontinuity in original stream.
                 int32_t discontinuitySeq;
                 int64_t stopTimeUs;
                 if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
@@ -1690,13 +1701,13 @@
                         || !mStopParams->findInt64(key, &stopTimeUs)
                         || (discontinuitySeq == mDiscontinuitySeq
                                 && timeUs >= stopTimeUs)) {
+                    FSLOGV(stream, "reached stop point, timeUs=%lld", (long long)timeUs);
                     mStreamTypeMask &= ~stream;
                     mPacketSources.removeItemsAt(i);
                     break;
                 }
             }
 
-            // Note that we do NOT dequeue any discontinuities except for format change.
             if (stream == LiveSession::STREAMTYPE_VIDEO) {
                 const bool discard = true;
                 status_t status;
@@ -1705,11 +1716,16 @@
                     mVideoBuffer->dequeueAccessUnit(&videoBuffer);
                     setAccessUnitProperties(videoBuffer, source, discard);
                     packetSource->queueAccessUnit(videoBuffer);
+                    int64_t bufferTimeUs;
+                    CHECK(videoBuffer->meta()->findInt64("timeUs", &bufferTimeUs));
+                    FSLOGV(stream, "queueAccessUnit (saved), timeUs=%lld",
+                            (long long)bufferTimeUs);
                 }
             }
 
             setAccessUnitProperties(accessUnit, source);
             packetSource->queueAccessUnit(accessUnit);
+            FSLOGV(stream, "queueAccessUnit, timeUs=%lld", (long long)timeUs);
         }
 
         if (err != OK) {
@@ -1727,7 +1743,7 @@
 
     if (!mStreamTypeMask) {
         // Signal gap is filled between original and new stream.
-        ALOGV("ERROR OUT OF RANGE");
+        FLOGV("reached stop point for all streams");
         return ERROR_OUT_OF_RANGE;
     }
 
@@ -1951,15 +1967,12 @@
                     return -EAGAIN;
                 }
 
-                mStartTimeUsNotify->setInt64("timeUsAudio", timeUs);
-                mStartTimeUsNotify->setInt32("discontinuitySeq", mDiscontinuitySeq);
                 mStartTimeUsNotify->setInt32("streamMask", LiveSession::STREAMTYPE_AUDIO);
                 mStartup = false;
             }
         }
 
         if (mStopParams != NULL) {
-            // Queue discontinuity in original stream.
             int32_t discontinuitySeq;
             int64_t stopTimeUs;
             if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index dab56df..f64d160 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -55,8 +55,11 @@
             const sp<AMessage> &notify,
             const sp<LiveSession> &session,
             const char *uri,
+            int32_t id,
             int32_t subtitleGeneration);
 
+    int32_t getFetcherID() const;
+
     sp<DataSource> getDataSource();
 
     void startAsync(
@@ -113,6 +116,8 @@
     sp<LiveSession> mSession;
     AString mURI;
 
+    int32_t mFetcherID;
+
     uint32_t mStreamTypeMask;
     int64_t mStartTimeUs;
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 01c3dd6..0a868bc 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -48,7 +48,8 @@
 static const size_t kTSPacketSize = 188;
 
 struct ATSParser::Program : public RefBase {
-    Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID);
+    Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID,
+            int64_t lastRecoveredPTS);
 
     bool parsePSISection(
             unsigned pid, ABitReader *br, status_t *err);
@@ -190,13 +191,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 ATSParser::Program::Program(
-        ATSParser *parser, unsigned programNumber, unsigned programMapPID)
+        ATSParser *parser, unsigned programNumber, unsigned programMapPID,
+        int64_t lastRecoveredPTS)
     : mParser(parser),
       mProgramNumber(programNumber),
       mProgramMapPID(programMapPID),
       mFirstPTSValid(false),
       mFirstPTS(0),
-      mLastRecoveredPTS(-1ll) {
+      mLastRecoveredPTS(lastRecoveredPTS) {
     ALOGV("new program number %u", programNumber);
 }
 
@@ -1041,6 +1043,7 @@
       mAbsoluteTimeAnchorUs(-1ll),
       mTimeOffsetValid(false),
       mTimeOffsetUs(0ll),
+      mLastRecoveredPTS(-1ll),
       mNumTSPacketsParsed(0),
       mNumPCRs(0) {
     mPSISections.add(0 /* PID */, new PSISection);
@@ -1059,11 +1062,21 @@
 void ATSParser::signalDiscontinuity(
         DiscontinuityType type, const sp<AMessage> &extra) {
     int64_t mediaTimeUs;
-    if ((type & DISCONTINUITY_TIME)
-            && extra != NULL
-            && extra->findInt64(
-                IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
-        mAbsoluteTimeAnchorUs = mediaTimeUs;
+    if ((type & DISCONTINUITY_TIME) && extra != NULL) {
+        if (extra->findInt64(IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
+            mAbsoluteTimeAnchorUs = mediaTimeUs;
+        }
+        if ((mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)
+                && extra->findInt64(
+                    IStreamListener::kKeyRecentMediaTimeUs, &mediaTimeUs)) {
+            if (mAbsoluteTimeAnchorUs >= 0ll) {
+                mediaTimeUs -= mAbsoluteTimeAnchorUs;
+            }
+            if (mTimeOffsetValid) {
+                mediaTimeUs -= mTimeOffsetUs;
+            }
+            mLastRecoveredPTS = (mediaTimeUs * 9) / 100;
+        }
     } else if (type == DISCONTINUITY_ABSOLUTE_TIME) {
         int64_t timeUs;
         CHECK(extra->findInt64("timeUs", &timeUs));
@@ -1147,7 +1160,7 @@
 
             if (!found) {
                 mPrograms.push(
-                        new Program(this, program_number, programMapPID));
+                        new Program(this, program_number, programMapPID, mLastRecoveredPTS));
             }
 
             if (mPSISections.indexOfKey(programMapPID) < 0) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 5c50747..a1405bd 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -118,6 +118,7 @@
 
     bool mTimeOffsetValid;
     int64_t mTimeOffsetUs;
+    int64_t mLastRecoveredPTS;
 
     size_t mNumTSPacketsParsed;
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index c2f1527..c7912c0 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -29,6 +29,7 @@
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
 #include <utils/Vector.h>
 
 #include <inttypes.h>
@@ -354,10 +355,15 @@
     int64_t time2 = -1;
     int64_t durationUs = 0;
 
-    List<sp<ABuffer> >::iterator it = mBuffers.begin();
-    while (it != mBuffers.end()) {
+    List<sp<ABuffer> >::iterator it;
+    for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
         const sp<ABuffer> &buffer = *it;
 
+        int32_t discard;
+        if (buffer->meta()->findInt32("discard", &discard) && discard) {
+            continue;
+        }
+
         int64_t timeUs;
         if (buffer->meta()->findInt64("timeUs", &timeUs)) {
             if (time1 < 0 || timeUs < time1) {
@@ -372,8 +378,6 @@
             durationUs += time2 - time1;
             time1 = time2 = -1;
         }
-
-        ++it;
     }
 
     return durationUs + (time2 - time1);
@@ -392,11 +396,19 @@
         return getBufferedDurationUs_l(&finalResult);
     }
 
-    List<sp<ABuffer> >::iterator it = mBuffers.begin();
-    sp<ABuffer> buffer = *it;
+    sp<ABuffer> buffer;
+    int32_t discard;
+    int64_t startTimeUs = -1ll;
+    List<sp<ABuffer> >::iterator it;
+    for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
+        buffer = *it;
+        if (buffer->meta()->findInt32("discard", &discard) && discard) {
+            continue;
+        }
+        buffer->meta()->findInt64("timeUs", &startTimeUs);
+        break;
+    }
 
-    int64_t startTimeUs;
-    buffer->meta()->findInt64("timeUs", &startTimeUs);
     if (startTimeUs < 0) {
         return 0;
     }
@@ -461,6 +473,10 @@
     mEnabled = enable;
 }
 
+/*
+ * returns the sample meta that's delayUs after queue head
+ * (NULL if such sample is unavailable)
+ */
 sp<AMessage> AnotherPacketSource::getMetaAfterLastDequeued(int64_t delayUs) {
     Mutex::Autolock autoLock(mLock);
     int64_t firstUs = -1;
@@ -490,19 +506,28 @@
             }
         }
     }
-    return mLatestEnqueuedMeta;
+    return NULL;
 }
 
-void AnotherPacketSource::trimBuffersAfterTimeUs(
-        size_t discontinuitySeq, int64_t timeUs) {
-    ALOGV("trimBuffersAfterTimeUs: discontinuitySeq %zu, timeUs %lld",
-            discontinuitySeq, (long long)timeUs);
+/*
+ * removes samples with time equal or after meta
+ */
+void AnotherPacketSource::trimBuffersAfterMeta(
+        const sp<AMessage> &meta) {
+    if (meta == NULL) {
+        ALOGW("trimming with NULL meta, ignoring");
+        return;
+    }
 
     Mutex::Autolock autoLock(mLock);
     if (mBuffers.empty()) {
         return;
     }
 
+    HLSTime stopTime(meta);
+    ALOGV("trimBuffersAfterMeta: discontinuitySeq %d, timeUs %lld",
+            stopTime.mSeq, (long long)stopTime.mTimeUs);
+
     List<sp<ABuffer> >::iterator it;
     sp<AMessage> newLatestEnqueuedMeta = NULL;
     int64_t newLastQueuedTimeUs = 0;
@@ -514,20 +539,15 @@
             newDiscontinuityCount++;
             continue;
         }
-        size_t curDiscontinuitySeq;
-        int64_t curTimeUs;
-        CHECK(buffer->meta()->findInt32(
-                "discontinuitySeq", (int32_t*)&curDiscontinuitySeq));
-        CHECK(buffer->meta()->findInt64("timeUs", &curTimeUs));
-        if ((curDiscontinuitySeq > discontinuitySeq
-                || (curDiscontinuitySeq == discontinuitySeq
-                        && curTimeUs >= timeUs))) {
-            ALOGI("trimming from %lld (inclusive) to end",
-                    (long long)curTimeUs);
+
+        HLSTime curTime(buffer->meta());
+        if (!(curTime < stopTime)) {
+            ALOGV("trimming from %lld (inclusive) to end",
+                    (long long)curTime.mTimeUs);
             break;
         }
         newLatestEnqueuedMeta = buffer->meta();
-        newLastQueuedTimeUs = curTimeUs;
+        newLastQueuedTimeUs = curTime.mTimeUs;
     }
     mBuffers.erase(it, mBuffers.end());
     mLatestEnqueuedMeta = newLatestEnqueuedMeta;
@@ -535,11 +555,20 @@
     mQueuedDiscontinuityCount = newDiscontinuityCount;
 }
 
-sp<AMessage> AnotherPacketSource::trimBuffersBeforeTimeUs(
-        size_t discontinuitySeq, int64_t timeUs) {
-    ALOGV("trimBuffersBeforeTimeUs: discontinuitySeq %zu, timeUs %lld",
-            discontinuitySeq, (long long)timeUs);
-    sp<AMessage> meta;
+/*
+ * removes samples with time equal or before meta;
+ * returns first sample left in the queue.
+ *
+ * (for AVC, if trim happens, the samples left will always start
+ * at next IDR.)
+ */
+sp<AMessage> AnotherPacketSource::trimBuffersBeforeMeta(
+        const sp<AMessage> &meta) {
+    HLSTime startTime(meta);
+    ALOGV("trimBuffersBeforeMeta: discontinuitySeq %d, timeUs %lld",
+            startTime.mSeq, (long long)startTime.mTimeUs);
+
+    sp<AMessage> firstMeta;
     Mutex::Autolock autoLock(mLock);
     if (mBuffers.empty()) {
         return NULL;
@@ -572,24 +601,19 @@
         if (isAvc && !IsIDR(buffer)) {
             continue;
         }
-        size_t curDiscontinuitySeq;
-        int64_t curTimeUs;
-        CHECK(buffer->meta()->findInt32(
-                "discontinuitySeq", (int32_t*)&curDiscontinuitySeq));
-        CHECK(buffer->meta()->findInt64("timeUs", &curTimeUs));
-        if ((curDiscontinuitySeq > discontinuitySeq
-                || (curDiscontinuitySeq == discontinuitySeq
-                        && curTimeUs > timeUs))) {
-            ALOGI("trimming from beginning to %lld (not inclusive)",
-                    (long long)curTimeUs);
-            meta = buffer->meta();
+
+        HLSTime curTime(buffer->meta());
+        if (startTime < curTime) {
+            ALOGV("trimming from beginning to %lld (not inclusive)",
+                    (long long)curTime.mTimeUs);
+            firstMeta = buffer->meta();
             break;
         }
     }
     mBuffers.erase(mBuffers.begin(), it);
     mQueuedDiscontinuityCount -= discontinuityCount;
     mLatestDequeuedMeta = NULL;
-    return meta;
+    return firstMeta;
 }
 
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index e126006..fa7dd6a 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -76,8 +76,8 @@
     sp<AMessage> getLatestDequeuedMeta();
     sp<AMessage> getMetaAfterLastDequeued(int64_t delayUs);
 
-    void trimBuffersAfterTimeUs(size_t discontinuitySeq, int64_t timeUs);
-    sp<AMessage> trimBuffersBeforeTimeUs(size_t discontinuitySeq, int64_t timeUs);
+    void trimBuffersAfterMeta(const sp<AMessage> &meta);
+    sp<AMessage> trimBuffersBeforeMeta(const sp<AMessage> &meta);
 
 protected:
     virtual ~AnotherPacketSource();
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 88da275..b17985c 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -257,8 +257,8 @@
                 uint8_t *ptr = (uint8_t *)data;
 
                 ssize_t startOffset = -1;
-                for (size_t i = 0; i + 3 < size; ++i) {
-                    if (!memcmp("\x00\x00\x00\x01", &ptr[i], 4)) {
+                for (size_t i = 0; i + 2 < size; ++i) {
+                    if (!memcmp("\x00\x00\x01", &ptr[i], 3)) {
                         startOffset = i;
                         break;
                     }
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 1f43d6d..33cfd1d 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -85,12 +85,6 @@
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
 
-    int64_t seekTimeUs;
-    ReadOptions::SeekMode seekMode;
-    if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
-        return ERROR_UNSUPPORTED;
-    }
-
     status_t finalResult;
     while (!mImpl->hasBufferAvailable(&finalResult)) {
         if (finalResult != OK) {
@@ -103,6 +97,17 @@
         }
     }
 
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode seekMode;
+    if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+        // A seek was requested, but we don't actually support seeking and so can only "seek" to
+        // the current position
+        int64_t nextBufTimeUs;
+        if (mImpl->nextBufferTime(&nextBufTimeUs) != OK || seekTimeUs != nextBufTimeUs) {
+            return ERROR_UNSUPPORTED;
+        }
+    }
+
     return mImpl->read(out, options);
 }
 
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 9b6958a..3ab241a 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -85,7 +85,7 @@
         void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
 
         if (libHandle == NULL) {
-            ALOGE("unable to dlopen %s", libName.c_str());
+            ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());
 
             return OMX_ErrorComponentNotFound;
         }
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index fee2347..f8446ac 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -44,9 +44,9 @@
     SpdifStreamOut.cpp          \
     Effects.cpp                 \
     AudioMixer.cpp.arm          \
-    PatchPanel.cpp
-
-LOCAL_SRC_FILES += StateQueue.cpp
+    BufferProviders.cpp         \
+    PatchPanel.cpp              \
+    StateQueue.cpp
 
 LOCAL_C_INCLUDES := \
     $(TOPDIR)frameworks/av/services/audiopolicy \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f3206cb..5002099 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -45,6 +45,8 @@
 #include "AudioFlinger.h"
 #include "ServiceUtilities.h"
 
+#include <media/AudioResamplerPublic.h>
+
 #include <media/EffectsFactoryApi.h>
 #include <audio_effects/effect_visualizer.h>
 #include <audio_effects/effect_ns.h>
@@ -1140,19 +1142,46 @@
     if (ret != NO_ERROR) {
         return 0;
     }
+    if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
+        return 0;
+    }
 
     AutoMutex lock(mHardwareLock);
     mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
-    audio_config_t config;
-    memset(&config, 0, sizeof(config));
-    config.sample_rate = sampleRate;
-    config.channel_mask = channelMask;
-    config.format = format;
+    audio_config_t config, proposed;
+    memset(&proposed, 0, sizeof(proposed));
+    proposed.sample_rate = sampleRate;
+    proposed.channel_mask = channelMask;
+    proposed.format = format;
 
     audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
-    size_t size = dev->get_input_buffer_size(dev, &config);
+    size_t frames;
+    for (;;) {
+        // Note: config is currently a const parameter for get_input_buffer_size()
+        // but we use a copy from proposed in case config changes from the call.
+        config = proposed;
+        frames = dev->get_input_buffer_size(dev, &config);
+        if (frames != 0) {
+            break; // hal success, config is the result
+        }
+        // change one parameter of the configuration each iteration to a more "common" value
+        // to see if the device will support it.
+        if (proposed.format != AUDIO_FORMAT_PCM_16_BIT) {
+            proposed.format = AUDIO_FORMAT_PCM_16_BIT;
+        } else if (proposed.sample_rate != 44100) { // 44.1 is claimed as must in CDD as well as
+            proposed.sample_rate = 44100;           // legacy AudioRecord.java. TODO: Query hw?
+        } else {
+            ALOGW("getInputBufferSize failed with minimum buffer size sampleRate %u, "
+                    "format %#x, channelMask 0x%X",
+                    sampleRate, format, channelMask);
+            break; // retries failed, break out of loop with frames == 0.
+        }
+    }
     mHardwareStatus = AUDIO_HW_IDLE;
-    return size;
+    if (frames > 0 && config.sample_rate != sampleRate) {
+        frames = destinationFramesPossible(frames, sampleRate, config.sample_rate);
+    }
+    return frames; // may be converted to bytes at the Java level.
 }
 
 uint32_t AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
@@ -1419,9 +1448,8 @@
         goto Exit;
     }
 
-    // we don't yet support anything other than 16-bit PCM
-    if (!(audio_is_valid_format(format) &&
-            audio_is_linear_pcm(format) && format == AUDIO_FORMAT_PCM_16_BIT)) {
+    // we don't yet support anything other than linear PCM
+    if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
         ALOGE("openRecord() invalid format %#x", format);
         lStatus = BAD_VALUE;
         goto Exit;
@@ -2002,11 +2030,11 @@
             status, address.string());
 
     // If the input could not be opened with the requested parameters and we can handle the
-    // conversion internally, try to open again with the proposed parameters. The AudioFlinger can
-    // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs.
+    // conversion internally, try to open again with the proposed parameters.
     if (status == BAD_VALUE &&
-            config->format == halconfig.format && halconfig.format == AUDIO_FORMAT_PCM_16_BIT &&
-        (halconfig.sample_rate <= 2 * config->sample_rate) &&
+        audio_is_linear_pcm(config->format) &&
+        audio_is_linear_pcm(halconfig.format) &&
+        (halconfig.sample_rate <= AUDIO_RESAMPLER_DOWN_RATIO_MAX * config->sample_rate) &&
         (audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_2) &&
         (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2)) {
         // FIXME describe the change proposed by HAL (save old values so we can log them here)
diff --git a/services/audioflinger/AudioHwDevice.cpp b/services/audioflinger/AudioHwDevice.cpp
index 09d86ea..3191598 100644
--- a/services/audioflinger/AudioHwDevice.cpp
+++ b/services/audioflinger/AudioHwDevice.cpp
@@ -44,7 +44,7 @@
     AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
 
     // Try to open the HAL first using the current format.
-    ALOGV("AudioHwDevice::openOutputStream(), try "
+    ALOGV("openOutputStream(), try "
             " sampleRate %d, Format %#x, "
             "channelMask %#x",
             config->sample_rate,
@@ -59,7 +59,7 @@
         // FIXME Look at any modification to the config.
         // The HAL might modify the config to suggest a wrapped format.
         // Log this so we can see what the HALs are doing.
-        ALOGI("AudioHwDevice::openOutputStream(), HAL returned"
+        ALOGI("openOutputStream(), HAL returned"
             " sampleRate %d, Format %#x, "
             "channelMask %#x, status %d",
             config->sample_rate,
@@ -72,16 +72,19 @@
                 && ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0)
                 && ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0);
 
-        // FIXME - Add isEncodingSupported() query to SPDIF wrapper then
-        // call it from here.
         if (wrapperNeeded) {
-            outputStream = new SpdifStreamOut(this, flags);
-            status = outputStream->open(handle, devices, &originalConfig, address);
-            if (status != NO_ERROR) {
-                ALOGE("ERROR - AudioHwDevice::openOutputStream(), SPDIF open returned %d",
-                    status);
-                delete outputStream;
-                outputStream = NULL;
+            if (SPDIFEncoder::isFormatSupported(originalConfig.format)) {
+                outputStream = new SpdifStreamOut(this, flags, originalConfig.format);
+                status = outputStream->open(handle, devices, &originalConfig, address);
+                if (status != NO_ERROR) {
+                    ALOGE("ERROR - openOutputStream(), SPDIF open returned %d",
+                        status);
+                    delete outputStream;
+                    outputStream = NULL;
+                }
+            } else {
+                ALOGE("ERROR - openOutputStream(), SPDIFEncoder does not support format 0x%08x",
+                    originalConfig.format);
             }
         }
     }
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index dddca02..c2c791f 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -38,9 +38,7 @@
 #include <audio_utils/format.h>
 #include <common_time/local_clock.h>
 #include <common_time/cc_helper.h>
-
-#include <media/EffectsFactoryApi.h>
-#include <audio_effects/effect_downmix.h>
+#include <media/AudioResamplerPublic.h>
 
 #include "AudioMixerOps.h"
 #include "AudioMixer.h"
@@ -91,323 +89,6 @@
     return a < b ? a : b;
 }
 
-AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
-        size_t outputFrameSize, size_t bufferFrameCount) :
-        mInputFrameSize(inputFrameSize),
-        mOutputFrameSize(outputFrameSize),
-        mLocalBufferFrameCount(bufferFrameCount),
-        mLocalBufferData(NULL),
-        mConsumed(0)
-{
-    ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
-            inputFrameSize, outputFrameSize, bufferFrameCount);
-    LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
-            "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
-            inputFrameSize, outputFrameSize);
-    if (mLocalBufferFrameCount) {
-        (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
-    }
-    mBuffer.frameCount = 0;
-}
-
-AudioMixer::CopyBufferProvider::~CopyBufferProvider()
-{
-    ALOGV("~CopyBufferProvider(%p)", this);
-    if (mBuffer.frameCount != 0) {
-        mTrackBufferProvider->releaseBuffer(&mBuffer);
-    }
-    free(mLocalBufferData);
-}
-
-status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
-        int64_t pts)
-{
-    //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
-    //        this, pBuffer, pBuffer->frameCount, pts);
-    if (mLocalBufferFrameCount == 0) {
-        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
-        if (res == OK) {
-            copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
-        }
-        return res;
-    }
-    if (mBuffer.frameCount == 0) {
-        mBuffer.frameCount = pBuffer->frameCount;
-        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
-        // At one time an upstream buffer provider had
-        // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
-        //
-        // By API spec, if res != OK, then mBuffer.frameCount == 0.
-        // but there may be improper implementations.
-        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
-        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
-            pBuffer->raw = NULL;
-            pBuffer->frameCount = 0;
-            return res;
-        }
-        mConsumed = 0;
-    }
-    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
-    size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
-    count = min(count, pBuffer->frameCount);
-    pBuffer->raw = mLocalBufferData;
-    pBuffer->frameCount = count;
-    copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
-            pBuffer->frameCount);
-    return OK;
-}
-
-void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
-{
-    //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
-    //        this, pBuffer, pBuffer->frameCount);
-    if (mLocalBufferFrameCount == 0) {
-        mTrackBufferProvider->releaseBuffer(pBuffer);
-        return;
-    }
-    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
-    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
-    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
-        mTrackBufferProvider->releaseBuffer(&mBuffer);
-        ALOG_ASSERT(mBuffer.frameCount == 0);
-    }
-    pBuffer->raw = NULL;
-    pBuffer->frameCount = 0;
-}
-
-void AudioMixer::CopyBufferProvider::reset()
-{
-    if (mBuffer.frameCount != 0) {
-        mTrackBufferProvider->releaseBuffer(&mBuffer);
-    }
-    mConsumed = 0;
-}
-
-AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider(
-        audio_channel_mask_t inputChannelMask,
-        audio_channel_mask_t outputChannelMask, audio_format_t format,
-        uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
-        CopyBufferProvider(
-            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
-            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
-            bufferFrameCount)  // set bufferFrameCount to 0 to do in-place
-{
-    ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
-            this, inputChannelMask, outputChannelMask, format,
-            sampleRate, sessionId);
-    if (!sIsMultichannelCapable
-            || EffectCreate(&sDwnmFxDesc.uuid,
-                    sessionId,
-                    SESSION_ID_INVALID_AND_IGNORED,
-                    &mDownmixHandle) != 0) {
-         ALOGE("DownmixerBufferProvider() error creating downmixer effect");
-         mDownmixHandle = NULL;
-         return;
-     }
-     // channel input configuration will be overridden per-track
-     mDownmixConfig.inputCfg.channels = inputChannelMask;   // FIXME: Should be bits
-     mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
-     mDownmixConfig.inputCfg.format = format;
-     mDownmixConfig.outputCfg.format = format;
-     mDownmixConfig.inputCfg.samplingRate = sampleRate;
-     mDownmixConfig.outputCfg.samplingRate = sampleRate;
-     mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
-     mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
-     // input and output buffer provider, and frame count will not be used as the downmix effect
-     // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
-     mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
-             EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
-     mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
-
-     int cmdStatus;
-     uint32_t replySize = sizeof(int);
-
-     // Configure downmixer
-     status_t status = (*mDownmixHandle)->command(mDownmixHandle,
-             EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
-             &mDownmixConfig /*pCmdData*/,
-             &replySize, &cmdStatus /*pReplyData*/);
-     if (status != 0 || cmdStatus != 0) {
-         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
-                 status, cmdStatus);
-         EffectRelease(mDownmixHandle);
-         mDownmixHandle = NULL;
-         return;
-     }
-
-     // Enable downmixer
-     replySize = sizeof(int);
-     status = (*mDownmixHandle)->command(mDownmixHandle,
-             EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
-             &replySize, &cmdStatus /*pReplyData*/);
-     if (status != 0 || cmdStatus != 0) {
-         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
-                 status, cmdStatus);
-         EffectRelease(mDownmixHandle);
-         mDownmixHandle = NULL;
-         return;
-     }
-
-     // Set downmix type
-     // parameter size rounded for padding on 32bit boundary
-     const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
-     const int downmixParamSize =
-             sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
-     effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
-     param->psize = sizeof(downmix_params_t);
-     const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
-     memcpy(param->data, &downmixParam, param->psize);
-     const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
-     param->vsize = sizeof(downmix_type_t);
-     memcpy(param->data + psizePadded, &downmixType, param->vsize);
-     replySize = sizeof(int);
-     status = (*mDownmixHandle)->command(mDownmixHandle,
-             EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
-             param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
-     free(param);
-     if (status != 0 || cmdStatus != 0) {
-         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
-                 status, cmdStatus);
-         EffectRelease(mDownmixHandle);
-         mDownmixHandle = NULL;
-         return;
-     }
-     ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
-}
-
-AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
-{
-    ALOGV("~DownmixerBufferProvider (%p)", this);
-    EffectRelease(mDownmixHandle);
-    mDownmixHandle = NULL;
-}
-
-void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
-    mDownmixConfig.inputCfg.buffer.frameCount = frames;
-    mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
-    mDownmixConfig.outputCfg.buffer.frameCount = frames;
-    mDownmixConfig.outputCfg.buffer.raw = dst;
-    // may be in-place if src == dst.
-    status_t res = (*mDownmixHandle)->process(mDownmixHandle,
-            &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
-    ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
-}
-
-/* call once in a pthread_once handler. */
-/*static*/ status_t AudioMixer::DownmixerBufferProvider::init()
-{
-    // find multichannel downmix effect if we have to play multichannel content
-    uint32_t numEffects = 0;
-    int ret = EffectQueryNumberEffects(&numEffects);
-    if (ret != 0) {
-        ALOGE("AudioMixer() error %d querying number of effects", ret);
-        return NO_INIT;
-    }
-    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
-
-    for (uint32_t i = 0 ; i < numEffects ; i++) {
-        if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
-            ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
-            if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
-                ALOGI("found effect \"%s\" from %s",
-                        sDwnmFxDesc.name, sDwnmFxDesc.implementor);
-                sIsMultichannelCapable = true;
-                break;
-            }
-        }
-    }
-    ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
-    return NO_INIT;
-}
-
-/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false;
-/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc;
-
-AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
-        audio_channel_mask_t outputChannelMask, audio_format_t format,
-        size_t bufferFrameCount) :
-        CopyBufferProvider(
-                audio_bytes_per_sample(format)
-                    * audio_channel_count_from_out_mask(inputChannelMask),
-                audio_bytes_per_sample(format)
-                    * audio_channel_count_from_out_mask(outputChannelMask),
-                bufferFrameCount),
-        mFormat(format),
-        mSampleSize(audio_bytes_per_sample(format)),
-        mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
-        mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
-{
-    ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
-            this, format, inputChannelMask, outputChannelMask,
-            mInputChannels, mOutputChannels);
-
-    const audio_channel_representation_t inputRepresentation =
-            audio_channel_mask_get_representation(inputChannelMask);
-    const audio_channel_representation_t outputRepresentation =
-            audio_channel_mask_get_representation(outputChannelMask);
-    const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask);
-    const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask);
-
-    switch (inputRepresentation) {
-    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
-        switch (outputRepresentation) {
-        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
-            memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
-                    outputBits, inputBits);
-            return;
-        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
-            // TODO: output channel index mask not currently allowed
-            // fall through
-        default:
-            break;
-        }
-        break;
-    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
-        switch (outputRepresentation) {
-        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
-            memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry),
-                    outputBits, inputBits);
-            return;
-        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
-            // TODO: output channel index mask not currently allowed
-            // fall through
-        default:
-            break;
-        }
-        break;
-    default:
-        break;
-    }
-    LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x",
-            inputChannelMask, outputChannelMask);
-}
-
-void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
-    memcpy_by_index_array(dst, mOutputChannels,
-            src, mInputChannels, mIdxAry, mSampleSize, frames);
-}
-
-AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
-        audio_format_t inputFormat, audio_format_t outputFormat,
-        size_t bufferFrameCount) :
-        CopyBufferProvider(
-            channels * audio_bytes_per_sample(inputFormat),
-            channels * audio_bytes_per_sample(outputFormat),
-            bufferFrameCount),
-        mChannels(channels),
-        mInputFormat(inputFormat),
-        mOutputFormat(outputFormat)
-{
-    ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
-}
-
-void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
-    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels);
-}
-
 // ----------------------------------------------------------------------------
 
 // Ensure mConfiguredNames bitmask is initialized properly on all architectures.
@@ -442,6 +123,7 @@
         t->resampler = NULL;
         t->downmixerBufferProvider = NULL;
         t->mReformatBufferProvider = NULL;
+        t->mTimestretchBufferProvider = NULL;
         t++;
     }
 
@@ -454,6 +136,7 @@
         delete t->resampler;
         delete t->downmixerBufferProvider;
         delete t->mReformatBufferProvider;
+        delete t->mTimestretchBufferProvider;
         t++;
     }
     delete [] mState.outputTemp;
@@ -532,6 +215,7 @@
         t->mReformatBufferProvider = NULL;
         t->downmixerBufferProvider = NULL;
         t->mPostDownmixReformatBufferProvider = NULL;
+        t->mTimestretchBufferProvider = NULL;
         t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
         t->mFormat = format;
         t->mMixerInFormat = selectMixerInFormat(format);
@@ -539,6 +223,8 @@
         t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
                 AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
         t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
+        t->mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+        t->mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
         // Check the downmixing (or upmixing) requirements.
         status_t status = t->prepareForDownmix();
         if (status != OK) {
@@ -731,6 +417,10 @@
         mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
         bufferProvider = mPostDownmixReformatBufferProvider;
     }
+    if (mTimestretchBufferProvider) {
+        mTimestretchBufferProvider->setBufferProvider(bufferProvider);
+        bufferProvider = mTimestretchBufferProvider;
+    }
 }
 
 void AudioMixer::deleteTrackName(int name)
@@ -751,7 +441,9 @@
     mState.tracks[name].unprepareForDownmix();
     // delete the reformatter
     mState.tracks[name].unprepareForReformat();
-
+    // delete the timestretch provider
+    delete track.mTimestretchBufferProvider;
+    track.mTimestretchBufferProvider = NULL;
     mTrackNames &= ~(1<<name);
 }
 
@@ -973,6 +665,26 @@
             }
         }
         break;
+        case TIMESTRETCH:
+            switch (param) {
+            case PLAYBACK_RATE: {
+                const float speed = reinterpret_cast<float*>(value)[0];
+                const float pitch = reinterpret_cast<float*>(value)[1];
+                ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= speed
+                        && speed <= AUDIO_TIMESTRETCH_SPEED_MAX,
+                        "bad speed %f", speed);
+                ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= pitch
+                        && pitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
+                        "bad pitch %f", pitch);
+                if (track.setPlaybackRate(speed, pitch)) {
+                    ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, %f %f", speed, pitch);
+                    // invalidateState(1 << name);
+                }
+                } break;
+            default:
+                LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
+            }
+            break;
 
     default:
         LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
@@ -1018,6 +730,28 @@
     return false;
 }
 
+bool AudioMixer::track_t::setPlaybackRate(float speed, float pitch)
+{
+    if (speed == mSpeed && pitch == mPitch) {
+        return false;
+    }
+    mSpeed = speed;
+    mPitch = pitch;
+    if (mTimestretchBufferProvider == NULL) {
+        // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
+        // but if none exists, it is the channel count (1 for mono).
+        const int timestretchChannelCount = downmixerBufferProvider != NULL
+                ? mMixerChannelCount : channelCount;
+        mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
+                mMixerInFormat, sampleRate, speed, pitch);
+        reconfigureBufferProviders();
+    } else {
+        reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
+                ->setPlaybackRate(speed, pitch);
+    }
+    return true;
+}
+
 /* Checks to see if the volume ramp has completed and clears the increment
  * variables appropriately.
  *
@@ -1096,6 +830,8 @@
         mState.tracks[name].downmixerBufferProvider->reset();
     } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
         mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
+    } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
+        mState.tracks[name].mTimestretchBufferProvider->reset();
     }
 
     mState.tracks[name].mInputBufferProvider = bufferProvider;
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 381036b..e27a0d1 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -29,6 +29,7 @@
 #include <utils/threads.h>
 
 #include "AudioResampler.h"
+#include "BufferProviders.h"
 
 // FIXME This is actually unity gain, which might not be max in future, expressed in U.12
 #define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
@@ -72,6 +73,7 @@
         RESAMPLE        = 0x3001,
         RAMP_VOLUME     = 0x3002, // ramp to new volume
         VOLUME          = 0x3003, // don't ramp
+        TIMESTRETCH     = 0x3004,
 
         // set Parameter names
         // for target TRACK
@@ -99,6 +101,9 @@
         VOLUME0         = 0x4200,
         VOLUME1         = 0x4201,
         AUXLEVEL        = 0x4210,
+        // for target TIMESTRETCH
+        PLAYBACK_RATE   = 0x4300, // Configure timestretch on this track name;
+                                  // parameter 'value' is a pointer to the new playback rate.
     };
 
 
@@ -159,7 +164,6 @@
 
     struct state_t;
     struct track_t;
-    class CopyBufferProvider;
 
     typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
                            int32_t* aux);
@@ -214,6 +218,9 @@
 
         /* Buffer providers are constructed to translate the track input data as needed.
          *
+         * TODO: perhaps make a single PlaybackConverterProvider class to move
+         * all pre-mixer track buffer conversions outside the AudioMixer class.
+         *
          * 1) mInputBufferProvider: The AudioTrack buffer provider.
          * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to
          *    match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
@@ -223,13 +230,14 @@
          *    the number of channels required by the mixer sink.
          * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
          *    the downmixer requirements to the mixer engine input requirements.
+         * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
          */
         AudioBufferProvider*     mInputBufferProvider;    // externally provided buffer provider.
-        CopyBufferProvider*      mReformatBufferProvider; // provider wrapper for reformatting.
-        CopyBufferProvider*      downmixerBufferProvider; // wrapper for channel conversion.
-        CopyBufferProvider*      mPostDownmixReformatBufferProvider;
+        PassthruBufferProvider*  mReformatBufferProvider; // provider wrapper for reformatting.
+        PassthruBufferProvider*  downmixerBufferProvider; // wrapper for channel conversion.
+        PassthruBufferProvider*  mPostDownmixReformatBufferProvider;
+        PassthruBufferProvider*  mTimestretchBufferProvider;
 
-        // 16-byte boundary
         int32_t     sessionId;
 
         audio_format_t mMixerFormat;     // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
@@ -251,6 +259,9 @@
         audio_channel_mask_t mMixerChannelMask;
         uint32_t             mMixerChannelCount;
 
+        float          mSpeed;
+        float          mPitch;
+
         bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
         bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
         bool        doesResample() const { return resampler != NULL; }
@@ -263,6 +274,7 @@
         void        unprepareForDownmix();
         status_t    prepareForReformat();
         void        unprepareForReformat();
+        bool        setPlaybackRate(float speed, float pitch);
         void        reconfigureBufferProviders();
     };
 
@@ -282,112 +294,6 @@
         track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
     };
 
-    // Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
-    // and ReformatBufferProvider.
-    // It handles a private buffer for use in converting format or channel masks from the
-    // input data to a form acceptable by the mixer.
-    // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
-    // processing pipeline.
-    class CopyBufferProvider : public AudioBufferProvider {
-    public:
-        // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
-        // If bufferFrameCount is 0, no private buffer is created and in-place modification of
-        // the upstream buffer provider's buffers is performed by copyFrames().
-        CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
-                size_t bufferFrameCount);
-        virtual ~CopyBufferProvider();
-
-        // Overrides AudioBufferProvider methods
-        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
-        virtual void releaseBuffer(Buffer* buffer);
-
-        // Other public methods
-
-        // call this to release the buffer to the upstream provider.
-        // treat it as an audio discontinuity for future samples.
-        virtual void reset();
-
-        // this function should be supplied by the derived class.  It converts
-        // #frames in the *src pointer to the *dst pointer.  It is public because
-        // some providers will allow this to work on arbitrary buffers outside
-        // of the internal buffers.
-        virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
-
-        // set the upstream buffer provider. Consider calling "reset" before this function.
-        void setBufferProvider(AudioBufferProvider *p) {
-            mTrackBufferProvider = p;
-        }
-
-    protected:
-        AudioBufferProvider* mTrackBufferProvider;
-        const size_t         mInputFrameSize;
-        const size_t         mOutputFrameSize;
-    private:
-        AudioBufferProvider::Buffer mBuffer;
-        const size_t         mLocalBufferFrameCount;
-        void*                mLocalBufferData;
-        size_t               mConsumed;
-    };
-
-    // DownmixerBufferProvider wraps a track AudioBufferProvider to provide
-    // position dependent downmixing by an Audio Effect.
-    class DownmixerBufferProvider : public CopyBufferProvider {
-    public:
-        DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
-                audio_channel_mask_t outputChannelMask, audio_format_t format,
-                uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
-        virtual ~DownmixerBufferProvider();
-        virtual void copyFrames(void *dst, const void *src, size_t frames);
-        bool isValid() const { return mDownmixHandle != NULL; }
-
-        static status_t init();
-        static bool isMultichannelCapable() { return sIsMultichannelCapable; }
-
-    protected:
-        effect_handle_t    mDownmixHandle;
-        effect_config_t    mDownmixConfig;
-
-        // effect descriptor for the downmixer used by the mixer
-        static effect_descriptor_t sDwnmFxDesc;
-        // indicates whether a downmix effect has been found and is usable by this mixer
-        static bool                sIsMultichannelCapable;
-        // FIXME: should we allow effects outside of the framework?
-        // We need to here. A special ioId that must be <= -2 so it does not map to a session.
-        static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
-    };
-
-    // RemixBufferProvider wraps a track AudioBufferProvider to perform an
-    // upmix or downmix to the proper channel count and mask.
-    class RemixBufferProvider : public CopyBufferProvider {
-    public:
-        RemixBufferProvider(audio_channel_mask_t inputChannelMask,
-                audio_channel_mask_t outputChannelMask, audio_format_t format,
-                size_t bufferFrameCount);
-        virtual void copyFrames(void *dst, const void *src, size_t frames);
-
-    protected:
-        const audio_format_t mFormat;
-        const size_t         mSampleSize;
-        const size_t         mInputChannels;
-        const size_t         mOutputChannels;
-        int8_t               mIdxAry[sizeof(uint32_t)*8]; // 32 bits => channel indices
-    };
-
-    // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
-    // to an acceptable mixer input format type.
-    class ReformatBufferProvider : public CopyBufferProvider {
-    public:
-        ReformatBufferProvider(int32_t channels,
-                audio_format_t inputFormat, audio_format_t outputFormat,
-                size_t bufferFrameCount);
-        virtual void copyFrames(void *dst, const void *src, size_t frames);
-
-    protected:
-        const int32_t        mChannels;
-        const audio_format_t mInputFormat;
-        const audio_format_t mOutputFormat;
-    };
-
     // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
     uint32_t        mTrackNames;
 
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 46e3d6c..e49b7b1 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -41,7 +41,7 @@
     AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) :
         AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
     }
-    virtual void resample(int32_t* out, size_t outFrameCount,
+    virtual size_t resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
 private:
     // number of bits used in interpolation multiply - 15 bits avoids overflow
@@ -51,9 +51,9 @@
     static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
 
     void init() {}
-    void resampleMono16(int32_t* out, size_t outFrameCount,
+    size_t resampleMono16(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
-    void resampleStereo16(int32_t* out, size_t outFrameCount,
+    size_t resampleStereo16(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
     void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
@@ -329,7 +329,7 @@
 
 // ----------------------------------------------------------------------------
 
-void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider) {
 
     // should never happen, but we overflow if it does
@@ -338,15 +338,16 @@
     // select the appropriate resampler
     switch (mChannelCount) {
     case 1:
-        resampleMono16(out, outFrameCount, provider);
-        break;
+        return resampleMono16(out, outFrameCount, provider);
     case 2:
-        resampleStereo16(out, outFrameCount, provider);
-        break;
+        return resampleStereo16(out, outFrameCount, provider);
+    default:
+        LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+        return 0;
     }
 }
 
-void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider) {
 
     int32_t vl = mVolume[0];
@@ -442,9 +443,10 @@
     // save state
     mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
+    return outputIndex / 2 /* channels for stereo */;
 }
 
-void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider) {
 
     int32_t vl = mVolume[0];
@@ -538,6 +540,7 @@
     // save state
     mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
+    return outputIndex;
 }
 
 #ifdef ASM_ARM_RESAMP1  // asm optimisation for ResamplerOrder1
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 863614a..a8e3e6f 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -67,12 +67,18 @@
     // Resample int16_t samples from provider and accumulate into 'out'.
     // A mono provider delivers a sequence of samples.
     // A stereo provider delivers a sequence of interleaved pairs of samples.
-    // Multi-channel providers are not supported.
+    //
     // In either case, 'out' holds interleaved pairs of fixed-point Q4.27.
     // That is, for a mono provider, there is an implicit up-channeling.
     // Since this method accumulates, the caller is responsible for clearing 'out' initially.
-    // FIXME assumes provider is always successful; it should return the actual frame count.
-    virtual void resample(int32_t* out, size_t outFrameCount,
+    //
+    // For a float resampler, 'out' holds interleaved pairs of float samples.
+    //
+    // Multichannel interleaved frames for n > 2 is supported for quality DYN_LOW_QUALITY,
+    // DYN_MED_QUALITY, and DYN_HIGH_QUALITY.
+    //
+    // Returns the number of frames resampled into the out buffer.
+    virtual size_t resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider) = 0;
 
     virtual void reset();
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
index d3cbd1c..172c2a5 100644
--- a/services/audioflinger/AudioResamplerCubic.cpp
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AudioSRC"
+#define LOG_TAG "AudioResamplerCubic"
 
 #include <stdint.h>
 #include <string.h>
@@ -32,7 +32,7 @@
     memset(&right, 0, sizeof(state));
 }
 
-void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider) {
 
     // should never happen, but we overflow if it does
@@ -41,15 +41,16 @@
     // select the appropriate resampler
     switch (mChannelCount) {
     case 1:
-        resampleMono16(out, outFrameCount, provider);
-        break;
+        return resampleMono16(out, outFrameCount, provider);
     case 2:
-        resampleStereo16(out, outFrameCount, provider);
-        break;
+        return resampleStereo16(out, outFrameCount, provider);
+    default:
+        LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+        return 0;
     }
 }
 
-void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider) {
 
     int32_t vl = mVolume[0];
@@ -67,7 +68,7 @@
         mBuffer.frameCount = inFrameCount;
         provider->getNextBuffer(&mBuffer, mPTS);
         if (mBuffer.raw == NULL) {
-            return;
+            return 0;
         }
         // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
     }
@@ -115,9 +116,10 @@
     // ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
     mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
+    return outputIndex / 2 /* channels for stereo */;
 }
 
-void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider) {
 
     int32_t vl = mVolume[0];
@@ -135,7 +137,7 @@
         mBuffer.frameCount = inFrameCount;
         provider->getNextBuffer(&mBuffer, mPTS);
         if (mBuffer.raw == NULL) {
-            return;
+            return 0;
         }
         // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount);
     }
@@ -182,6 +184,7 @@
     // ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
     mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
+    return outputIndex;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
index 1ddc5f9..4b45b0b 100644
--- a/services/audioflinger/AudioResamplerCubic.h
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -31,7 +31,7 @@
     AudioResamplerCubic(int inChannelCount, int32_t sampleRate) :
         AudioResampler(inChannelCount, sampleRate, MED_QUALITY) {
     }
-    virtual void resample(int32_t* out, size_t outFrameCount,
+    virtual size_t resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
 private:
     // number of bits used in interpolation multiply - 14 bits avoids overflow
@@ -43,9 +43,9 @@
         int32_t a, b, c, y0, y1, y2, y3;
     } state;
     void init();
-    void resampleMono16(int32_t* out, size_t outFrameCount,
+    size_t resampleMono16(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
-    void resampleStereo16(int32_t* out, size_t outFrameCount,
+    size_t resampleStereo16(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
     static inline int32_t interp(state* p, int32_t x) {
         return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1;
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index c21d4ca..6481b85 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -477,15 +477,15 @@
 }
 
 template<typename TC, typename TI, typename TO>
-void AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider)
 {
-    (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
+    return (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
 }
 
 template<typename TC, typename TI, typename TO>
 template<int CHANNELS, bool LOCKED, int STRIDE>
-void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
+size_t AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
         AudioBufferProvider* provider)
 {
     // TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out.
@@ -610,6 +610,7 @@
     ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer
     mInBuffer.setImpulse(impulse);
     mPhaseFraction = phaseFraction;
+    return outputIndex / OUTPUT_CHANNELS;
 }
 
 /* instantiate templates used by AudioResampler::create */
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
index 238b163..3b1c381 100644
--- a/services/audioflinger/AudioResamplerDyn.h
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -52,7 +52,7 @@
 
     virtual void setVolume(float left, float right);
 
-    virtual void resample(int32_t* out, size_t outFrameCount,
+    virtual size_t resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
 
 private:
@@ -111,10 +111,10 @@
             int inSampleRate, int outSampleRate, double tbwCheat);
 
     template<int CHANNELS, bool LOCKED, int STRIDE>
-    void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
+    size_t resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
 
     // define a pointer to member function type for resample
-    typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
+    typedef size_t (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
             size_t outFrameCount, AudioBufferProvider* provider);
 
     // data - the contiguous storage and layout of these is important.
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index ba9a356..41730ee 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -256,7 +256,7 @@
     mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right));
 }
 
-void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider)
 {
     // FIXME store current state (up or down sample) and only load the coefs when the state
@@ -272,17 +272,18 @@
     // select the appropriate resampler
     switch (mChannelCount) {
     case 1:
-        resample<1>(out, outFrameCount, provider);
-        break;
+        return resample<1>(out, outFrameCount, provider);
     case 2:
-        resample<2>(out, outFrameCount, provider);
-        break;
+        return resample<2>(out, outFrameCount, provider);
+    default:
+        LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+        return 0;
     }
 }
 
 
 template<int CHANNELS>
-void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
         AudioBufferProvider* provider)
 {
     const Constants& c(*mConstants);
@@ -357,6 +358,7 @@
     mImpulse = impulse;
     mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
+    return outputIndex / CHANNELS;
 }
 
 template<int CHANNELS>
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 6d8e85d..0fbeac8 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -39,7 +39,7 @@
 
     virtual ~AudioResamplerSinc();
 
-    virtual void resample(int32_t* out, size_t outFrameCount,
+    virtual size_t resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
 private:
     void init();
@@ -47,7 +47,7 @@
     virtual void setVolume(float left, float right);
 
     template<int CHANNELS>
-    void resample(int32_t* out, size_t outFrameCount,
+    size_t resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
 
     template<int CHANNELS>
diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp
new file mode 100644
index 0000000..e058e6c
--- /dev/null
+++ b/services/audioflinger/BufferProviders.cpp
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferProvider"
+//#define LOG_NDEBUG 0
+
+#include <audio_effects/effect_downmix.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
+#include <media/AudioResamplerPublic.h>
+#include <media/EffectsFactoryApi.h>
+
+#include <utils/Log.h>
+
+#include "Configuration.h"
+#include "BufferProviders.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+template <typename T>
+static inline T min(const T& a, const T& b)
+{
+    return a < b ? a : b;
+}
+
+CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
+        size_t outputFrameSize, size_t bufferFrameCount) :
+        mInputFrameSize(inputFrameSize),
+        mOutputFrameSize(outputFrameSize),
+        mLocalBufferFrameCount(bufferFrameCount),
+        mLocalBufferData(NULL),
+        mConsumed(0)
+{
+    ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
+            inputFrameSize, outputFrameSize, bufferFrameCount);
+    LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
+            "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
+            inputFrameSize, outputFrameSize);
+    if (mLocalBufferFrameCount) {
+        (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
+    }
+    mBuffer.frameCount = 0;
+}
+
+CopyBufferProvider::~CopyBufferProvider()
+{
+    ALOGV("~CopyBufferProvider(%p)", this);
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    free(mLocalBufferData);
+}
+
+status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+        int64_t pts)
+{
+    //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+    //        this, pBuffer, pBuffer->frameCount, pts);
+    if (mLocalBufferFrameCount == 0) {
+        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+        if (res == OK) {
+            copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
+        }
+        return res;
+    }
+    if (mBuffer.frameCount == 0) {
+        mBuffer.frameCount = pBuffer->frameCount;
+        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+        // At one time an upstream buffer provider had
+        // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
+        //
+        // By API spec, if res != OK, then mBuffer.frameCount == 0.
+        // but there may be improper implementations.
+        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+            pBuffer->raw = NULL;
+            pBuffer->frameCount = 0;
+            return res;
+        }
+        mConsumed = 0;
+    }
+    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+    size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
+    count = min(count, pBuffer->frameCount);
+    pBuffer->raw = mLocalBufferData;
+    pBuffer->frameCount = count;
+    copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
+            pBuffer->frameCount);
+    return OK;
+}
+
+void CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+    //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
+    //        this, pBuffer, pBuffer->frameCount);
+    if (mLocalBufferFrameCount == 0) {
+        mTrackBufferProvider->releaseBuffer(pBuffer);
+        return;
+    }
+    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
+    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+        ALOG_ASSERT(mBuffer.frameCount == 0);
+    }
+    pBuffer->raw = NULL;
+    pBuffer->frameCount = 0;
+}
+
+void CopyBufferProvider::reset()
+{
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    mConsumed = 0;
+}
+
+DownmixerBufferProvider::DownmixerBufferProvider(
+        audio_channel_mask_t inputChannelMask,
+        audio_channel_mask_t outputChannelMask, audio_format_t format,
+        uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
+        CopyBufferProvider(
+            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
+            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
+            bufferFrameCount)  // set bufferFrameCount to 0 to do in-place
+{
+    ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
+            this, inputChannelMask, outputChannelMask, format,
+            sampleRate, sessionId);
+    if (!sIsMultichannelCapable
+            || EffectCreate(&sDwnmFxDesc.uuid,
+                    sessionId,
+                    SESSION_ID_INVALID_AND_IGNORED,
+                    &mDownmixHandle) != 0) {
+         ALOGE("DownmixerBufferProvider() error creating downmixer effect");
+         mDownmixHandle = NULL;
+         return;
+     }
+     // channel input configuration will be overridden per-track
+     mDownmixConfig.inputCfg.channels = inputChannelMask;   // FIXME: Should be bits
+     mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
+     mDownmixConfig.inputCfg.format = format;
+     mDownmixConfig.outputCfg.format = format;
+     mDownmixConfig.inputCfg.samplingRate = sampleRate;
+     mDownmixConfig.outputCfg.samplingRate = sampleRate;
+     mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+     mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+     // input and output buffer provider, and frame count will not be used as the downmix effect
+     // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
+     mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
+             EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
+     mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
+
+     int cmdStatus;
+     uint32_t replySize = sizeof(int);
+
+     // Configure downmixer
+     status_t status = (*mDownmixHandle)->command(mDownmixHandle,
+             EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
+             &mDownmixConfig /*pCmdData*/,
+             &replySize, &cmdStatus /*pReplyData*/);
+     if (status != 0 || cmdStatus != 0) {
+         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
+                 status, cmdStatus);
+         EffectRelease(mDownmixHandle);
+         mDownmixHandle = NULL;
+         return;
+     }
+
+     // Enable downmixer
+     replySize = sizeof(int);
+     status = (*mDownmixHandle)->command(mDownmixHandle,
+             EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
+             &replySize, &cmdStatus /*pReplyData*/);
+     if (status != 0 || cmdStatus != 0) {
+         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
+                 status, cmdStatus);
+         EffectRelease(mDownmixHandle);
+         mDownmixHandle = NULL;
+         return;
+     }
+
+     // Set downmix type
+     // parameter size rounded for padding on 32bit boundary
+     const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
+     const int downmixParamSize =
+             sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
+     effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
+     param->psize = sizeof(downmix_params_t);
+     const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
+     memcpy(param->data, &downmixParam, param->psize);
+     const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
+     param->vsize = sizeof(downmix_type_t);
+     memcpy(param->data + psizePadded, &downmixType, param->vsize);
+     replySize = sizeof(int);
+     status = (*mDownmixHandle)->command(mDownmixHandle,
+             EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
+             param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
+     free(param);
+     if (status != 0 || cmdStatus != 0) {
+         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
+                 status, cmdStatus);
+         EffectRelease(mDownmixHandle);
+         mDownmixHandle = NULL;
+         return;
+     }
+     ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
+}
+
+DownmixerBufferProvider::~DownmixerBufferProvider()
+{
+    ALOGV("~DownmixerBufferProvider (%p)", this);
+    EffectRelease(mDownmixHandle);
+    mDownmixHandle = NULL;
+}
+
+void DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+    mDownmixConfig.inputCfg.buffer.frameCount = frames;
+    mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
+    mDownmixConfig.outputCfg.buffer.frameCount = frames;
+    mDownmixConfig.outputCfg.buffer.raw = dst;
+    // may be in-place if src == dst.
+    status_t res = (*mDownmixHandle)->process(mDownmixHandle,
+            &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
+    ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
+}
+
+/* call once in a pthread_once handler. */
+/*static*/ status_t DownmixerBufferProvider::init()
+{
+    // find multichannel downmix effect if we have to play multichannel content
+    uint32_t numEffects = 0;
+    int ret = EffectQueryNumberEffects(&numEffects);
+    if (ret != 0) {
+        ALOGE("AudioMixer() error %d querying number of effects", ret);
+        return NO_INIT;
+    }
+    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
+
+    for (uint32_t i = 0 ; i < numEffects ; i++) {
+        if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
+            ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
+            if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
+                ALOGI("found effect \"%s\" from %s",
+                        sDwnmFxDesc.name, sDwnmFxDesc.implementor);
+                sIsMultichannelCapable = true;
+                break;
+            }
+        }
+    }
+    ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
+    return NO_INIT;
+}
+
+/*static*/ bool DownmixerBufferProvider::sIsMultichannelCapable = false;
+/*static*/ effect_descriptor_t DownmixerBufferProvider::sDwnmFxDesc;
+
+RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
+        audio_channel_mask_t outputChannelMask, audio_format_t format,
+        size_t bufferFrameCount) :
+        CopyBufferProvider(
+                audio_bytes_per_sample(format)
+                    * audio_channel_count_from_out_mask(inputChannelMask),
+                audio_bytes_per_sample(format)
+                    * audio_channel_count_from_out_mask(outputChannelMask),
+                bufferFrameCount),
+        mFormat(format),
+        mSampleSize(audio_bytes_per_sample(format)),
+        mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
+        mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
+{
+    ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
+            this, format, inputChannelMask, outputChannelMask,
+            mInputChannels, mOutputChannels);
+
+    const audio_channel_representation_t inputRepresentation =
+            audio_channel_mask_get_representation(inputChannelMask);
+    const audio_channel_representation_t outputRepresentation =
+            audio_channel_mask_get_representation(outputChannelMask);
+    const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask);
+    const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask);
+
+    switch (inputRepresentation) {
+    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+        switch (outputRepresentation) {
+        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+            memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
+                    outputBits, inputBits);
+            return;
+        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+            // TODO: output channel index mask not currently allowed
+            // fall through
+        default:
+            break;
+        }
+        break;
+    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+        switch (outputRepresentation) {
+        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+            memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry),
+                    outputBits, inputBits);
+            return;
+        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+            // TODO: output channel index mask not currently allowed
+            // fall through
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+    LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x",
+            inputChannelMask, outputChannelMask);
+}
+
+void RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+    memcpy_by_index_array(dst, mOutputChannels,
+            src, mInputChannels, mIdxAry, mSampleSize, frames);
+}
+
+ReformatBufferProvider::ReformatBufferProvider(int32_t channelCount,
+        audio_format_t inputFormat, audio_format_t outputFormat,
+        size_t bufferFrameCount) :
+        CopyBufferProvider(
+                channelCount * audio_bytes_per_sample(inputFormat),
+                channelCount * audio_bytes_per_sample(outputFormat),
+                bufferFrameCount),
+        mChannelCount(channelCount),
+        mInputFormat(inputFormat),
+        mOutputFormat(outputFormat)
+{
+    ALOGV("ReformatBufferProvider(%p)(%u, %#x, %#x)",
+            this, channelCount, inputFormat, outputFormat);
+}
+
+void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
+}
+
+TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
+        audio_format_t format, uint32_t sampleRate, float speed, float pitch) :
+        mChannelCount(channelCount),
+        mFormat(format),
+        mSampleRate(sampleRate),
+        mFrameSize(channelCount * audio_bytes_per_sample(format)),
+        mSpeed(speed),
+        mPitch(pitch),
+        mLocalBufferFrameCount(0),
+        mLocalBufferData(NULL),
+        mRemaining(0)
+{
+    ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f)",
+            this, channelCount, format, sampleRate, speed, pitch);
+    mBuffer.frameCount = 0;
+}
+
+TimestretchBufferProvider::~TimestretchBufferProvider()
+{
+    ALOGV("~TimestretchBufferProvider(%p)", this);
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    free(mLocalBufferData);
+}
+
+status_t TimestretchBufferProvider::getNextBuffer(
+        AudioBufferProvider::Buffer *pBuffer, int64_t pts)
+{
+    ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+            this, pBuffer, pBuffer->frameCount, pts);
+
+    // BYPASS
+    //return mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+
+    // check if previously processed data is sufficient.
+    if (pBuffer->frameCount <= mRemaining) {
+        ALOGV("previous sufficient");
+        pBuffer->raw = mLocalBufferData;
+        return OK;
+    }
+
+    // do we need to resize our buffer?
+    if (pBuffer->frameCount > mLocalBufferFrameCount) {
+        void *newmem;
+        if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) {
+            if (mRemaining != 0) {
+                memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize);
+            }
+            free(mLocalBufferData);
+            mLocalBufferData = newmem;
+            mLocalBufferFrameCount = pBuffer->frameCount;
+        }
+    }
+
+    // need to fetch more data
+    const size_t outputDesired = pBuffer->frameCount - mRemaining;
+    mBuffer.frameCount = mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
+            ? outputDesired : outputDesired * mSpeed + 1;
+
+    status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+
+    ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+    if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+        ALOGD("buffer error");
+        if (mRemaining == 0) {
+            pBuffer->raw = NULL;
+            pBuffer->frameCount = 0;
+            return res;
+        } else { // return partial count
+            pBuffer->raw = mLocalBufferData;
+            pBuffer->frameCount = mRemaining;
+            return OK;
+        }
+    }
+
+    // time-stretch the data
+    size_t dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired);
+    size_t srcAvailable = mBuffer.frameCount;
+    processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable,
+            mBuffer.raw, &srcAvailable);
+
+    // release all data consumed
+    mBuffer.frameCount = srcAvailable;
+    mTrackBufferProvider->releaseBuffer(&mBuffer);
+
+    // update buffer vars with the actual data processed and return with buffer
+    mRemaining += dstAvailable;
+
+    pBuffer->raw = mLocalBufferData;
+    pBuffer->frameCount = mRemaining;
+
+    return OK;
+}
+
+void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+    ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))",
+       this, pBuffer, pBuffer->frameCount);
+
+    // BYPASS
+    //return mTrackBufferProvider->releaseBuffer(pBuffer);
+
+    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+    if (pBuffer->frameCount < mRemaining) {
+        memcpy(mLocalBufferData,
+                (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize,
+                (mRemaining - pBuffer->frameCount) * mFrameSize);
+        mRemaining -= pBuffer->frameCount;
+    } else if (pBuffer->frameCount == mRemaining) {
+        mRemaining = 0;
+    } else {
+        LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)",
+                pBuffer->frameCount, mRemaining);
+    }
+
+    pBuffer->raw = NULL;
+    pBuffer->frameCount = 0;
+}
+
+void TimestretchBufferProvider::reset()
+{
+    mRemaining = 0;
+}
+
+status_t TimestretchBufferProvider::setPlaybackRate(float speed, float pitch)
+{
+    mSpeed = speed;
+    mPitch = pitch;
+    return OK;
+}
+
+void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames,
+        const void *srcBuffer, size_t *srcFrames)
+{
+    ALOGV("processFrames(%zu %zu)  remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
+    // Note dstFrames is the required number of frames.
+
+    // Ensure consumption from src is as expected.
+    const size_t targetSrc = *dstFrames * mSpeed;
+    if (*srcFrames < targetSrc) { // limit dst frames to that possible
+        *dstFrames = *srcFrames / mSpeed;
+    } else if (*srcFrames > targetSrc + 1) {
+        *srcFrames = targetSrc + 1;
+    }
+
+    // Do the time stretch by memory copy without any local buffer.
+    if (*dstFrames <= *srcFrames) {
+        size_t copySize = mFrameSize * *dstFrames;
+        memcpy(dstBuffer, srcBuffer, copySize);
+    } else {
+        // cyclically repeat the source.
+        for (size_t count = 0; count < *dstFrames; count += *srcFrames) {
+            size_t remaining = min(*srcFrames, *dstFrames - count);
+            memcpy((uint8_t*)dstBuffer + mFrameSize * count,
+                    srcBuffer, mFrameSize * *srcFrames);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h
new file mode 100644
index 0000000..2b6ea47
--- /dev/null
+++ b/services/audioflinger/BufferProviders.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_PROVIDERS_H
+#define ANDROID_BUFFER_PROVIDERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <hardware/audio_effect.h>
+#include <media/AudioBufferProvider.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class PassthruBufferProvider : public AudioBufferProvider {
+public:
+    PassthruBufferProvider() : mTrackBufferProvider(NULL) { }
+
+    virtual ~PassthruBufferProvider() { }
+
+    // call this to release the buffer to the upstream provider.
+    // treat it as an audio discontinuity for future samples.
+    virtual void reset() { }
+
+    // set the upstream buffer provider. Consider calling "reset" before this function.
+    virtual void setBufferProvider(AudioBufferProvider *p) {
+        mTrackBufferProvider = p;
+    }
+
+protected:
+    AudioBufferProvider *mTrackBufferProvider;
+};
+
+// Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
+// and ReformatBufferProvider.
+// It handles a private buffer for use in converting format or channel masks from the
+// input data to a form acceptable by the mixer.
+// TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
+// processing pipeline.
+class CopyBufferProvider : public PassthruBufferProvider {
+public:
+    // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
+    // If bufferFrameCount is 0, no private buffer is created and in-place modification of
+    // the upstream buffer provider's buffers is performed by copyFrames().
+    CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
+            size_t bufferFrameCount);
+    virtual ~CopyBufferProvider();
+
+    // Overrides AudioBufferProvider methods
+    virtual status_t getNextBuffer(Buffer *buffer, int64_t pts);
+    virtual void releaseBuffer(Buffer *buffer);
+
+    // Overrides PassthruBufferProvider
+    virtual void reset();
+
+    // this function should be supplied by the derived class.  It converts
+    // #frames in the *src pointer to the *dst pointer.  It is public because
+    // some providers will allow this to work on arbitrary buffers outside
+    // of the internal buffers.
+    virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
+
+protected:
+    const size_t         mInputFrameSize;
+    const size_t         mOutputFrameSize;
+private:
+    AudioBufferProvider::Buffer mBuffer;
+    const size_t         mLocalBufferFrameCount;
+    void                *mLocalBufferData;
+    size_t               mConsumed;
+};
+
+// DownmixerBufferProvider derives from CopyBufferProvider to provide
+// position dependent downmixing by an Audio Effect.
+class DownmixerBufferProvider : public CopyBufferProvider {
+public:
+    DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
+            audio_channel_mask_t outputChannelMask, audio_format_t format,
+            uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
+    virtual ~DownmixerBufferProvider();
+    //Overrides
+    virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+    bool isValid() const { return mDownmixHandle != NULL; }
+    static status_t init();
+    static bool isMultichannelCapable() { return sIsMultichannelCapable; }
+
+protected:
+    effect_handle_t    mDownmixHandle;
+    effect_config_t    mDownmixConfig;
+
+    // effect descriptor for the downmixer used by the mixer
+    static effect_descriptor_t sDwnmFxDesc;
+    // indicates whether a downmix effect has been found and is usable by this mixer
+    static bool                sIsMultichannelCapable;
+    // FIXME: should we allow effects outside of the framework?
+    // We need to here. A special ioId that must be <= -2 so it does not map to a session.
+    static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
+};
+
+// RemixBufferProvider derives from CopyBufferProvider to perform an
+// upmix or downmix to the proper channel count and mask.
+class RemixBufferProvider : public CopyBufferProvider {
+public:
+    RemixBufferProvider(audio_channel_mask_t inputChannelMask,
+            audio_channel_mask_t outputChannelMask, audio_format_t format,
+            size_t bufferFrameCount);
+    //Overrides
+    virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+protected:
+    const audio_format_t mFormat;
+    const size_t         mSampleSize;
+    const size_t         mInputChannels;
+    const size_t         mOutputChannels;
+    int8_t               mIdxAry[sizeof(uint32_t) * 8]; // 32 bits => channel indices
+};
+
+// ReformatBufferProvider derives from CopyBufferProvider to convert the input data
+// to an acceptable mixer input format type.
+class ReformatBufferProvider : public CopyBufferProvider {
+public:
+    ReformatBufferProvider(int32_t channelCount,
+            audio_format_t inputFormat, audio_format_t outputFormat,
+            size_t bufferFrameCount);
+    virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+protected:
+    const uint32_t       mChannelCount;
+    const audio_format_t mInputFormat;
+    const audio_format_t mOutputFormat;
+};
+
+// TimestretchBufferProvider derives from PassthruBufferProvider for time stretching
+class TimestretchBufferProvider : public PassthruBufferProvider {
+public:
+    TimestretchBufferProvider(int32_t channelCount,
+            audio_format_t format, uint32_t sampleRate, float speed, float pitch);
+    virtual ~TimestretchBufferProvider();
+
+    // Overrides AudioBufferProvider methods
+    virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+    virtual void releaseBuffer(Buffer* buffer);
+
+    // Overrides PassthruBufferProvider
+    virtual void reset();
+
+    virtual status_t setPlaybackRate(float speed, float pitch);
+
+    // processes frames
+    // dstBuffer is where to place the data
+    // dstFrames [in/out] is the desired frames (return with actual placed in buffer)
+    // srcBuffer is the source data
+    // srcFrames [in/out] is the available source frames (return with consumed)
+    virtual void processFrames(void *dstBuffer, size_t *dstFrames,
+            const void *srcBuffer, size_t *srcFrames);
+
+protected:
+    const uint32_t       mChannelCount;
+    const audio_format_t mFormat;
+    const uint32_t       mSampleRate; // const for now (TODO change this)
+    const size_t         mFrameSize;
+    float                mSpeed;
+    float                mPitch;
+
+private:
+    AudioBufferProvider::Buffer mBuffer;
+    size_t               mLocalBufferFrameCount;
+    void                *mLocalBufferData;
+    size_t               mRemaining;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_BUFFER_PROVIDERS_H
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index efbdcff..834947f 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -200,26 +200,17 @@
                     status = BAD_VALUE;
                     goto exit;
                 }
-                // limit to connections between devices and input streams for HAL before 3.0
-                if (patch->sinks[i].ext.mix.hw_module == srcModule &&
-                        (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
-                        (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
-                    ALOGW("createAudioPatch() invalid sink type %d for device source",
-                          patch->sinks[i].type);
-                    status = BAD_VALUE;
-                    goto exit;
-                }
             }
 
-            if (patch->sinks[0].ext.device.hw_module != srcModule) {
-                // limit to device to device connection if not on same hw module
-                if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
-                    ALOGW("createAudioPatch() invalid sink type for cross hw module");
-                    status = INVALID_OPERATION;
-                    goto exit;
-                }
-                // special case num sources == 2 -=> reuse an exiting output mix to connect to the
-                // sink
+            // manage patches requiring a software bridge
+            // - Device to device AND
+            //    - source HW module != destination HW module OR
+            //    - audio HAL version < 3.0
+            //    - special patch request with 2 sources (reuse one existing output mix)
+            if ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+                    ((patch->sinks[0].ext.device.hw_module != srcModule) ||
+                    (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) ||
+                    (patch->num_sources == 2))) {
                 if (patch->num_sources == 2) {
                     if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX ||
                             patch->sinks[0].ext.device.hw_module !=
@@ -304,6 +295,11 @@
                                                                &halHandle);
                     }
                 } else {
+                    if (patch->sinks[0].type != AUDIO_PORT_TYPE_MIX) {
+                        status = INVALID_OPERATION;
+                        goto exit;
+                    }
+
                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
                                                                     patch->sinks[0].ext.mix.handle);
                     if (thread == 0) {
@@ -472,6 +468,7 @@
     // 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,
                                            sampleRate,
                                            outChannelMask,
                                            format,
@@ -578,8 +575,8 @@
                 break;
             }
 
-            if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
-                    patch->sinks[0].ext.device.hw_module != srcModule) {
+            if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE ||
+                    removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
                 clearPatchConnections(removedPatch);
                 break;
             }
@@ -693,5 +690,4 @@
     return NO_ERROR;
 }
 
-
 } // namespace android
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 45df6a9..c51021b 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -298,6 +298,7 @@
 public:
 
                         PatchTrack(PlaybackThread *playbackThread,
+                                   audio_stream_type_t streamType,
                                    uint32_t sampleRate,
                                    audio_channel_mask_t channelMask,
                                    audio_format_t format,
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 204a9d6..25d6d95 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -34,6 +34,7 @@
                                 IAudioFlinger::track_flags_t flags,
                                 track_type type);
     virtual             ~RecordTrack();
+    virtual status_t    initCheck() const;
 
     virtual status_t    start(AudioSystem::sync_event_t event, int triggerSession);
     virtual void        stop();
@@ -66,21 +67,6 @@
 
     bool                mOverflow;  // overflow on most recent attempt to fill client buffer
 
-           // updated by RecordThread::readInputParameters_l()
-            AudioResampler                      *mResampler;
-
-            // interleaved stereo pairs of fixed-point Q4.27
-            int32_t                             *mRsmpOutBuffer;
-            // current allocated frame count for the above, which may be larger than needed
-            size_t                              mRsmpOutFrameCount;
-
-            size_t                              mRsmpInUnrel;   // unreleased frames remaining from
-                                                                // most recent getNextBuffer
-                                                                // for debug only
-
-            // rolling counter that is never cleared
-            int32_t                             mRsmpInFront;   // next available frame
-
             AudioBufferProvider::Buffer mSink;  // references client's buffer sink in shared memory
 
             // sync event triggering actual audio capture. Frames read before this event will
@@ -93,7 +79,10 @@
             ssize_t                             mFramesToDrop;
 
             // used by resampler to find source frames
-            ResamplerBufferProvider *mResamplerBufferProvider;
+            ResamplerBufferProvider            *mResamplerBufferProvider;
+
+            // used by the record thread to convert frames to proper destination format
+            RecordBufferConverter              *mRecordBufferConverter;
 };
 
 // playback track, used by PatchPanel
diff --git a/services/audioflinger/SpdifStreamOut.cpp b/services/audioflinger/SpdifStreamOut.cpp
index d23588e..45b541a 100644
--- a/services/audioflinger/SpdifStreamOut.cpp
+++ b/services/audioflinger/SpdifStreamOut.cpp
@@ -32,10 +32,12 @@
  * If the AudioFlinger is processing encoded data and the HAL expects
  * PCM then we need to wrap the data in an SPDIF wrapper.
  */
-SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags)
+SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev,
+            audio_output_flags_t flags,
+            audio_format_t format)
         : AudioStreamOut(dev,flags)
         , mRateMultiplier(1)
-        , mSpdifEncoder(this)
+        , mSpdifEncoder(this, format)
         , mRenderPositionHal(0)
         , mPreviousHalPosition32(0)
 {
@@ -49,15 +51,15 @@
 {
     struct audio_config customConfig = *config;
 
-    customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
-    customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-
     // Some data bursts run at a higher sample rate.
+    // TODO Move this into the audio_utils as a static method.
     switch(config->format) {
         case AUDIO_FORMAT_E_AC3:
             mRateMultiplier = 4;
             break;
         case AUDIO_FORMAT_AC3:
+        case AUDIO_FORMAT_DTS:
+        case AUDIO_FORMAT_DTS_HD:
             mRateMultiplier = 1;
             break;
         default:
@@ -67,6 +69,9 @@
     }
     customConfig.sample_rate = config->sample_rate * mRateMultiplier;
 
+    customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+    customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
     // Always print this because otherwise it could be very confusing if the
     // HAL and AudioFlinger are using different formats.
     // Print before open() because HAL may modify customConfig.
diff --git a/services/audioflinger/SpdifStreamOut.h b/services/audioflinger/SpdifStreamOut.h
index cb82ac7..d81c064 100644
--- a/services/audioflinger/SpdifStreamOut.h
+++ b/services/audioflinger/SpdifStreamOut.h
@@ -38,7 +38,8 @@
 class SpdifStreamOut : public AudioStreamOut {
 public:
 
-    SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags);
+    SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags,
+            audio_format_t format);
 
     virtual ~SpdifStreamOut() { }
 
@@ -77,8 +78,9 @@
     class MySPDIFEncoder : public SPDIFEncoder
     {
     public:
-        MySPDIFEncoder(SpdifStreamOut *spdifStreamOut)
-          : mSpdifStreamOut(spdifStreamOut)
+        MySPDIFEncoder(SpdifStreamOut *spdifStreamOut, audio_format_t format)
+          :  SPDIFEncoder(format)
+          , mSpdifStreamOut(spdifStreamOut)
         {
         }
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4efb3d7..b30fd20 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -86,7 +86,13 @@
 #define ALOGVV(a...) do { } while(0)
 #endif
 
+// TODO: Move these macro/inlines to a header file.
 #define max(a, b) ((a) > (b) ? (a) : (b))
+template <typename T>
+static inline T min(const T& a, const T& b)
+{
+    return a < b ? a : b;
+}
 
 namespace android {
 
@@ -1602,13 +1608,19 @@
     // If you change this calculation, also review the start threshold which is related.
     if (!(*flags & IAudioFlinger::TRACK_FAST)
             && audio_is_linear_pcm(format) && sharedBuffer == 0) {
+        // this must match AudioTrack.cpp calculateMinFrameCount().
+        // TODO: Move to a common library
         uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
         uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
         if (minBufCount < 2) {
             minBufCount = 2;
         }
+        // For normal mixing tracks, if speed is > 1.0f (normal), AudioTrack
+        // or the client should compute and pass in a larger buffer request.
         size_t minFrameCount =
-                minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate);
+                minBufCount * sourceFramesNeededWithTimestretch(
+                        sampleRate, mNormalFrameCount,
+                        mSampleRate, AUDIO_TIMESTRETCH_SPEED_NORMAL /*speed*/);
         if (frameCount < minFrameCount) { // including frameCount == 0
             frameCount = minFrameCount;
         }
@@ -3586,21 +3598,17 @@
         // hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
         // during last round
         size_t desiredFrames;
-        uint32_t sr = track->sampleRate();
-        if (sr == mSampleRate) {
-            desiredFrames = mNormalFrameCount;
-        } else {
-            desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate);
-            // add frames already consumed but not yet released by the resampler
-            // because mAudioTrackServerProxy->framesReady() will include these frames
-            desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
-#if 0
-            // the minimum track buffer size is normally twice the number of frames necessary
-            // to fill one buffer and the resampler should not leave more than one buffer worth
-            // of unreleased frames after each pass, but just in case...
-            ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
-#endif
-        }
+        const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
+        float speed, pitch;
+        track->mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
+
+        desiredFrames = sourceFramesNeededWithTimestretch(
+                sampleRate, mNormalFrameCount, mSampleRate, speed);
+        // TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.
+        // add frames already consumed but not yet released by the resampler
+        // because mAudioTrackServerProxy->framesReady() will include these frames
+        desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+
         uint32_t minFrames = 1;
         if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
                 (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
@@ -3763,6 +3771,17 @@
                 AudioMixer::RESAMPLE,
                 AudioMixer::SAMPLE_RATE,
                 (void *)(uintptr_t)reqSampleRate);
+
+            // set the playback rate as an float array {speed, pitch}
+            float playbackRate[2];
+            track->mAudioTrackServerProxy->getPlaybackRate(
+                    &playbackRate[0] /*speed*/, &playbackRate[1] /*pitch*/);
+            mAudioMixer->setParameter(
+                name,
+                AudioMixer::TIMESTRETCH,
+                AudioMixer::PLAYBACK_RATE,
+                playbackRate);
+
             /*
              * Select the appropriate output buffer for the track.
              *
@@ -5290,7 +5309,6 @@
     // FIXME mNormalSource
 }
 
-
 AudioFlinger::RecordThread::~RecordThread()
 {
     if (mFastCapture != 0) {
@@ -5594,6 +5612,9 @@
                 continue;
             }
 
+            // TODO: This code probably should be moved to RecordTrack.
+            // TODO: Update the activeTrack buffer converter in case of reconfigure.
+
             enum {
                 OVERRUN_UNKNOWN,
                 OVERRUN_TRUE,
@@ -5608,131 +5629,28 @@
                 size_t framesOut = activeTrack->mSink.frameCount;
                 LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0));
 
-                int32_t front = activeTrack->mRsmpInFront;
-                ssize_t filled = rear - front;
+                // check available frames and handle overrun conditions
+                // if the record track isn't draining fast enough.
+                bool hasOverrun;
                 size_t framesIn;
-
-                if (filled < 0) {
-                    // should not happen, but treat like a massive overrun and re-sync
-                    framesIn = 0;
-                    activeTrack->mRsmpInFront = rear;
-                    overrun = OVERRUN_TRUE;
-                } else if ((size_t) filled <= mRsmpInFrames) {
-                    framesIn = (size_t) filled;
-                } else {
-                    // client is not keeping up with server, but give it latest data
-                    framesIn = mRsmpInFrames;
-                    activeTrack->mRsmpInFront = front = rear - framesIn;
+                activeTrack->mResamplerBufferProvider->sync(&framesIn, &hasOverrun);
+                if (hasOverrun) {
                     overrun = OVERRUN_TRUE;
                 }
-
                 if (framesOut == 0 || framesIn == 0) {
                     break;
                 }
 
-                if (activeTrack->mResampler == NULL) {
-                    // no resampling
-                    if (framesIn > framesOut) {
-                        framesIn = framesOut;
-                    } else {
-                        framesOut = framesIn;
-                    }
-                    int8_t *dst = activeTrack->mSink.i8;
-                    while (framesIn > 0) {
-                        front &= mRsmpInFramesP2 - 1;
-                        size_t part1 = mRsmpInFramesP2 - front;
-                        if (part1 > framesIn) {
-                            part1 = framesIn;
-                        }
-                        int8_t *src = (int8_t *)mRsmpInBuffer + (front * mFrameSize);
-                        if (mChannelCount == activeTrack->mChannelCount) {
-                            memcpy(dst, src, part1 * mFrameSize);
-                        } else if (mChannelCount == 1) {
-                            upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src,
-                                    part1);
-                        } else {
-                            downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
-                                    (const int16_t *)src, part1);
-                        }
-                        dst += part1 * activeTrack->mFrameSize;
-                        front += part1;
-                        framesIn -= part1;
-                    }
-                    activeTrack->mRsmpInFront += framesOut;
-
-                } else {
-                    // resampling
-                    // FIXME framesInNeeded should really be part of resampler API, and should
-                    //       depend on the SRC ratio
-                    //       to keep mRsmpInBuffer full so resampler always has sufficient input
-                    size_t framesInNeeded;
-                    // FIXME only re-calculate when it changes, and optimize for common ratios
-                    // Do not precompute in/out because floating point is not associative
-                    // e.g. a*b/c != a*(b/c).
-                    const double in(mSampleRate);
-                    const double out(activeTrack->mSampleRate);
-                    framesInNeeded = ceil(framesOut * in / out) + 1;
-                    ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g",
-                                framesInNeeded, framesOut, in / out);
-                    // Although we theoretically have framesIn in circular buffer, some of those are
-                    // unreleased frames, and thus must be discounted for purpose of budgeting.
-                    size_t unreleased = activeTrack->mRsmpInUnrel;
-                    framesIn = framesIn > unreleased ? framesIn - unreleased : 0;
-                    if (framesIn < framesInNeeded) {
-                        ALOGV("not enough to resample: have %u frames in but need %u in to "
-                                "produce %u out given in/out ratio of %.4g",
-                                framesIn, framesInNeeded, framesOut, in / out);
-                        size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * out / in) : 0;
-                        LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut);
-                        if (newFramesOut == 0) {
-                            break;
-                        }
-                        framesInNeeded = ceil(newFramesOut * in / out) + 1;
-                        ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g",
-                                framesInNeeded, newFramesOut, out / in);
-                        LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded);
-                        ALOGV("success 2: have %u frames in and need %u in to produce %u out "
-                              "given in/out ratio of %.4g",
-                              framesIn, framesInNeeded, newFramesOut, in / out);
-                        framesOut = newFramesOut;
-                    } else {
-                        ALOGV("success 1: have %u in and need %u in to produce %u out "
-                            "given in/out ratio of %.4g",
-                            framesIn, framesInNeeded, framesOut, in / out);
-                    }
-
-                    // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
-                    if (activeTrack->mRsmpOutFrameCount < framesOut) {
-                        // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
-                        delete[] activeTrack->mRsmpOutBuffer;
-                        // resampler always outputs stereo
-                        activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2];
-                        activeTrack->mRsmpOutFrameCount = framesOut;
-                    }
-
-                    // resampler accumulates, but we only have one source track
-                    memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
-                    activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut,
-                            // FIXME how about having activeTrack implement this interface itself?
-                            activeTrack->mResamplerBufferProvider
-                            /*this*/ /* AudioBufferProvider* */);
-                    // ditherAndClamp() works as long as all buffers returned by
-                    // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
-                    if (activeTrack->mChannelCount == 1) {
-                        // temporarily type pun mRsmpOutBuffer from Q4.27 to int16_t
-                        ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer,
-                                framesOut);
-                        // the resampler always outputs stereo samples:
-                        // do post stereo to mono conversion
-                        downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16,
-                                (const int16_t *)activeTrack->mRsmpOutBuffer, framesOut);
-                    } else {
-                        ditherAndClamp((int32_t *)activeTrack->mSink.raw,
-                                activeTrack->mRsmpOutBuffer, framesOut);
-                    }
-                    // now done with mRsmpOutBuffer
-
-                }
+                // Don't allow framesOut to be larger than what is possible with resampling
+                // from framesIn.
+                // This isn't strictly necessary but helps limit buffer resizing in
+                // RecordBufferConverter.  TODO: remove when no longer needed.
+                framesOut = min(framesOut,
+                        destinationFramesPossible(
+                                framesIn, mSampleRate, activeTrack->mSampleRate));
+                // process frames from the RecordThread buffer provider to the RecordTrack buffer
+                framesOut = activeTrack->mRecordBufferConverter->convert(
+                        activeTrack->mSink.raw, activeTrack->mResamplerBufferProvider, framesOut);
 
                 if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) {
                     overrun = OVERRUN_FALSE;
@@ -6041,12 +5959,9 @@
         // was initialized to some value closer to the thread's mRsmpInFront, then the track could
         // see previously buffered data before it called start(), but with greater risk of overrun.
 
-        recordTrack->mRsmpInFront = mRsmpInRear;
-        recordTrack->mRsmpInUnrel = 0;
-        // FIXME why reset?
-        if (recordTrack->mResampler != NULL) {
-            recordTrack->mResampler->reset();
-        }
+        recordTrack->mResamplerBufferProvider->reset();
+        // clear any converter state as new data will be discontinuous
+        recordTrack->mRecordBufferConverter->reset();
         recordTrack->mState = TrackBase::STARTING_2;
         // signal thread to start
         mWaitWorkCV.broadcast();
@@ -6222,12 +6137,52 @@
     write(fd, result.string(), result.size());
 }
 
+
+void AudioFlinger::RecordThread::ResamplerBufferProvider::reset()
+{
+    sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
+    RecordThread *recordThread = (RecordThread *) threadBase.get();
+    mRsmpInFront = recordThread->mRsmpInRear;
+    mRsmpInUnrel = 0;
+}
+
+void AudioFlinger::RecordThread::ResamplerBufferProvider::sync(
+        size_t *framesAvailable, bool *hasOverrun)
+{
+    sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
+    RecordThread *recordThread = (RecordThread *) threadBase.get();
+    const int32_t rear = recordThread->mRsmpInRear;
+    const int32_t front = mRsmpInFront;
+    const ssize_t filled = rear - front;
+
+    size_t framesIn;
+    bool overrun = false;
+    if (filled < 0) {
+        // should not happen, but treat like a massive overrun and re-sync
+        framesIn = 0;
+        mRsmpInFront = rear;
+        overrun = true;
+    } else if ((size_t) filled <= recordThread->mRsmpInFrames) {
+        framesIn = (size_t) filled;
+    } else {
+        // client is not keeping up with server, but give it latest data
+        framesIn = recordThread->mRsmpInFrames;
+        mRsmpInFront = /* front = */ rear - framesIn;
+        overrun = true;
+    }
+    if (framesAvailable != NULL) {
+        *framesAvailable = framesIn;
+    }
+    if (hasOverrun != NULL) {
+        *hasOverrun = overrun;
+    }
+}
+
 // AudioBufferProvider interface
 status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
         AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
 {
-    RecordTrack *activeTrack = mRecordTrack;
-    sp<ThreadBase> threadBase = activeTrack->mThread.promote();
+    sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
     if (threadBase == 0) {
         buffer->frameCount = 0;
         buffer->raw = NULL;
@@ -6235,7 +6190,7 @@
     }
     RecordThread *recordThread = (RecordThread *) threadBase.get();
     int32_t rear = recordThread->mRsmpInRear;
-    int32_t front = activeTrack->mRsmpInFront;
+    int32_t front = mRsmpInFront;
     ssize_t filled = rear - front;
     // FIXME should not be P2 (don't want to increase latency)
     // FIXME if client not keeping up, discard
@@ -6252,17 +6207,16 @@
         part1 = ask;
     }
     if (part1 == 0) {
-        // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
-        LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved");
+        // out of data is fine since the resampler will return a short-count.
         buffer->raw = NULL;
         buffer->frameCount = 0;
-        activeTrack->mRsmpInUnrel = 0;
+        mRsmpInUnrel = 0;
         return NOT_ENOUGH_DATA;
     }
 
     buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount;
     buffer->frameCount = part1;
-    activeTrack->mRsmpInUnrel = part1;
+    mRsmpInUnrel = part1;
     return NO_ERROR;
 }
 
@@ -6270,18 +6224,197 @@
 void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer(
         AudioBufferProvider::Buffer* buffer)
 {
-    RecordTrack *activeTrack = mRecordTrack;
     size_t stepCount = buffer->frameCount;
     if (stepCount == 0) {
         return;
     }
-    ALOG_ASSERT(stepCount <= activeTrack->mRsmpInUnrel);
-    activeTrack->mRsmpInUnrel -= stepCount;
-    activeTrack->mRsmpInFront += stepCount;
+    ALOG_ASSERT(stepCount <= mRsmpInUnrel);
+    mRsmpInUnrel -= stepCount;
+    mRsmpInFront += stepCount;
     buffer->raw = NULL;
     buffer->frameCount = 0;
 }
 
+AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
+        audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+        uint32_t srcSampleRate,
+        audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+        uint32_t dstSampleRate) :
+            mSrcChannelMask(AUDIO_CHANNEL_INVALID), // updateParameters will set following vars
+            // mSrcFormat
+            // mSrcSampleRate
+            // mDstChannelMask
+            // mDstFormat
+            // mDstSampleRate
+            // mSrcChannelCount
+            // mDstChannelCount
+            // mDstFrameSize
+            mBuf(NULL), mBufFrames(0), mBufFrameSize(0),
+            mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0)
+{
+    (void)updateParameters(srcChannelMask, srcFormat, srcSampleRate,
+            dstChannelMask, dstFormat, dstSampleRate);
+}
+
+AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() {
+    free(mBuf);
+    delete mResampler;
+    free(mRsmpOutBuffer);
+}
+
+size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
+        AudioBufferProvider *provider, size_t frames)
+{
+    if (mSrcSampleRate == mDstSampleRate) {
+        ALOGVV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
+                mSrcSampleRate, mSrcFormat, mDstFormat);
+
+        AudioBufferProvider::Buffer buffer;
+        for (size_t i = frames; i > 0; ) {
+            buffer.frameCount = i;
+            status_t status = provider->getNextBuffer(&buffer, 0);
+            if (status != OK || buffer.frameCount == 0) {
+                frames -= i; // cannot fill request.
+                break;
+            }
+            // convert to destination buffer
+            convert(dst, buffer.raw, buffer.frameCount);
+
+            dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize;
+            i -= buffer.frameCount;
+            provider->releaseBuffer(&buffer);
+        }
+    } else {
+         ALOGVV("RESAMPLING mSrcSampleRate:%u mDstSampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
+                 mSrcSampleRate, mDstSampleRate, mSrcFormat, mDstFormat);
+
+        // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
+        if (mRsmpOutFrameCount < frames) {
+            // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
+            free(mRsmpOutBuffer);
+            // resampler always outputs stereo (FOR NOW)
+            (void)posix_memalign(&mRsmpOutBuffer, 32, frames * FCC_2 * sizeof(int32_t) /*Q4.27*/);
+            mRsmpOutFrameCount = frames;
+        }
+        // resampler accumulates, but we only have one source track
+        memset(mRsmpOutBuffer, 0, frames * FCC_2 * sizeof(int32_t));
+        frames = mResampler->resample((int32_t*)mRsmpOutBuffer, frames, provider);
+
+        // convert to destination buffer
+        convert(dst, mRsmpOutBuffer, frames);
+    }
+    return frames;
+}
+
+status_t AudioFlinger::RecordThread::RecordBufferConverter::updateParameters(
+        audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+        uint32_t srcSampleRate,
+        audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+        uint32_t dstSampleRate)
+{
+    // quick evaluation if there is any change.
+    if (mSrcFormat == srcFormat
+            && mSrcChannelMask == srcChannelMask
+            && mSrcSampleRate == srcSampleRate
+            && mDstFormat == dstFormat
+            && mDstChannelMask == dstChannelMask
+            && mDstSampleRate == dstSampleRate) {
+        return NO_ERROR;
+    }
+
+    const bool valid =
+            audio_is_input_channel(srcChannelMask)
+            && audio_is_input_channel(dstChannelMask)
+            && audio_is_valid_format(srcFormat) && audio_is_linear_pcm(srcFormat)
+            && audio_is_valid_format(dstFormat) && audio_is_linear_pcm(dstFormat)
+            && (srcSampleRate <= dstSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX)
+            ; // no upsampling checks for now
+    if (!valid) {
+        return BAD_VALUE;
+    }
+
+    mSrcFormat = srcFormat;
+    mSrcChannelMask = srcChannelMask;
+    mSrcSampleRate = srcSampleRate;
+    mDstFormat = dstFormat;
+    mDstChannelMask = dstChannelMask;
+    mDstSampleRate = dstSampleRate;
+
+    // compute derived parameters
+    mSrcChannelCount = audio_channel_count_from_in_mask(srcChannelMask);
+    mDstChannelCount = audio_channel_count_from_in_mask(dstChannelMask);
+    mDstFrameSize = mDstChannelCount * audio_bytes_per_sample(mDstFormat);
+
+    // do we need a format buffer?
+    if (mSrcFormat != mDstFormat && mDstChannelCount != mSrcChannelCount) {
+        mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat);
+    } else {
+        mBufFrameSize = 0;
+    }
+    mBufFrames = 0; // force the buffer to be resized.
+
+    // do we need to resample?
+    if (mSrcSampleRate != mDstSampleRate) {
+        if (mResampler != NULL) {
+            delete mResampler;
+        }
+        mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
+                mSrcChannelCount, mDstSampleRate); // may seem confusing...
+        mResampler->setSampleRate(mSrcSampleRate);
+        mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+    }
+    return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convert(
+        void *dst, /*const*/ void *src, size_t frames)
+{
+    // check if a memcpy will do
+    if (mResampler == NULL
+            && mSrcChannelCount == mDstChannelCount
+            && mSrcFormat == mDstFormat) {
+        memcpy(dst, src,
+                frames * mDstChannelCount * audio_bytes_per_sample(mDstFormat));
+        return;
+    }
+    // reallocate buffer if needed
+    if (mBufFrameSize != 0 && mBufFrames < frames) {
+        free(mBuf);
+        mBufFrames = frames;
+        (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+    }
+    // do processing
+    if (mResampler != NULL) {
+        // src channel count is always >= 2.
+        void *dstBuf = mBuf != NULL ? mBuf : dst;
+        // ditherAndClamp() works as long as all buffers returned by
+        // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
+        if (mDstChannelCount == 1) {
+            // the resampler always outputs stereo samples.
+            // FIXME: this rewrites back into src
+            ditherAndClamp((int32_t *)src, (const int32_t *)src, frames);
+            downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf,
+                    (const int16_t *)src, frames);
+        } else {
+            ditherAndClamp((int32_t *)dstBuf, (const int32_t *)src, frames);
+        }
+    } else if (mSrcChannelCount != mDstChannelCount) {
+        void *dstBuf = mBuf != NULL ? mBuf : dst;
+        if (mSrcChannelCount == 1) {
+            upmix_to_stereo_i16_from_mono_i16((int16_t *)dstBuf, (const int16_t *)src,
+                    frames);
+        } else {
+            downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf,
+                    (const int16_t *)src, frames);
+        }
+    }
+    if (mSrcFormat != mDstFormat) {
+        void *srcBuf = mBuf != NULL ? mBuf : src;
+        memcpy_by_audio_format(dst, mDstFormat, srcBuf, mSrcFormat,
+                frames * mDstChannelCount);
+    }
+}
+
 bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
                                                         status_t& status)
 {
@@ -6303,7 +6436,7 @@
         reconfig = true;
     }
     if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
-        if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+        if (!audio_is_linear_pcm((audio_format_t) value)) {
             status = BAD_VALUE;
         } else {
             reqFormat = (audio_format_t) value;
@@ -6377,10 +6510,10 @@
         }
         if (reconfig) {
             if (status == BAD_VALUE &&
-                reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
-                reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+                audio_is_linear_pcm(mInput->stream->common.get_format(&mInput->stream->common)) &&
+                audio_is_linear_pcm(reqFormat) &&
                 (mInput->stream->common.get_sample_rate(&mInput->stream->common)
-                        <= (2 * samplingRate)) &&
+                        <= (AUDIO_RESAMPLER_DOWN_RATIO_MAX * samplingRate)) &&
                 audio_channel_count_from_in_mask(
                         mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 &&
                 (channelMask == AUDIO_CHANNEL_IN_MONO ||
@@ -6451,6 +6584,8 @@
     // The value is somewhat arbitrary, and could probably be even larger.
     // A larger value should allow more old data to be read after a track calls start(),
     // without increasing latency.
+    //
+    // Note this is independent of the maximum downsampling ratio permitted for capture.
     mRsmpInFrames = mFrameCount * 7;
     mRsmpInFramesP2 = roundup(mRsmpInFrames);
     delete[] mRsmpInBuffer;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index d600ea9..27bc56b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1036,17 +1036,127 @@
 public:
 
     class RecordTrack;
+
+    /* The ResamplerBufferProvider is used to retrieve recorded input data from the
+     * RecordThread.  It maintains local state on the relative position of the read
+     * position of the RecordTrack compared with the RecordThread.
+     */
     class ResamplerBufferProvider : public AudioBufferProvider
-                        // derives from AudioBufferProvider interface for use by resampler
     {
     public:
-        ResamplerBufferProvider(RecordTrack* recordTrack) : mRecordTrack(recordTrack) { }
+        ResamplerBufferProvider(RecordTrack* recordTrack) :
+            mRecordTrack(recordTrack),
+            mRsmpInUnrel(0), mRsmpInFront(0) { }
         virtual ~ResamplerBufferProvider() { }
+
+        // called to set the ResamplerBufferProvider to head of the RecordThread data buffer,
+        // skipping any previous data read from the hal.
+        virtual void reset();
+
+        /* Synchronizes RecordTrack position with the RecordThread.
+         * Calculates available frames and handle overruns if the RecordThread
+         * has advanced faster than the ResamplerBufferProvider has retrieved data.
+         * TODO: why not do this for every getNextBuffer?
+         *
+         * Parameters
+         * framesAvailable:  pointer to optional output size_t to store record track
+         *                   frames available.
+         *      hasOverrun:  pointer to optional boolean, returns true if track has overrun.
+         */
+
+        virtual void sync(size_t *framesAvailable = NULL, bool *hasOverrun = NULL);
+
         // AudioBufferProvider interface
         virtual status_t    getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
         virtual void        releaseBuffer(AudioBufferProvider::Buffer* buffer);
     private:
         RecordTrack * const mRecordTrack;
+        size_t              mRsmpInUnrel;   // unreleased frames remaining from
+                                            // most recent getNextBuffer
+                                            // for debug only
+        int32_t             mRsmpInFront;   // next available frame
+                                            // rolling counter that is never cleared
+    };
+
+    /* The RecordBufferConverter is used for format, channel, and sample rate
+     * conversion for a RecordTrack.
+     *
+     * TODO: Self contained, so move to a separate file later.
+     *
+     * RecordBufferConverter uses the convert() method rather than exposing a
+     * buffer provider interface; this is to save a memory copy.
+     */
+    class RecordBufferConverter
+    {
+    public:
+        RecordBufferConverter(
+                audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+                uint32_t srcSampleRate,
+                audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+                uint32_t dstSampleRate);
+
+        ~RecordBufferConverter();
+
+        /* Converts input data from an AudioBufferProvider by format, channelMask,
+         * and sampleRate to a destination buffer.
+         *
+         * Parameters
+         *      dst:  buffer to place the converted data.
+         * provider:  buffer provider to obtain source data.
+         *   frames:  number of frames to convert
+         *
+         * Returns the number of frames converted.
+         */
+        size_t convert(void *dst, AudioBufferProvider *provider, size_t frames);
+
+        // returns NO_ERROR if constructor was successful
+        status_t initCheck() const {
+            // mSrcChannelMask set on successful updateParameters
+            return mSrcChannelMask != AUDIO_CHANNEL_INVALID ? NO_ERROR : NO_INIT;
+        }
+
+        // allows dynamic reconfigure of all parameters
+        status_t updateParameters(
+                audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+                uint32_t srcSampleRate,
+                audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+                uint32_t dstSampleRate);
+
+        // called to reset resampler buffers on record track discontinuity
+        void reset() {
+            if (mResampler != NULL) {
+                mResampler->reset();
+            }
+        }
+
+    private:
+        // internal convert function for format and channel mask.
+        void convert(void *dst, /*const*/ void *src, size_t frames);
+
+        // user provided information
+        audio_channel_mask_t mSrcChannelMask;
+        audio_format_t       mSrcFormat;
+        uint32_t             mSrcSampleRate;
+        audio_channel_mask_t mDstChannelMask;
+        audio_format_t       mDstFormat;
+        uint32_t             mDstSampleRate;
+
+        // derived information
+        uint32_t             mSrcChannelCount;
+        uint32_t             mDstChannelCount;
+        size_t               mDstFrameSize;
+
+        // format conversion buffer
+        void                *mBuf;
+        size_t               mBufFrames;
+        size_t               mBufFrameSize;
+
+        // resampler info
+        AudioResampler      *mResampler;
+        // interleaved stereo pairs of fixed-point Q4.27 or float depending on resampler
+        void                *mRsmpOutBuffer;
+        // current allocated frame count for the above, which may be larger than needed
+        size_t               mRsmpOutFrameCount;
     };
 
 #include "RecordTracks.h"
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index dc9f249..da2d634 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -903,9 +903,14 @@
             mPreviousTimestampValid = false;
             return INVALID_OPERATION;
         }
+        // FIXME Not accurate under dynamic changes of sample rate and speed.
+        // Do not use track's mSampleRate as it is not current for mixer tracks.
+        uint32_t sampleRate = mAudioTrackServerProxy->getSampleRate();
+        float speed, pitch;
+        mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
         uint32_t unpresentedFrames =
-                ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
-                playbackThread->mSampleRate;
+                ((double) playbackThread->mLatchQ.mUnpresentedFrames * sampleRate * speed)
+                / playbackThread->mSampleRate;
         // FIXME Since we're using a raw pointer as the key, it is theoretically possible
         //       for a brand new track to share the same address as a recently destroyed
         //       track, and thus for us to get the frames released of the wrong track.
@@ -1861,13 +1866,14 @@
 
 
 AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThread,
+                                                     audio_stream_type_t streamType,
                                                      uint32_t sampleRate,
                                                      audio_channel_mask_t channelMask,
                                                      audio_format_t format,
                                                      size_t frameCount,
                                                      void *buffer,
                                                      IAudioFlinger::track_flags_t flags)
-    :   Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
+    :   Track(playbackThread, NULL, streamType,
               sampleRate, format, channelMask, frameCount,
               buffer, 0, 0, getuid(), flags, TYPE_PATCH),
               mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
@@ -1989,29 +1995,30 @@
                           ((flags & IAudioFlinger::TRACK_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
                           ((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
                   type),
-        mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
-        // See real initialization of mRsmpInFront at RecordThread::start()
-        mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
+        mOverflow(false),
+        mFramesToDrop(0)
 {
     if (mCblk == NULL) {
         return;
     }
 
+    mRecordBufferConverter = new RecordBufferConverter(
+            thread->mChannelMask, thread->mFormat, thread->mSampleRate,
+            channelMask, format, sampleRate);
+    // Check if the RecordBufferConverter construction was successful.
+    // If not, don't continue with construction.
+    //
+    // NOTE: It would be extremely rare that the record track cannot be created
+    // for the current device, but a pending or future device change would make
+    // the record track configuration valid.
+    if (mRecordBufferConverter->initCheck() != NO_ERROR) {
+        ALOGE("RecordTrack unable to create record buffer converter");
+        return;
+    }
+
     mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
                                               mFrameSize, !isExternalTrack());
-
-    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
-    // FIXME I don't understand either of the channel count checks
-    if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 &&
-            channelCount <= FCC_2) {
-        // sink SR
-        mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
-                thread->mChannelCount, sampleRate);
-        // source SR
-        mResampler->setSampleRate(thread->mSampleRate);
-        mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
-        mResamplerBufferProvider = new ResamplerBufferProvider(this);
-    }
+    mResamplerBufferProvider = new ResamplerBufferProvider(this);
 
     if (flags & IAudioFlinger::TRACK_FAST) {
         ALOG_ASSERT(thread->mFastTrackAvail);
@@ -2022,11 +2029,19 @@
 AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
 {
     ALOGV("%s", __func__);
-    delete mResampler;
-    delete[] mRsmpOutBuffer;
+    delete mRecordBufferConverter;
     delete mResamplerBufferProvider;
 }
 
+status_t AudioFlinger::RecordThread::RecordTrack::initCheck() const
+{
+    status_t status = TrackBase::initCheck();
+    if (status == NO_ERROR && mServerProxy == 0) {
+        status = BAD_VALUE;
+    }
+    return status;
+}
+
 // AudioBufferProvider interface
 status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
         int64_t pts __unused)
diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk
index 8604ef5..76997be 100644
--- a/services/audioflinger/tests/Android.mk
+++ b/services/audioflinger/tests/Android.mk
@@ -39,6 +39,7 @@
 LOCAL_SRC_FILES:= \
 	test-mixer.cpp \
 	../AudioMixer.cpp.arm \
+	../BufferProviders.cpp
 
 LOCAL_C_INCLUDES := \
 	$(call include-path-for, audio-effects) \
diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp
index d6217ba..9e375db 100644
--- a/services/audioflinger/tests/resampler_tests.cpp
+++ b/services/audioflinger/tests/resampler_tests.cpp
@@ -48,7 +48,10 @@
         if (thisFrames == 0 || thisFrames > outputFrames - i) {
             thisFrames = outputFrames - i;
         }
-        resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
+        size_t framesResampled = resampler->resample(
+                (int32_t*) output + channels*i, thisFrames, provider);
+        // we should have enough buffer space, so there is no short count.
+        ASSERT_EQ(thisFrames, framesResampled);
         i += thisFrames;
     }
 }
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 116d0d6..48d0e29 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -110,6 +110,7 @@
                                         audio_format_t format,
                                         audio_channel_mask_t channelMask,
                                         audio_output_flags_t flags,
+                                        int selectedDeviceId,
                                         const audio_offload_info_t *offloadInfo) = 0;
     // indicates to the audio policy manager that the output starts being used by corresponding stream.
     virtual status_t startOutput(audio_io_handle_t output,
diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h
index a4cc759..4205589 100755
--- a/services/audiopolicy/common/include/Volume.h
+++ b/services/audiopolicy/common/include/Volume.h
@@ -18,6 +18,10 @@
 
 #include <system/audio.h>
 #include <utils/Log.h>
+#include <math.h>
+
+// Absolute min volume in dB (can be represented in single precision normal float value)
+#define VOLUME_MIN_DB (-758)
 
 class VolumeCurvePoint
 {
@@ -32,7 +36,7 @@
     /**
      * 4 points to define the volume attenuation curve, each characterized by the volume
      * index (from 0 to 100) at which they apply, and the attenuation in dB at that index.
-     * we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl()
+     * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
      *
      * @todo shall become configurable
      */
@@ -134,4 +138,20 @@
         }
     }
 
+    static inline float DbToAmpl(float decibels)
+    {
+        if (decibels <= VOLUME_MIN_DB) {
+            return 0.0f;
+        }
+        return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
+    }
+
+    static inline float AmplToDb(float amplification)
+    {
+        if (amplification == 0) {
+            return VOLUME_MIN_DB;
+        }
+        return 20 * log10(amplification);
+    }
+
 };
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 71ba1cb..7c265aa 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -25,6 +25,7 @@
 LOCAL_C_INCLUDES += \
     $(LOCAL_PATH)/include \
     $(TOPDIR)frameworks/av/services/audiopolicy/common/include \
+    $(TOPDIR)frameworks/av/services/audiopolicy
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
     $(LOCAL_PATH)/include
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 7536a37..18bcfdb 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -34,12 +34,11 @@
 public:
     AudioInputDescriptor(const sp<IOProfile>& profile);
     void setIoHandle(audio_io_handle_t ioHandle);
-
+    audio_port_handle_t getId() const;
     audio_module_handle_t getModuleHandle() const;
 
     status_t    dump(int fd);
 
-    audio_port_handle_t           mId;
     audio_io_handle_t             mIoHandle;       // input handle
     audio_devices_t               mDevice;         // current device this input is routed to
     AudioMix                      *mPolicyMix;     // non NULL when used by a dynamic policy
@@ -57,6 +56,9 @@
             const struct audio_port_config *srcConfig = NULL) const;
     virtual sp<AudioPort> getAudioPort() const { return mProfile; }
     void toAudioPort(struct audio_port *port) const;
+
+private:
+    audio_port_handle_t           mId;
 };
 
 class AudioInputCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 43ee691..f1aee46 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -27,24 +27,36 @@
 
 class IOProfile;
 class AudioMix;
+class AudioPolicyClientInterface;
 
 // 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
 {
 public:
-    AudioOutputDescriptor(const sp<IOProfile>& profile);
+    AudioOutputDescriptor(const sp<AudioPort>& port,
+                          AudioPolicyClientInterface *clientInterface);
+    virtual ~AudioOutputDescriptor() {}
 
     status_t    dump(int fd);
+    void        log(const char* indent);
 
-    audio_devices_t device() const;
-    void changeRefCount(audio_stream_type_t stream, int delta);
+    audio_port_handle_t getId() const;
+    virtual audio_devices_t device() const;
+    virtual bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
+    virtual audio_devices_t supportedDevices();
+    virtual bool isDuplicated() const { return false; }
+    virtual uint32_t latency() { return 0; }
+    virtual bool isFixedVolume(audio_devices_t device);
+    virtual sp<AudioOutputDescriptor> subOutput1() { return 0; }
+    virtual sp<AudioOutputDescriptor> subOutput2() { return 0; }
+    virtual bool setVolume(float volume,
+                           audio_stream_type_t stream,
+                           audio_devices_t device,
+                           uint32_t delayMs,
+                           bool force);
+    virtual void changeRefCount(audio_stream_type_t stream, int delta);
 
-    void setIoHandle(audio_io_handle_t ioHandle);
-    bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
-    audio_devices_t supportedDevices();
-    uint32_t latency();
-    bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
     bool isActive(uint32_t inPastMs = 0) const;
     bool isStreamActive(audio_stream_type_t stream,
                         uint32_t inPastMs = 0,
@@ -52,32 +64,69 @@
 
     virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
                            const struct audio_port_config *srcConfig = NULL) const;
-    virtual sp<AudioPort> getAudioPort() const { return mProfile; }
-    void toAudioPort(struct audio_port *port) const;
+    virtual sp<AudioPort> getAudioPort() const { return mPort; }
+    virtual void toAudioPort(struct audio_port *port) const;
 
     audio_module_handle_t getModuleHandle() const;
 
-    audio_port_handle_t mId;
-    audio_io_handle_t mIoHandle;              // output handle
-    uint32_t mLatency;                  //
-    audio_output_flags_t mFlags;   //
+    sp<AudioPort>       mPort;
     audio_devices_t mDevice;                   // current device this output is routed to
-    AudioMix *mPolicyMix;             // non NULL when used by a dynamic policy
     audio_patch_handle_t mPatchHandle;
     uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output
     nsecs_t mStopTime[AUDIO_STREAM_CNT];
-    sp<AudioOutputDescriptor> mOutput1;    // used by duplicated outputs: first output
-    sp<AudioOutputDescriptor> mOutput2;    // used by duplicated outputs: second output
-    float mCurVolume[AUDIO_STREAM_CNT];   // current stream volume
+    float mCurVolume[AUDIO_STREAM_CNT];   // current stream volume in dB
     int mMuteCount[AUDIO_STREAM_CNT];     // mute request counter
-    const sp<IOProfile> mProfile;          // I/O profile this output derives from
     bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
                                         // device selection. See checkDeviceMuteStrategies()
+    AudioPolicyClientInterface *mClientInterface;
+
+protected:
+    audio_port_handle_t mId;
+};
+
+// Audio output driven by a software mixer in audio flinger.
+class SwAudioOutputDescriptor: public AudioOutputDescriptor
+{
+public:
+    SwAudioOutputDescriptor(const sp<IOProfile>& profile,
+                            AudioPolicyClientInterface *clientInterface);
+    virtual ~SwAudioOutputDescriptor() {}
+
+    status_t    dump(int fd);
+
+    void setIoHandle(audio_io_handle_t ioHandle);
+
+    virtual audio_devices_t device() const;
+    virtual bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
+    virtual audio_devices_t supportedDevices();
+    virtual uint32_t latency();
+    virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
+    virtual bool isFixedVolume(audio_devices_t device);
+    virtual sp<AudioOutputDescriptor> subOutput1() { return mOutput1; }
+    virtual sp<AudioOutputDescriptor> subOutput2() { return mOutput2; }
+    virtual void changeRefCount(audio_stream_type_t stream, int delta);
+    virtual bool setVolume(float volume,
+                           audio_stream_type_t stream,
+                           audio_devices_t device,
+                           uint32_t delayMs,
+                           bool force);
+
+    virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+                           const struct audio_port_config *srcConfig = NULL) const;
+    virtual void toAudioPort(struct audio_port *port) const;
+
+    const sp<IOProfile> mProfile;          // I/O profile this output derives from
+    audio_io_handle_t mIoHandle;           // output handle
+    uint32_t mLatency;                  //
+    audio_output_flags_t mFlags;   //
+    AudioMix *mPolicyMix;             // non NULL when used by a dynamic policy
+    sp<SwAudioOutputDescriptor> mOutput1;    // used by duplicated outputs: first output
+    sp<SwAudioOutputDescriptor> mOutput2;    // used by duplicated outputs: second output
     uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
 };
 
-class AudioOutputCollection :
-        public DefaultKeyedVector< audio_io_handle_t, sp<AudioOutputDescriptor> >
+class SwAudioOutputCollection :
+        public DefaultKeyedVector< audio_io_handle_t, sp<SwAudioOutputDescriptor> >
 {
 public:
     bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
@@ -96,9 +145,9 @@
      */
     audio_io_handle_t getA2dpOutput() const;
 
-    sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
+    sp<SwAudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
 
-    sp<AudioOutputDescriptor> getPrimaryOutput() const;
+    sp<SwAudioOutputDescriptor> getPrimaryOutput() const;
 
     /**
      * return true if any output is playing anything besides the stream to ignore
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 988aed6..d51f4e1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-class AudioOutputDescriptor;
+class SwAudioOutputDescriptor;
 
 /**
  * custom mix entry in mPolicyMixes
@@ -33,19 +33,19 @@
 public:
     AudioPolicyMix() {}
 
-    const sp<AudioOutputDescriptor> &getOutput() const;
+    const sp<SwAudioOutputDescriptor> &getOutput() const;
 
-    void setOutput(sp<AudioOutputDescriptor> &output);
+    void setOutput(sp<SwAudioOutputDescriptor> &output);
 
     void clearOutput();
 
-    android::AudioMix &getMix();
+    android::AudioMix *getMix();
 
     void setMix(AudioMix &mix);
 
 private:
     AudioMix    mMix;                   // Audio policy mix descriptor
-    sp<AudioOutputDescriptor> mOutput;  // Corresponding output stream
+    sp<SwAudioOutputDescriptor> mOutput;  // Corresponding output stream
 };
 
 
@@ -58,24 +58,24 @@
 
     status_t unregisterMix(String8 address);
 
-    void closeOutput(sp<AudioOutputDescriptor> &desc);
+    void closeOutput(sp<SwAudioOutputDescriptor> &desc);
 
     /**
      * Try to find an output descriptor for the given attributes.
      *
-     * @param[in] attributes to consider for the research of output descriptor.
+     * @param[in] attributes to consider fowr the research of output descriptor.
      * @param[out] desc to return if an output could be found.
      *
      * @return NO_ERROR if an output was found for the given attribute (in this case, the
      *                  descriptor output param is initialized), error code otherwise.
      */
-    status_t getOutputForAttr(audio_attributes_t attributes, sp<AudioOutputDescriptor> &desc);
+    status_t getOutputForAttr(audio_attributes_t attributes, sp<SwAudioOutputDescriptor> &desc);
 
     audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource,
                                                   audio_devices_t availableDeviceTypes,
                                                   AudioMix **policyMix);
 
-    status_t getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix);
+    status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix);
 };
 
 }; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index 4f7f2bc..1c2c27e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -32,13 +32,11 @@
 {
 public:
     AudioPort(const String8& name, audio_port_type_t type,
-              audio_port_role_t role, const sp<HwModule>& module);
+              audio_port_role_t role);
     virtual ~AudioPort() {}
 
-    audio_port_handle_t getHandle() { return mId; }
-
-    void attach(const sp<HwModule>& module);
-    bool isAttached() { return mId != 0; }
+    virtual void attach(const sp<HwModule>& module);
+    bool isAttached() { return mModule != 0; }
 
     static audio_port_handle_t getNextUniqueId();
 
@@ -64,8 +62,12 @@
     // searches for an exact match
     status_t checkExactChannelMask(audio_channel_mask_t channelMask) const;
     // searches for a compatible match, currently implemented for input channel masks only
-    status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const;
-    status_t checkFormat(audio_format_t format) const;
+    status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask,
+            audio_channel_mask_t *updatedChannelMask) const;
+
+    status_t checkExactFormat(audio_format_t format) const;
+    // searches for a compatible match, currently implemented for input formats only
+    status_t checkCompatibleFormat(audio_format_t format, audio_format_t *updatedFormat) const;
     status_t checkGain(const struct audio_gain_config *gainConfig, int index) const;
 
     uint32_t pickSamplingRate() const;
@@ -73,11 +75,19 @@
     audio_format_t pickFormat() const;
 
     static const audio_format_t sPcmFormatCompareTable[];
+    static int compareFormatsGoodToBad(
+            const audio_format_t *format1, const audio_format_t *format2) {
+        // compareFormats sorts from bad to good, we reverse it here
+        return compareFormats(*format2, *format1);
+    }
     static int compareFormats(audio_format_t format1, audio_format_t format2);
 
     audio_module_handle_t getModuleHandle() const;
+    uint32_t getModuleVersion() const;
+    const char *getModuleName() const;
 
     void dump(int fd, int spaces) const;
+    void log(const char* indent) const;
 
     String8           mName;
     audio_port_type_t mType;
@@ -94,13 +104,6 @@
     uint32_t mFlags; // attribute flags (e.g primary output,
                      // direct output...).
 
-
-protected:
-    //TODO - clarify the role of mId in this case, both an "attached" indicator
-    // and a unique ID for identifying a port to the (upcoming) selection API,
-    // and its relationship to the mId in AudioOutputDescriptor and AudioInputDescriptor.
-    audio_port_handle_t mId;
-
 private:
     static volatile int32_t mNextUniqueId;
 };
diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
index 53cb4a3..f8c4d08 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
@@ -39,11 +39,12 @@
 };
 
 #define STRING_TO_ENUM(string) { #string, string }
+#define NAME_TO_ENUM(name, value) { name, value }
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 #endif
 
-const StringToEnum sDeviceNameToEnumTable[] = {
+const StringToEnum sDeviceTypeToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE),
@@ -94,6 +95,57 @@
     STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
 };
 
+const StringToEnum sDeviceNameToEnumTable[] = {
+    NAME_TO_ENUM("Earpiece", AUDIO_DEVICE_OUT_EARPIECE),
+    NAME_TO_ENUM("Speaker", AUDIO_DEVICE_OUT_SPEAKER),
+    NAME_TO_ENUM("Speaker Protected", AUDIO_DEVICE_OUT_SPEAKER_SAFE),
+    NAME_TO_ENUM("Wired Headset", AUDIO_DEVICE_OUT_WIRED_HEADSET),
+    NAME_TO_ENUM("Wired Headphones", AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
+    NAME_TO_ENUM("BT SCO", AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
+    NAME_TO_ENUM("BT SCO Headset", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
+    NAME_TO_ENUM("BT SCO Car Kit", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
+    NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_SCO),
+    NAME_TO_ENUM("BT A2DP Out", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
+    NAME_TO_ENUM("BT A2DP Headphones", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
+    NAME_TO_ENUM("BT A2DP Speaker", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
+    NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_A2DP),
+    NAME_TO_ENUM("HDMI Out", AUDIO_DEVICE_OUT_AUX_DIGITAL),
+    NAME_TO_ENUM("HDMI Out", AUDIO_DEVICE_OUT_HDMI),
+    NAME_TO_ENUM("Analog Dock Out", AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
+    NAME_TO_ENUM("Digital Dock Out", AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
+    NAME_TO_ENUM("USB Host Out", AUDIO_DEVICE_OUT_USB_ACCESSORY),
+    NAME_TO_ENUM("USB Device Out", AUDIO_DEVICE_OUT_USB_DEVICE),
+    NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_USB),
+    NAME_TO_ENUM("Reroute Submix Out", AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
+    NAME_TO_ENUM("Telephony Tx", AUDIO_DEVICE_OUT_TELEPHONY_TX),
+    NAME_TO_ENUM("Line Out", AUDIO_DEVICE_OUT_LINE),
+    NAME_TO_ENUM("HDMI ARC Out", AUDIO_DEVICE_OUT_HDMI_ARC),
+    NAME_TO_ENUM("S/PDIF Out", AUDIO_DEVICE_OUT_SPDIF),
+    NAME_TO_ENUM("FM transceiver Out", AUDIO_DEVICE_OUT_FM),
+    NAME_TO_ENUM("Aux Line Out", AUDIO_DEVICE_OUT_AUX_LINE),
+    NAME_TO_ENUM("Ambient Mic", AUDIO_DEVICE_IN_AMBIENT),
+    NAME_TO_ENUM("Built-In Mic", AUDIO_DEVICE_IN_BUILTIN_MIC),
+    NAME_TO_ENUM("BT SCO Headset Mic", AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
+    NAME_TO_ENUM("", AUDIO_DEVICE_IN_ALL_SCO),
+    NAME_TO_ENUM("Wired Headset Mic", AUDIO_DEVICE_IN_WIRED_HEADSET),
+    NAME_TO_ENUM("HDMI In", AUDIO_DEVICE_IN_AUX_DIGITAL),
+    NAME_TO_ENUM("HDMI In", AUDIO_DEVICE_IN_HDMI),
+    NAME_TO_ENUM("Telephony Rx", AUDIO_DEVICE_IN_TELEPHONY_RX),
+    NAME_TO_ENUM("Telephony Rx", AUDIO_DEVICE_IN_VOICE_CALL),
+    NAME_TO_ENUM("Built-In Back Mic", AUDIO_DEVICE_IN_BACK_MIC),
+    NAME_TO_ENUM("Reroute Submix In", AUDIO_DEVICE_IN_REMOTE_SUBMIX),
+    NAME_TO_ENUM("Analog Dock In", AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
+    NAME_TO_ENUM("Digital Dock In", AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
+    NAME_TO_ENUM("USB Host In", AUDIO_DEVICE_IN_USB_ACCESSORY),
+    NAME_TO_ENUM("USB Device In", AUDIO_DEVICE_IN_USB_DEVICE),
+    NAME_TO_ENUM("FM Tuner In", AUDIO_DEVICE_IN_FM_TUNER),
+    NAME_TO_ENUM("TV Tuner In", AUDIO_DEVICE_IN_TV_TUNER),
+    NAME_TO_ENUM("Line In", AUDIO_DEVICE_IN_LINE),
+    NAME_TO_ENUM("S/PDIF In", AUDIO_DEVICE_IN_SPDIF),
+    NAME_TO_ENUM("BT A2DP In", AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
+    NAME_TO_ENUM("Loopback In", AUDIO_DEVICE_IN_LOOPBACK),
+};
+
 const StringToEnum sOutputFlagNameToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
     STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
@@ -134,6 +186,8 @@
     STRING_TO_ENUM(AUDIO_FORMAT_OPUS),
     STRING_TO_ENUM(AUDIO_FORMAT_AC3),
     STRING_TO_ENUM(AUDIO_FORMAT_E_AC3),
+    STRING_TO_ENUM(AUDIO_FORMAT_DTS),
+    STRING_TO_ENUM(AUDIO_FORMAT_DTS_HD),
 };
 
 const StringToEnum sOutChannelsNameToEnumTable[] = {
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index d15f6b4..aa37eec 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -41,19 +41,22 @@
             const struct audio_port_config *srcConfig = NULL) const;
 
     // AudioPort
+    virtual void attach(const sp<HwModule>& module);
     virtual void loadGains(cnode *root);
     virtual void toAudioPort(struct audio_port *port) const;
 
+    audio_port_handle_t getId() const;
     audio_devices_t type() const { return mDeviceType; }
     status_t dump(int fd, int spaces, int index) const;
+    void log() const;
 
     String8 mAddress;
-    audio_port_handle_t mId;
 
     static String8  emptyNameStr;
 
 private:
-    audio_devices_t mDeviceType;
+    audio_devices_t     mDeviceType;
+    audio_port_handle_t mId;
 
 friend class DeviceVector;
 };
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index 095e759..ab6fcc1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -33,7 +33,7 @@
 class IOProfile : public AudioPort
 {
 public:
-    IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module);
+    IOProfile(const String8& name, audio_port_role_t role);
     virtual ~IOProfile();
 
     // This method is used for both output and input.
@@ -45,7 +45,9 @@
                              uint32_t samplingRate,
                              uint32_t *updatedSamplingRate,
                              audio_format_t format,
+                             audio_format_t *updatedFormat,
                              audio_channel_mask_t channelMask,
+                             audio_channel_mask_t *updatedChannelMask,
                              uint32_t flags) const;
 
     void dump(int fd);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index fa66728..937160b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -27,9 +27,9 @@
 namespace android {
 
 AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile)
-    : mId(0), mIoHandle(0),
+    : mIoHandle(0),
       mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0),
-      mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false)
+      mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false), mId(0)
 {
     if (profile != NULL) {
         mSamplingRate = profile->pickSamplingRate();
@@ -49,9 +49,17 @@
 
 audio_module_handle_t AudioInputDescriptor::getModuleHandle() const
 {
+    if (mProfile == 0) {
+        return 0;
+    }
     return mProfile->getModuleHandle();
 }
 
+audio_port_handle_t AudioInputDescriptor::getId() const
+{
+    return mId;
+}
+
 void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig,
                                              const struct audio_port_config *srcConfig) const
 {
@@ -68,7 +76,7 @@
     dstConfig->id = mId;
     dstConfig->role = AUDIO_PORT_ROLE_SINK;
     dstConfig->type = AUDIO_PORT_TYPE_MIX;
-    dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+    dstConfig->ext.mix.hw_module = getModuleHandle();
     dstConfig->ext.mix.handle = mIoHandle;
     dstConfig->ext.mix.usecase.source = mInputSource;
 }
@@ -80,7 +88,7 @@
     mProfile->toAudioPort(port);
     port->id = mId;
     toAudioPortConfig(&port->active_config);
-    port->ext.mix.hw_module = mProfile->mModule->mHandle;
+    port->ext.mix.hw_module = getModuleHandle();
     port->ext.mix.handle = mIoHandle;
     port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
 }
@@ -91,7 +99,7 @@
     char buffer[SIZE];
     String8 result;
 
-    snprintf(buffer, SIZE, " ID: %d\n", mId);
+    snprintf(buffer, SIZE, " ID: %d\n", getId());
     result.append(buffer);
     snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
     result.append(buffer);
@@ -130,7 +138,7 @@
     sp<AudioInputDescriptor> inputDesc = NULL;
     for (size_t i = 0; i < size(); i++) {
         inputDesc = valueAt(i);
-        if (inputDesc->mId == id) {
+        if (inputDesc->getId() == id) {
             break;
         }
     }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index cdb5b51..596aa1d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -17,9 +17,11 @@
 #define LOG_TAG "APM::AudioOutputDescriptor"
 //#define LOG_NDEBUG 0
 
+#include <AudioPolicyInterface.h>
 #include "AudioOutputDescriptor.h"
 #include "IOProfile.h"
 #include "AudioGain.h"
+#include "Volume.h"
 #include "HwModule.h"
 #include <media/AudioPolicy.h>
 
@@ -29,11 +31,10 @@
 
 namespace android {
 
-AudioOutputDescriptor::AudioOutputDescriptor(const sp<IOProfile>& profile)
-    : mId(0), mIoHandle(0), mLatency(0),
-    mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL),
-    mPatchHandle(0),
-    mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0)
+AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port,
+                                             AudioPolicyClientInterface *clientInterface)
+    : mPort(port), mDevice(AUDIO_DEVICE_NONE),
+      mPatchHandle(0), mClientInterface(clientInterface), mId(0)
 {
     // clear usage count for all stream types
     for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
@@ -45,66 +46,50 @@
     for (int i = 0; i < NUM_STRATEGIES; i++) {
         mStrategyMutedByDevice[i] = false;
     }
-    if (profile != NULL) {
-        mFlags = (audio_output_flags_t)profile->mFlags;
-        mSamplingRate = profile->pickSamplingRate();
-        mFormat = profile->pickFormat();
-        mChannelMask = profile->pickChannelMask();
-        if (profile->mGains.size() > 0) {
-            profile->mGains[0]->getDefaultConfig(&mGain);
+    if (port != NULL) {
+        mSamplingRate = port->pickSamplingRate();
+        mFormat = port->pickFormat();
+        mChannelMask = port->pickChannelMask();
+        if (port->mGains.size() > 0) {
+            port->mGains[0]->getDefaultConfig(&mGain);
         }
     }
 }
 
 audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const
 {
-    return mProfile->getModuleHandle();
+    return mPort->getModuleHandle();
+}
+
+audio_port_handle_t AudioOutputDescriptor::getId() const
+{
+    return mId;
 }
 
 audio_devices_t AudioOutputDescriptor::device() const
 {
-    if (isDuplicated()) {
-        return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice);
-    } else {
-        return mDevice;
-    }
+    return mDevice;
 }
 
-void AudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
+audio_devices_t AudioOutputDescriptor::supportedDevices()
 {
-    mId = AudioPort::getNextUniqueId();
-    mIoHandle = ioHandle;
-}
-
-uint32_t AudioOutputDescriptor::latency()
-{
-    if (isDuplicated()) {
-        return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency;
-    } else {
-        return mLatency;
-    }
+    return mDevice;
 }
 
 bool AudioOutputDescriptor::sharesHwModuleWith(
         const sp<AudioOutputDescriptor> outputDesc)
 {
-    if (isDuplicated()) {
-        return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
-    } else if (outputDesc->isDuplicated()){
-        return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2);
+    if (outputDesc->isDuplicated()) {
+        return sharesHwModuleWith(outputDesc->subOutput1()) ||
+                    sharesHwModuleWith(outputDesc->subOutput2());
     } else {
-        return (mProfile->mModule == outputDesc->mProfile->mModule);
+        return (getModuleHandle() == outputDesc->getModuleHandle());
     }
 }
 
 void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
                                                                    int delta)
 {
-    // forward usage count change to attached outputs
-    if (isDuplicated()) {
-        mOutput1->changeRefCount(stream, delta);
-        mOutput2->changeRefCount(stream, delta);
-    }
     if ((delta + (int)mRefCount[stream]) < 0) {
         ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d",
               delta, stream, mRefCount[stream]);
@@ -115,15 +100,6 @@
     ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
 }
 
-audio_devices_t AudioOutputDescriptor::supportedDevices()
-{
-    if (isDuplicated()) {
-        return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices());
-    } else {
-        return mProfile->mSupportedDevices.types() ;
-    }
-}
-
 bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const
 {
     nsecs_t sysTime = 0;
@@ -160,12 +136,33 @@
     return false;
 }
 
+
+bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused)
+{
+    return false;
+}
+
+bool AudioOutputDescriptor::setVolume(float volume,
+                                      audio_stream_type_t stream,
+                                      audio_devices_t device __unused,
+                                      uint32_t delayMs,
+                                      bool force)
+{
+    // We actually change the volume if:
+    // - the float value returned by computeVolume() changed
+    // - the force flag is set
+    if (volume != mCurVolume[stream] || force) {
+        ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
+        mCurVolume[stream] = volume;
+        return true;
+    }
+    return false;
+}
+
 void AudioOutputDescriptor::toAudioPortConfig(
                                                  struct audio_port_config *dstConfig,
                                                  const struct audio_port_config *srcConfig) const
 {
-    ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle);
-
     dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
                             AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN;
     if (srcConfig != NULL) {
@@ -176,22 +173,16 @@
     dstConfig->id = mId;
     dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
     dstConfig->type = AUDIO_PORT_TYPE_MIX;
-    dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
-    dstConfig->ext.mix.handle = mIoHandle;
+    dstConfig->ext.mix.hw_module = getModuleHandle();
     dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
 }
 
 void AudioOutputDescriptor::toAudioPort(
                                                     struct audio_port *port) const
 {
-    ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
-    mProfile->toAudioPort(port);
+    mPort->toAudioPort(port);
     port->id = mId;
-    toAudioPortConfig(&port->active_config);
-    port->ext.mix.hw_module = mProfile->mModule->mHandle;
-    port->ext.mix.handle = mIoHandle;
-    port->ext.mix.latency_class =
-            mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+    port->ext.mix.hw_module = getModuleHandle();
 }
 
 status_t AudioOutputDescriptor::dump(int fd)
@@ -208,10 +199,6 @@
     result.append(buffer);
     snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask);
     result.append(buffer);
-    snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
-    result.append(buffer);
     snprintf(buffer, SIZE, " Devices %08x\n", device());
     result.append(buffer);
     snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
@@ -226,11 +213,165 @@
     return NO_ERROR;
 }
 
-bool AudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+void AudioOutputDescriptor::log(const char* indent)
+{
+    ALOGI("%sID: %d,0x%X, [rt:%d fmt:0x%X ch:0x%X]",
+          indent, mId, mId, mSamplingRate, mFormat, mChannelMask);
+}
+
+// SwAudioOutputDescriptor implementation
+SwAudioOutputDescriptor::SwAudioOutputDescriptor(
+        const sp<IOProfile>& profile, AudioPolicyClientInterface *clientInterface)
+    : AudioOutputDescriptor(profile, clientInterface),
+    mProfile(profile), mIoHandle(0), mLatency(0),
+    mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
+    mOutput1(0), mOutput2(0), mDirectOpenCount(0)
+{
+    if (profile != NULL) {
+        mFlags = (audio_output_flags_t)profile->mFlags;
+    }
+}
+
+void SwAudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
+{
+    mId = AudioPort::getNextUniqueId();
+    mIoHandle = ioHandle;
+}
+
+
+status_t SwAudioOutputDescriptor::dump(int fd)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+
+    AudioOutputDescriptor::dump(fd);
+
+    return NO_ERROR;
+}
+
+audio_devices_t SwAudioOutputDescriptor::device() const
+{
+    if (isDuplicated()) {
+        return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice);
+    } else {
+        return mDevice;
+    }
+}
+
+bool SwAudioOutputDescriptor::sharesHwModuleWith(
+        const sp<AudioOutputDescriptor> outputDesc)
+{
+    if (isDuplicated()) {
+        return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
+    } else if (outputDesc->isDuplicated()){
+        return sharesHwModuleWith(outputDesc->subOutput1()) ||
+                    sharesHwModuleWith(outputDesc->subOutput2());
+    } else {
+        return AudioOutputDescriptor::sharesHwModuleWith(outputDesc);
+    }
+}
+
+audio_devices_t SwAudioOutputDescriptor::supportedDevices()
+{
+    if (isDuplicated()) {
+        return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices());
+    } else {
+        return mProfile->mSupportedDevices.types() ;
+    }
+}
+
+uint32_t SwAudioOutputDescriptor::latency()
+{
+    if (isDuplicated()) {
+        return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency;
+    } else {
+        return mLatency;
+    }
+}
+
+void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+                                                                   int delta)
+{
+    // forward usage count change to attached outputs
+    if (isDuplicated()) {
+        mOutput1->changeRefCount(stream, delta);
+        mOutput2->changeRefCount(stream, delta);
+    }
+    AudioOutputDescriptor::changeRefCount(stream, delta);
+}
+
+
+bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device)
+{
+    // unit gain if rerouting to external policy
+    if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
+        if (mPolicyMix != NULL) {
+            ALOGV("max gain when rerouting for output=%d", mIoHandle);
+            return true;
+        }
+    }
+    return false;
+}
+
+void SwAudioOutputDescriptor::toAudioPortConfig(
+                                                 struct audio_port_config *dstConfig,
+                                                 const struct audio_port_config *srcConfig) const
+{
+
+    ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle);
+    AudioOutputDescriptor::toAudioPortConfig(dstConfig, srcConfig);
+
+    dstConfig->ext.mix.handle = mIoHandle;
+}
+
+void SwAudioOutputDescriptor::toAudioPort(
+                                                    struct audio_port *port) const
+{
+    ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
+
+    AudioOutputDescriptor::toAudioPort(port);
+
+    toAudioPortConfig(&port->active_config);
+    port->ext.mix.handle = mIoHandle;
+    port->ext.mix.latency_class =
+            mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+}
+
+bool SwAudioOutputDescriptor::setVolume(float volume,
+                                        audio_stream_type_t stream,
+                                        audio_devices_t device,
+                                        uint32_t delayMs,
+                                        bool force)
+{
+    bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);
+
+    if (changed) {
+        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
+        // enabled
+        float volume = Volume::DbToAmpl(mCurVolume[stream]);
+        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
+            mClientInterface->setStreamVolume(
+                    AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
+        }
+        mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
+    }
+    return changed;
+}
+
+// SwAudioOutputCollection implementation
+
+bool SwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
 {
     nsecs_t sysTime = systemTime();
     for (size_t i = 0; i < this->size(); i++) {
-        const sp<AudioOutputDescriptor> outputDesc = this->valueAt(i);
+        const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
         if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
             return true;
         }
@@ -238,12 +379,12 @@
     return false;
 }
 
-bool AudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
+bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
                                                    uint32_t inPastMs) const
 {
     nsecs_t sysTime = systemTime();
     for (size_t i = 0; i < size(); i++) {
-        const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+        const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
         if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) &&
                 outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
             // do not consider re routing (when the output is going to a dynamic policy)
@@ -256,10 +397,10 @@
     return false;
 }
 
-audio_io_handle_t AudioOutputCollection::getA2dpOutput() const
+audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const
 {
     for (size_t i = 0; i < size(); i++) {
-        sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+        sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
         if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) {
             return this->keyAt(i);
         }
@@ -267,10 +408,10 @@
     return 0;
 }
 
-sp<AudioOutputDescriptor> AudioOutputCollection::getPrimaryOutput() const
+sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getPrimaryOutput() const
 {
     for (size_t i = 0; i < size(); i++) {
-        const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+        const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
         if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
             return outputDesc;
         }
@@ -278,26 +419,26 @@
     return NULL;
 }
 
-sp<AudioOutputDescriptor> AudioOutputCollection::getOutputFromId(audio_port_handle_t id) const
+sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getOutputFromId(audio_port_handle_t id) const
 {
-    sp<AudioOutputDescriptor> outputDesc = NULL;
+    sp<SwAudioOutputDescriptor> outputDesc = NULL;
     for (size_t i = 0; i < size(); i++) {
         outputDesc = valueAt(i);
-        if (outputDesc->mId == id) {
+        if (outputDesc->getId() == id) {
             break;
         }
     }
     return outputDesc;
 }
 
-bool AudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
+bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
 {
     for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) {
         if (s == (size_t) streamToIgnore) {
             continue;
         }
         for (size_t i = 0; i < size(); i++) {
-            const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+            const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
             if (outputDesc->mRefCount[s] != 0) {
                 return true;
             }
@@ -306,15 +447,15 @@
     return false;
 }
 
-audio_devices_t AudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const
+audio_devices_t SwAudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const
 {
-    sp<AudioOutputDescriptor> outputDesc = valueFor(handle);
+    sp<SwAudioOutputDescriptor> outputDesc = valueFor(handle);
     audio_devices_t devices = outputDesc->mProfile->mSupportedDevices.types();
     return devices;
 }
 
 
-status_t AudioOutputCollection::dump(int fd) const
+status_t SwAudioOutputCollection::dump(int fd) const
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
index 3a317fa..a06d867 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
@@ -54,8 +54,8 @@
     for (size_t i = 0; i < mPatch.num_sources; i++) {
         if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) {
             snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
-                     mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
-                                                        ARRAY_SIZE(sDeviceNameToEnumTable),
+                     mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+                                                        ARRAY_SIZE(sDeviceTypeToEnumTable),
                                                         mPatch.sources[i].ext.device.type));
         } else {
             snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
@@ -68,8 +68,8 @@
     for (size_t i = 0; i < mPatch.num_sinks; i++) {
         if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) {
             snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
-                     mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
-                                                        ARRAY_SIZE(sDeviceNameToEnumTable),
+                     mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+                                                        ARRAY_SIZE(sDeviceTypeToEnumTable),
                                                         mPatch.sinks[i].ext.device.type));
         } else {
             snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 84a53ebd..77fc0b9 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -26,12 +26,12 @@
 
 namespace android {
 
-void AudioPolicyMix::setOutput(sp<AudioOutputDescriptor> &output)
+void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
 {
     mOutput = output;
 }
 
-const sp<AudioOutputDescriptor> &AudioPolicyMix::getOutput() const
+const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
 {
     return mOutput;
 }
@@ -46,9 +46,9 @@
     mMix = mix;
 }
 
-android::AudioMix &AudioPolicyMix::getMix()
+android::AudioMix *AudioPolicyMix::getMix()
 {
-    return mMix;
+    return &mMix;
 }
 
 status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix)
@@ -88,7 +88,7 @@
     return NO_ERROR;
 }
 
-void AudioPolicyMixCollection::closeOutput(sp<AudioOutputDescriptor> &desc)
+void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
 {
     for (size_t i = 0; i < size(); i++) {
         sp<AudioPolicyMix> policyMix = valueAt(i);
@@ -99,40 +99,40 @@
 }
 
 status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes,
-                                                    sp<AudioOutputDescriptor> &desc)
+                                                    sp<SwAudioOutputDescriptor> &desc)
 {
     for (size_t i = 0; i < size(); i++) {
         sp<AudioPolicyMix> policyMix = valueAt(i);
-        AudioMix mix = policyMix->getMix();
+        AudioMix *mix = policyMix->getMix();
 
-        if (mix.mMixType == MIX_TYPE_PLAYERS) {
-            for (size_t j = 0; j < mix.mCriteria.size(); j++) {
-                if ((RULE_MATCH_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule &&
-                     mix.mCriteria[j].mAttr.mUsage == attributes.usage) ||
-                        (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule &&
-                         mix.mCriteria[j].mAttr.mUsage != attributes.usage)) {
+        if (mix->mMixType == MIX_TYPE_PLAYERS) {
+            for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+                if ((RULE_MATCH_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
+                     mix->mCriteria[j].mAttr.mUsage == attributes.usage) ||
+                        (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
+                         mix->mCriteria[j].mAttr.mUsage != attributes.usage)) {
                     desc = policyMix->getOutput();
                     break;
                 }
                 if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
                         strncmp(attributes.tags + strlen("addr="),
-                                mix.mRegistrationId.string(),
+                                mix->mRegistrationId.string(),
                                 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
                     desc = policyMix->getOutput();
                     break;
                 }
             }
-        } else if (mix.mMixType == MIX_TYPE_RECORDERS) {
+        } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
             if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
                     strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
                     strncmp(attributes.tags + strlen("addr="),
-                            mix.mRegistrationId.string(),
+                            mix->mRegistrationId.string(),
                             AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
                 desc = policyMix->getOutput();
             }
         }
         if (desc != 0) {
-            desc->mPolicyMix = &mix;
+            desc->mPolicyMix = mix;
             return NO_ERROR;
         }
     }
@@ -144,19 +144,19 @@
                                                                         AudioMix **policyMix)
 {
     for (size_t i = 0; i < size(); i++) {
-        AudioMix mix = valueAt(i)->getMix();
+        AudioMix *mix = valueAt(i)->getMix();
 
-        if (mix.mMixType != MIX_TYPE_RECORDERS) {
+        if (mix->mMixType != MIX_TYPE_RECORDERS) {
             continue;
         }
-        for (size_t j = 0; j < mix.mCriteria.size(); j++) {
-            if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule &&
-                    mix.mCriteria[j].mAttr.mSource == inputSource) ||
-               (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule &&
-                    mix.mCriteria[j].mAttr.mSource != inputSource)) {
+        for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+            if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
+                    mix->mCriteria[j].mAttr.mSource == inputSource) ||
+               (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
+                    mix->mCriteria[j].mAttr.mSource != inputSource)) {
                 if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
                     if (policyMix != NULL) {
-                        *policyMix = &mix;
+                        *policyMix = mix;
                     }
                     return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
                 }
@@ -167,7 +167,7 @@
     return AUDIO_DEVICE_NONE;
 }
 
-status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix)
+status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
 {
     if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
         return BAD_VALUE;
@@ -180,13 +180,13 @@
         return BAD_VALUE;
     }
     sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
-    AudioMix mix = audioPolicyMix->getMix();
+    AudioMix *mix = audioPolicyMix->getMix();
 
-    if (mix.mMixType != MIX_TYPE_PLAYERS) {
+    if (mix->mMixType != MIX_TYPE_PLAYERS) {
         ALOGW("getInputForAttr() bad policy mix type for address %s", address.string());
         return BAD_VALUE;
     }
-    policyMix = &mix;
+    *policyMix = mix;
     return NO_ERROR;
 }
 
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 46a119e..f3978ec 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "APM::AudioPort"
 //#define LOG_NDEBUG 0
-
+#include <media/AudioResamplerPublic.h>
 #include "AudioPort.h"
 #include "HwModule.h"
 #include "AudioGain.h"
@@ -31,8 +31,8 @@
 // --- AudioPort class implementation
 
 AudioPort::AudioPort(const String8& name, audio_port_type_t type,
-                     audio_port_role_t role, const sp<HwModule>& module) :
-    mName(name), mType(type), mRole(role), mModule(module), mFlags(0), mId(0)
+                     audio_port_role_t role) :
+    mName(name), mType(type), mRole(role), mFlags(0)
 {
     mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
                     ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
@@ -40,7 +40,6 @@
 
 void AudioPort::attach(const sp<HwModule>& module)
 {
-    mId = getNextUniqueId();
     mModule = module;
 }
 
@@ -51,9 +50,28 @@
 
 audio_module_handle_t AudioPort::getModuleHandle() const
 {
+    if (mModule == 0) {
+        return 0;
+    }
     return mModule->mHandle;
 }
 
+uint32_t AudioPort::getModuleVersion() const
+{
+    if (mModule == 0) {
+        return 0;
+    }
+    return mModule->mHalVersion;
+}
+
+const char *AudioPort::getModuleName() const
+{
+    if (mModule == 0) {
+        return "";
+    }
+    return mModule->mName;
+}
+
 void AudioPort::toAudioPort(struct audio_port *port) const
 {
     port->role = mRole;
@@ -198,6 +216,7 @@
         }
         str = strtok(NULL, "|");
     }
+    mFormats.sort(compareFormatsGoodToBad);
 }
 
 void AudioPort::loadInChannels(char *name)
@@ -340,6 +359,9 @@
         uint32_t *updatedSamplingRate) const
 {
     if (mSamplingRates.isEmpty()) {
+        if (updatedSamplingRate != NULL) {
+            *updatedSamplingRate = samplingRate;
+        }
         return NO_ERROR;
     }
 
@@ -369,16 +391,11 @@
             }
         }
     }
-    // This uses hard-coded knowledge about AudioFlinger resampling ratios.
-    // TODO Move these assumptions out.
-    static const uint32_t kMaxDownSampleRatio = 6;  // beyond this aliasing occurs
-    static const uint32_t kMaxUpSampleRatio = 256;  // beyond this sample rate inaccuracies occur
-                                                    // due to approximation by an int32_t of the
-                                                    // phase increments
+
     // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum.
     if (minAbove >= 0) {
         candidate = mSamplingRates[minAbove];
-        if (candidate / kMaxDownSampleRatio <= samplingRate) {
+        if (candidate / AUDIO_RESAMPLER_DOWN_RATIO_MAX <= samplingRate) {
             if (updatedSamplingRate != NULL) {
                 *updatedSamplingRate = candidate;
             }
@@ -388,7 +405,7 @@
     // But if we have to up-sample from a lower sampling rate, that's OK.
     if (maxBelow >= 0) {
         candidate = mSamplingRates[maxBelow];
-        if (candidate * kMaxUpSampleRatio >= samplingRate) {
+        if (candidate * AUDIO_RESAMPLER_UP_RATIO_MAX >= samplingRate) {
             if (updatedSamplingRate != NULL) {
                 *updatedSamplingRate = candidate;
             }
@@ -413,10 +430,13 @@
     return BAD_VALUE;
 }
 
-status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask)
-        const
+status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
+        audio_channel_mask_t *updatedChannelMask) const
 {
     if (mChannelMasks.isEmpty()) {
+        if (updatedChannelMask != NULL) {
+            *updatedChannelMask = channelMask;
+        }
         return NO_ERROR;
     }
 
@@ -425,6 +445,9 @@
         // FIXME Does not handle multi-channel automatic conversions yet
         audio_channel_mask_t supported = mChannelMasks[i];
         if (supported == channelMask) {
+            if (updatedChannelMask != NULL) {
+                *updatedChannelMask = channelMask;
+            }
             return NO_ERROR;
         }
         if (isRecordThread) {
@@ -434,6 +457,9 @@
                     && channelMask == AUDIO_CHANNEL_IN_MONO) ||
                 (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
                     || channelMask == AUDIO_CHANNEL_IN_STEREO))) {
+                if (updatedChannelMask != NULL) {
+                    *updatedChannelMask = supported;
+                }
                 return NO_ERROR;
             }
         }
@@ -441,7 +467,7 @@
     return BAD_VALUE;
 }
 
-status_t AudioPort::checkFormat(audio_format_t format) const
+status_t AudioPort::checkExactFormat(audio_format_t format) const
 {
     if (mFormats.isEmpty()) {
         return NO_ERROR;
@@ -455,6 +481,33 @@
     return BAD_VALUE;
 }
 
+status_t AudioPort::checkCompatibleFormat(audio_format_t format, audio_format_t *updatedFormat)
+        const
+{
+    if (mFormats.isEmpty()) {
+        if (updatedFormat != NULL) {
+            *updatedFormat = format;
+        }
+        return NO_ERROR;
+    }
+
+    const bool checkInexact = // when port is input and format is linear pcm
+            mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK
+            && audio_is_linear_pcm(format);
+
+    for (size_t i = 0; i < mFormats.size(); ++i) {
+        if (mFormats[i] == format ||
+                (checkInexact && audio_is_linear_pcm(mFormats[i]))) {
+            // for inexact checks we take the first linear pcm format since
+            // mFormats is sorted from best PCM format to worst PCM format.
+            if (updatedFormat != NULL) {
+                *updatedFormat = mFormats[i];
+            }
+            return NO_ERROR;
+        }
+    }
+    return BAD_VALUE;
+}
 
 uint32_t AudioPort::pickSamplingRate() const
 {
@@ -629,7 +682,7 @@
     char buffer[SIZE];
     String8 result;
 
-    if (mName.size() != 0) {
+    if (mName.length() != 0) {
         snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
         result.append(buffer);
     }
@@ -687,13 +740,16 @@
     if (mGains.size() != 0) {
         snprintf(buffer, SIZE, "%*s- gains:\n", spaces, "");
         write(fd, buffer, strlen(buffer) + 1);
-        result.append(buffer);
         for (size_t i = 0; i < mGains.size(); i++) {
             mGains[i]->dump(fd, spaces + 2, i);
         }
     }
 }
 
+void AudioPort::log(const char* indent) const
+{
+    ALOGI("%s Port[nm:%s, type:%d, role:%d]", indent, mName.string(), mType, mRole);
+}
 
 // --- AudioPortConfig class implementation
 
@@ -735,7 +791,7 @@
         mChannelMask = config->channel_mask;
     }
     if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
-        status = audioport->checkFormat(config->format);
+        status = audioport->checkExactFormat(config->format);
         if (status != NO_ERROR) {
             goto exit;
         }
diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
index fe5bc5f..9ab1d61 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
@@ -113,8 +113,8 @@
     char *devName = strtok(name, "|");
     while (devName != NULL) {
         if (strlen(devName) != 0) {
-            device |= stringToEnum(sDeviceNameToEnumTable,
-                                 ARRAY_SIZE(sDeviceNameToEnumTable),
+            device |= stringToEnum(sDeviceTypeToEnumTable,
+                                 ARRAY_SIZE(sDeviceTypeToEnumTable),
                                  devName);
          }
         devName = strtok(NULL, "|");
@@ -224,8 +224,8 @@
                   availableOutputDevices.types());
         } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
             audio_devices_t device = (audio_devices_t)stringToEnum(
-                    sDeviceNameToEnumTable,
-                    ARRAY_SIZE(sDeviceNameToEnumTable),
+                    sDeviceTypeToEnumTable,
+                    ARRAY_SIZE(sDeviceTypeToEnumTable),
                     (char *)node->value);
             if (device != AUDIO_DEVICE_NONE) {
                 defaultOutputDevice = new DeviceDescriptor(String8("default-output"), device);
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 7df7d75..9573583 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -29,13 +29,23 @@
 DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) :
     AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
               audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
-                                             AUDIO_PORT_ROLE_SOURCE,
-              NULL),
-    mAddress(""), mDeviceType(type)
+                                             AUDIO_PORT_ROLE_SOURCE),
+    mAddress(""), mDeviceType(type), mId(0)
 {
 
 }
 
+audio_port_handle_t DeviceDescriptor::getId() const
+{
+    return mId;
+}
+
+void DeviceDescriptor::attach(const sp<HwModule>& module)
+{
+    AudioPort::attach(module);
+    mId = getNextUniqueId();
+}
+
 bool DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const
 {
     // Devices are considered equal if they:
@@ -139,11 +149,14 @@
     char *devName = strtok(name, "|");
     while (devName != NULL) {
         if (strlen(devName) != 0) {
-            audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceNameToEnumTable,
-                                 ARRAY_SIZE(sDeviceNameToEnumTable),
+            audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceTypeToEnumTable,
+                                 ARRAY_SIZE(sDeviceTypeToEnumTable),
                                  devName);
             if (type != AUDIO_DEVICE_NONE) {
-                sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(name), type);
+                devName = (char *)ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
+                                                           ARRAY_SIZE(sDeviceNameToEnumTable),
+                                                           type);
+                sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(devName), type);
                 if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX ||
                         type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) {
                     dev->mAddress = String8("0");
@@ -183,7 +196,7 @@
 {
     sp<DeviceDescriptor> device;
     for (size_t i = 0; i < size(); i++) {
-        if (itemAt(i)->getHandle() == id) {
+        if (itemAt(i)->getId() == id) {
             device = itemAt(i);
             break;
         }
@@ -303,8 +316,8 @@
         result.append(buffer);
     }
     snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
-            ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
-                    ARRAY_SIZE(sDeviceNameToEnumTable),
+            ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+                    ARRAY_SIZE(sDeviceTypeToEnumTable),
                     mDeviceType));
     result.append(buffer);
     if (mAddress.size() != 0) {
@@ -317,4 +330,16 @@
     return NO_ERROR;
 }
 
+void DeviceDescriptor::log() const
+{
+    ALOGI("Device id:%d type:0x%X:%s, addr:%s",
+          mId,
+          mDeviceType,
+          ConfigParsingUtils::enumToString(
+             sDeviceNameToEnumTable, ARRAY_SIZE(sDeviceNameToEnumTable), mDeviceType),
+          mAddress.string());
+
+    AudioPort::log("  ");
+}
+
 }; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index 0097d69..e955447 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -48,7 +48,7 @@
 {
     cnode *node = root->first_child;
 
-    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK);
 
     while (node) {
         if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
@@ -83,6 +83,7 @@
         ALOGV("loadInput() adding input Supported Devices %04x",
               profile->mSupportedDevices.types());
 
+        profile->attach(this);
         mInputProfiles.add(profile);
         return NO_ERROR;
     } else {
@@ -94,7 +95,7 @@
 {
     cnode *node = root->first_child;
 
-    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE);
 
     while (node) {
         if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
@@ -128,7 +129,7 @@
 
         ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
               profile->mSupportedDevices.types(), profile->mFlags);
-
+        profile->attach(this);
         mOutputProfiles.add(profile);
         return NO_ERROR;
     } else {
@@ -154,7 +155,6 @@
         return BAD_VALUE;
     }
     sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
-    deviceDesc->mModule = this;
 
     node = root->first_child;
     while (node) {
@@ -183,7 +183,7 @@
 status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config,
                                                   audio_devices_t device, String8 address)
 {
-    sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE, this);
+    sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE);
 
     profile->mSamplingRates.add(config->sample_rate);
     profile->mChannelMasks.add(config->channel_mask);
@@ -193,6 +193,7 @@
     devDesc->mAddress = address;
     profile->mSupportedDevices.add(devDesc);
 
+    profile->attach(this);
     mOutputProfiles.add(profile);
 
     return NO_ERROR;
@@ -213,7 +214,7 @@
 status_t HwModule::addInputProfile(String8 name, const audio_config_t *config,
                                                   audio_devices_t device, String8 address)
 {
-    sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK, this);
+    sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK);
 
     profile->mSamplingRates.add(config->sample_rate);
     profile->mChannelMasks.add(config->channel_mask);
@@ -225,6 +226,7 @@
 
     ALOGV("addInputProfile() name %s rate %d mask 0x08", name.string(), config->sample_rate, config->channel_mask);
 
+    profile->attach(this);
     mInputProfiles.add(profile);
 
     return NO_ERROR;
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index 376dd22..7b6d51d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -23,9 +23,8 @@
 
 namespace android {
 
-IOProfile::IOProfile(const String8& name, audio_port_role_t role,
-                                         const sp<HwModule>& module)
-    : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module)
+IOProfile::IOProfile(const String8& name, audio_port_role_t role)
+    : AudioPort(name, AUDIO_PORT_TYPE_MIX, role)
 {
 }
 
@@ -41,7 +40,9 @@
                                     uint32_t samplingRate,
                                     uint32_t *updatedSamplingRate,
                                     audio_format_t format,
+                                    audio_format_t *updatedFormat,
                                     audio_channel_mask_t channelMask,
+                                    audio_channel_mask_t *updatedChannelMask,
                                     uint32_t flags) const
 {
     const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE;
@@ -72,7 +73,14 @@
          return false;
     }
 
-    if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) {
+    if (!audio_is_valid_format(format)) {
+        return false;
+    }
+    if (isPlaybackThread && checkExactFormat(format) != NO_ERROR) {
+        return false;
+    }
+    audio_format_t myUpdatedFormat = format;
+    if (isRecordThread && checkCompatibleFormat(format, &myUpdatedFormat) != NO_ERROR) {
         return false;
     }
 
@@ -80,8 +88,9 @@
             checkExactChannelMask(channelMask) != NO_ERROR)) {
         return false;
     }
+    audio_channel_mask_t myUpdatedChannelMask = channelMask;
     if (isRecordThread && (!audio_is_input_channel(channelMask) ||
-            checkCompatibleChannelMask(channelMask) != NO_ERROR)) {
+            checkCompatibleChannelMask(channelMask, &myUpdatedChannelMask) != NO_ERROR)) {
         return false;
     }
 
@@ -100,6 +109,12 @@
     if (updatedSamplingRate != NULL) {
         *updatedSamplingRate = myUpdatedSamplingRate;
     }
+    if (updatedFormat != NULL) {
+        *updatedFormat = myUpdatedFormat;
+    }
+    if (updatedChannelMask != NULL) {
+        *updatedChannelMask = myUpdatedChannelMask;
+    }
     return true;
 }
 
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index eadaa77..db0573f 100755
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -134,16 +134,16 @@
                                               audio_policy_dev_state_t state) = 0;
 
     /**
-     * Translate a volume index given by the UI to an amplification value for a stream type
+     * Translate a volume index given by the UI to an amplification value in dB for a stream type
      * and a device category.
      *
      * @param[in] deviceCategory for which the conversion is requested.
      * @param[in] stream type for which the conversion is requested.
      * @param[in] indexInUi index received from the UI to be translated.
      *
-     * @return amplification value matching the UI index for this given device and stream.
+     * @return amplification value in dB matching the UI index for this given device and stream.
      */
-    virtual float volIndexToAmpl(Volume::device_category deviceCategory, audio_stream_type_t stream,
+    virtual float volIndexToDb(Volume::device_category deviceCategory, audio_stream_type_t stream,
                                  int indexInUi) = 0;
 
     /**
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
index 4f5427e..6d43df2 100755
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
@@ -43,7 +43,7 @@
 
     virtual const AudioPolicyMixCollection &getAudioPolicyMixCollection() const = 0;
 
-    virtual const AudioOutputCollection &getOutputs() const = 0;
+    virtual const SwAudioOutputCollection &getOutputs() const = 0;
 
     virtual const AudioInputCollection &getInputs() const = 0;
 
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index b4d7246..50f1609 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -63,13 +63,14 @@
     return (mApmObserver != NULL) ?  NO_ERROR : NO_INIT;
 }
 
-float Engine::volIndexToAmpl(Volume::device_category category, audio_stream_type_t streamType,
+float Engine::volIndexToDb(Volume::device_category category, audio_stream_type_t streamType,
                              int indexInUi)
 {
     const StreamDescriptor &streamDesc = mApmObserver->getStreamDescriptors().valueAt(streamType);
-    return Gains::volIndexToAmpl(category, streamDesc, indexInUi);
+    return Gains::volIndexToDb(category, streamDesc, indexInUi);
 }
 
+
 status_t Engine::initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
 {
     ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
@@ -243,7 +244,7 @@
 
 routing_strategy Engine::getStrategyForUsage(audio_usage_t usage)
 {
-    const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+    const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
 
     // usage to strategy mapping
     switch (usage) {
@@ -291,7 +292,7 @@
     const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
     const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
 
-    const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+    const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
 
     uint32_t device = AUDIO_DEVICE_NONE;
     uint32_t availableOutputDevicesType = availableOutputDevices.types();
@@ -358,7 +359,7 @@
             if (((availableInputDevices.types() &
                     AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) ||
                     (((txDevice & availPrimaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
-                         (primaryOutput->getAudioPort()->mModule->mHalVersion <
+                         (primaryOutput->getAudioPort()->getModuleVersion() <
                              AUDIO_DEVICE_API_VERSION_3_0))) {
                 availableOutputDevicesType = availPrimaryOutputDevices;
             }
@@ -515,7 +516,7 @@
         if (device2 == AUDIO_DEVICE_NONE) {
             device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
         }
-        if ((device2 == AUDIO_DEVICE_NONE)) {
+        if (device2 == AUDIO_DEVICE_NONE) {
             device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE;
         }
         if (device2 == AUDIO_DEVICE_NONE) {
@@ -582,7 +583,7 @@
 {
     const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
     const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
-    const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+    const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
     audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
 
     uint32_t device = AUDIO_DEVICE_NONE;
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index f44556c..56a4748 100755
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -101,10 +101,10 @@
         {
             return mPolicyEngine->initializeVolumeCurves(isSpeakerDrcEnabled);
         }
-        virtual float volIndexToAmpl(Volume::device_category deviceCategory,
+        virtual float volIndexToDb(Volume::device_category deviceCategory,
                                      audio_stream_type_t stream,int indexInUi)
         {
-            return mPolicyEngine->volIndexToAmpl(deviceCategory, stream, indexInUi);
+            return mPolicyEngine->volIndexToDb(deviceCategory, stream, indexInUi);
         }
     private:
         Engine *mPolicyEngine;
@@ -141,7 +141,7 @@
     audio_devices_t getDeviceForStrategy(routing_strategy strategy) const;
     audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const;
 
-    float volIndexToAmpl(Volume::device_category category,
+    float volIndexToDb(Volume::device_category category,
                          audio_stream_type_t stream, int indexInUi);
     status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax);
     void initializeVolumeCurves(bool isSpeakerDrcEnabled);
diff --git a/services/audiopolicy/enginedefault/src/Gains.cpp b/services/audiopolicy/enginedefault/src/Gains.cpp
index a684fdd..78f2909 100644
--- a/services/audiopolicy/enginedefault/src/Gains.cpp
+++ b/services/audiopolicy/enginedefault/src/Gains.cpp
@@ -197,10 +197,10 @@
 };
 
 //static
-float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
-        int indexInUi)
+float Gains::volIndexToDb(Volume::device_category deviceCategory,
+                          const StreamDescriptor& streamDesc,
+                          int indexInUi)
 {
-    Volume::device_category deviceCategory = Volume::getDeviceCategory(device);
     const VolumeCurvePoint *curve = streamDesc.getVolumeCurvePoint(deviceCategory);
 
     // the volume index in the UI is relative to the min and max volume indices for this stream type
@@ -212,7 +212,7 @@
     // find what part of the curve this index volume belongs to, or if it's out of bounds
     int segment = 0;
     if (volIdx < curve[Volume::VOLMIN].mIndex) {         // out of bounds
-        return 0.0f;
+        return VOLUME_MIN_DB;
     } else if (volIdx < curve[Volume::VOLKNEE1].mIndex) {
         segment = 0;
     } else if (volIdx < curve[Volume::VOLKNEE2].mIndex) {
@@ -220,7 +220,7 @@
     } else if (volIdx <= curve[Volume::VOLMAX].mIndex) {
         segment = 2;
     } else {                                                               // out of bounds
-        return 1.0f;
+        return 0.0f;
     }
 
     // linear interpolation in the attenuation table in dB
@@ -231,17 +231,25 @@
                     ((float)(curve[segment+1].mIndex -
                             curve[segment].mIndex)) );
 
-    float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
-
-    ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f",
+    ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
             curve[segment].mIndex, volIdx,
             curve[segment+1].mIndex,
             curve[segment].mDBAttenuation,
             decibels,
-            curve[segment+1].mDBAttenuation,
-            amplification);
+            curve[segment+1].mDBAttenuation);
 
-    return amplification;
+    return decibels;
 }
 
+
+//static
+float Gains::volIndexToAmpl(Volume::device_category deviceCategory,
+                            const StreamDescriptor& streamDesc,
+                            int indexInUi)
+{
+    return Volume::DbToAmpl(volIndexToDb(deviceCategory, streamDesc, indexInUi));
+}
+
+
+
 }; // namespace android
diff --git a/services/audiopolicy/enginedefault/src/Gains.h b/services/audiopolicy/enginedefault/src/Gains.h
index b5601ca..7620b7d 100644
--- a/services/audiopolicy/enginedefault/src/Gains.h
+++ b/services/audiopolicy/enginedefault/src/Gains.h
@@ -29,8 +29,13 @@
 class Gains
 {
 public :
-    static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
-                    int indexInUi);
+    static float volIndexToAmpl(Volume::device_category deviceCategory,
+                                const StreamDescriptor& streamDesc,
+                                int indexInUi);
+
+    static float volIndexToDb(Volume::device_category deviceCategory,
+                              const StreamDescriptor& streamDesc,
+                              int indexInUi);
 
     // default volume curve
     static const VolumeCurvePoint sDefaultVolumeCurve[Volume::VOLCNT];
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 797a2b4..ba9f996 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -157,7 +157,7 @@
         // outputs must be closed after checkOutputForAllStrategies() is executed
         if (!outputs.isEmpty()) {
             for (size_t i = 0; i < outputs.size(); i++) {
-                sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+                sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
                 // close unused outputs after device disconnection or direct outputs that have been
                 // opened by checkOutputsForDevice() to query dynamic parameters
                 if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
@@ -176,18 +176,17 @@
             updateCallRouting(newDevice);
         }
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            audio_io_handle_t output = mOutputs.keyAt(i);
-            if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
-                audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i),
-                                                               true /*fromCache*/);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
+                audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
                 // do not force device change on duplicated output because if device is 0, it will
                 // also force a device 0 for the two outputs it is duplicated to which may override
                 // a valid device selection on those outputs.
-                bool force = !mOutputs.valueAt(i)->isDuplicated()
+                bool force = !desc->isDuplicated()
                         && (!device_distinguishes_on_address(device)
                                 // always force when disconnecting (a non-duplicated device)
                                 || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
-                setOutputDevice(output, newDevice, force, 0);
+                setOutputDevice(desc, newDevice, force, 0);
             }
         }
 
@@ -349,10 +348,11 @@
                                                 AUDIO_OUTPUT_FLAG_NONE,
                                                 AUDIO_FORMAT_INVALID);
         if (output != AUDIO_IO_HANDLE_NONE) {
-            sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+            sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
             ALOG_ASSERT(!outputDesc->isDuplicated(),
                         "updateCallRouting() RX device output is duplicated");
             outputDesc->toAudioPortConfig(&patch.sources[1]);
+            patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
             patch.num_sources = 2;
         }
 
@@ -395,6 +395,7 @@
             ALOG_ASSERT(!outputDesc->isDuplicated(),
                         "updateCallRouting() RX device output is duplicated");
             outputDesc->toAudioPortConfig(&patch.sources[1]);
+            patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
             patch.num_sources = 2;
         }
 
@@ -448,13 +449,13 @@
     checkOutputForAllStrategies();
     updateDevicesAndOutputs();
 
-    sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
+    sp<SwAudioOutputDescriptor> hwOutputDesc = mPrimaryOutput;
 
     int delayMs = 0;
     if (isStateInCall(state)) {
         nsecs_t sysTime = systemTime();
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
             // mute media and sonification strategies and delay device switch by the largest
             // latency of any output where either strategy is active.
             // This avoid sending the ring tone or music tail into the earpiece or headset.
@@ -464,14 +465,14 @@
                  isStrategyActive(desc, STRATEGY_SONIFICATION,
                                   SONIFICATION_HEADSET_MUSIC_DELAY,
                                   sysTime)) &&
-                    (delayMs < (int)desc->mLatency*2)) {
-                delayMs = desc->mLatency*2;
+                    (delayMs < (int)desc->latency()*2)) {
+                delayMs = desc->latency()*2;
             }
-            setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i));
-            setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+            setStrategyMute(STRATEGY_MEDIA, true, desc);
+            setStrategyMute(STRATEGY_MEDIA, false, desc, MUTE_TIME_MS,
                 getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/));
-            setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i));
-            setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+            setStrategyMute(STRATEGY_SONIFICATION, true, desc);
+            setStrategyMute(STRATEGY_SONIFICATION, false, desc, MUTE_TIME_MS,
                 getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/));
         }
     }
@@ -547,13 +548,13 @@
         updateCallRouting(newDevice);
     }
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        audio_io_handle_t output = mOutputs.keyAt(i);
-        audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/);
-        if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
-            setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+        audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/);
+        if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
+            setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE));
         }
         if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
-            applyStreamVolumes(output, newDevice, 0, true);
+            applyStreamVolumes(outputDesc, newDevice, 0, true);
         }
     }
 
@@ -584,8 +585,10 @@
         }
         for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
             sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
-            bool found = profile->isCompatibleProfile(device, String8(""), samplingRate,
-                    NULL /*updatedSamplingRate*/, format, channelMask,
+            bool found = profile->isCompatibleProfile(device, String8(""),
+                    samplingRate, NULL /*updatedSamplingRate*/,
+                    format, NULL /*updatedFormat*/,
+                    channelMask, NULL /*updatedChannelMask*/,
                     flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ?
                         AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_DIRECT);
             if (found && (mAvailableOutputDevices.types() & profile->mSupportedDevices.types())) {
@@ -621,6 +624,7 @@
                                               audio_format_t format,
                                               audio_channel_mask_t channelMask,
                                               audio_output_flags_t flags,
+                                              audio_port_handle_t selectedDeviceId,
                                               const audio_offload_info_t *offloadInfo)
 {
     audio_attributes_t attributes;
@@ -639,7 +643,7 @@
         }
         stream_type_to_audio_attributes(*stream, &attributes);
     }
-    sp<AudioOutputDescriptor> desc;
+    sp<SwAudioOutputDescriptor> desc;
     if (mPolicyMixes.getOutputForAttr(attributes, desc) == NO_ERROR) {
         ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
         if (!audio_is_linear_pcm(format)) {
@@ -675,6 +679,17 @@
     if (*output == AUDIO_IO_HANDLE_NONE) {
         return INVALID_OPERATION;
     }
+
+    // Explicit routing?
+    sp<DeviceDescriptor> deviceDesc;
+
+    for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
+        if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) {
+            deviceDesc = mAvailableOutputDevices[i];
+            break;
+        }
+    }
+    mOutputRoutes.addRoute(session, *stream, deviceDesc);
     return NO_ERROR;
 }
 
@@ -699,7 +714,8 @@
 
         if (mTestOutputs[mCurOutput] == 0) {
             ALOGV("getOutput() opening test output");
-            sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+            sp<AudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(NULL,
+                                                                               mpClientInterface);
             outputDesc->mDevice = mTestDevice;
             outputDesc->mLatency = mTestLatencyMs;
             outputDesc->mFlags =
@@ -775,10 +791,10 @@
     }
 
     if (profile != 0) {
-        sp<AudioOutputDescriptor> outputDesc = NULL;
+        sp<SwAudioOutputDescriptor> outputDesc = NULL;
 
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
             if (!desc->isDuplicated() && (profile == desc->mProfile)) {
                 outputDesc = desc;
                 // reuse direct output if currently open and configured with same parameters
@@ -795,7 +811,7 @@
         if (outputDesc != NULL) {
             closeOutput(outputDesc->mIoHandle);
         }
-        outputDesc = new AudioOutputDescriptor(profile);
+        outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface);
         outputDesc->mDevice = device;
         outputDesc->mLatency = 0;
         outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);
@@ -806,7 +822,7 @@
         if (offloadInfo != NULL) {
             config.offload_info = *offloadInfo;
         }
-        status = mpClientInterface->openOutput(profile->mModule->mHandle,
+        status = mpClientInterface->openOutput(profile->getModuleHandle(),
                                                &output,
                                                &config,
                                                &outputDesc->mDevice,
@@ -856,7 +872,6 @@
     }
 
 non_direct_output:
-
     // ignoring channel mask due to downmix capability in mixer
 
     // open a non direct output
@@ -874,7 +889,7 @@
     ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,"
             "format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);
 
-    ALOGV("getOutput() returns output %d", output);
+    ALOGV("  getOutputForDevice() returns output %d", output);
 
     return output;
 }
@@ -902,7 +917,7 @@
     audio_io_handle_t outputPrimary = 0;
 
     for (size_t i = 0; i < outputs.size(); i++) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
         if (!outputDesc->isDuplicated()) {
             // if a valid format is specified, skip output if not compatible
             if (format != AUDIO_FORMAT_INVALID) {
@@ -941,15 +956,59 @@
                                              audio_stream_type_t stream,
                                              audio_session_t session)
 {
-    ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
+    ALOGV("startOutput() output %d, stream %d, session %d",
+          output, stream, session);
     ssize_t index = mOutputs.indexOfKey(output);
     if (index < 0) {
         ALOGW("startOutput() unknown output %d", output);
         return BAD_VALUE;
     }
 
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+
+    audio_devices_t newDevice;
+    if (outputDesc->mPolicyMix != NULL) {
+        newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+    } else {
+        newDevice = AUDIO_DEVICE_NONE;
+    }
+
+    uint32_t delayMs = 0;
+
+    // Routing?
+    mOutputRoutes.incRouteActivity(session);
+
+    status_t status = startSource(outputDesc, stream, newDevice, &delayMs);
+
+    if (status != NO_ERROR) {
+        mOutputRoutes.decRouteActivity(session);
+    }
+    // Automatically enable the remote submix input when output is started on a re routing mix
+    // of type MIX_TYPE_RECORDERS
+    if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
+            outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+            setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                    outputDesc->mPolicyMix->mRegistrationId,
+                    "remote-submix");
+    }
+
+    if (delayMs != 0) {
+        usleep(delayMs * 1000);
+    }
+
+    return status;
+}
+
+status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc,
+                                             audio_stream_type_t stream,
+                                             audio_devices_t device,
+                                             uint32_t *delayMs)
+{
     // cannot start playback of STREAM_TTS if any other output is being used
     uint32_t beaconMuteLatency = 0;
+
+    *delayMs = 0;
     if (stream == AUDIO_STREAM_TTS) {
         ALOGV("\t found BEACON stream");
         if (mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -962,8 +1021,6 @@
         beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT);
     }
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
-
     // increment usage count for this stream on the requested output:
     // NOTE that the usage count is the same for duplicated output and hardware output which is
     // necessary for a correct control of hardware output routing by startOutput() and stopOutput()
@@ -971,11 +1028,8 @@
 
     if (outputDesc->mRefCount[stream] == 1) {
         // starting an output being rerouted?
-        audio_devices_t newDevice;
-        if (outputDesc->mPolicyMix != NULL) {
-            newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
-        } else {
-            newDevice = getNewOutputDevice(output, false /*fromCache*/);
+        if (device == AUDIO_DEVICE_NONE) {
+            device = getNewOutputDevice(outputDesc, false /*fromCache*/);
         }
         routing_strategy strategy = getStrategy(stream);
         bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
@@ -991,7 +1045,7 @@
                 // In this case, the audio HAL must receive the new device selection so that it can
                 // change the device currently selected by the other active output.
                 if (outputDesc->sharesHwModuleWith(desc) &&
-                    desc->device() != newDevice) {
+                    desc->device() != device) {
                     force = true;
                 }
                 // wait for audio on other active outputs to be presented when starting
@@ -1003,7 +1057,7 @@
                 }
             }
         }
-        uint32_t muteWaitMs = setOutputDevice(output, newDevice, force);
+        uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force);
 
         // handle special case for sonification while in call
         if (isInCall()) {
@@ -1012,32 +1066,18 @@
 
         // apply volume rules for current stream and device if necessary
         checkAndSetVolume(stream,
-                          mStreams[stream].getVolumeIndex(newDevice),
-                          output,
-                          newDevice);
+                          mStreams.valueFor(stream).getVolumeIndex(device),
+                          outputDesc,
+                          device);
 
         // update the outputs if starting an output with a stream that can affect notification
         // routing
         handleNotificationRoutingForStream(stream);
 
-        // Automatically enable the remote submix input when output is started on a re routing mix
-        // of type MIX_TYPE_RECORDERS
-        if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
-                outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                        outputDesc->mPolicyMix->mRegistrationId,
-                        "remote-submix");
-        }
-
         // force reevaluating accessibility routing when ringtone or alarm starts
         if (strategy == STRATEGY_SONIFICATION) {
             mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY);
         }
-
-        if (waitMs > muteWaitMs) {
-            usleep((waitMs - muteWaitMs) * 2 * 1000);
-        }
     }
     return NO_ERROR;
 }
@@ -1054,8 +1094,32 @@
         return BAD_VALUE;
     }
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
 
+    if (outputDesc->mRefCount[stream] == 1) {
+        // Automatically disable the remote submix input when output is stopped on a
+        // re routing mix of type MIX_TYPE_RECORDERS
+        if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+                outputDesc->mPolicyMix != NULL &&
+                outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+            setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                    outputDesc->mPolicyMix->mRegistrationId,
+                    "remote-submix");
+        }
+    }
+
+    // Routing?
+    if (outputDesc->mRefCount[stream] > 0) {
+        mOutputRoutes.decRouteActivity(session);
+    }
+
+    return stopSource(outputDesc, stream);
+}
+
+status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc,
+                                            audio_stream_type_t stream)
+{
     // always handle stream stop, check which stream type is stopping
     handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
 
@@ -1067,41 +1131,31 @@
     if (outputDesc->mRefCount[stream] > 0) {
         // decrement usage count of this stream on the output
         outputDesc->changeRefCount(stream, -1);
+
         // store time at which the stream was stopped - see isStreamActive()
         if (outputDesc->mRefCount[stream] == 0) {
-            // Automatically disable the remote submix input when output is stopped on a
-            // re routing mix of type MIX_TYPE_RECORDERS
-            if (audio_is_remote_submix_device(outputDesc->mDevice) &&
-                    outputDesc->mPolicyMix != NULL &&
-                    outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
-                        outputDesc->mPolicyMix->mRegistrationId,
-                        "remote-submix");
-            }
-
             outputDesc->mStopTime[stream] = systemTime();
-            audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
+            audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
             // delay the device switch by twice the latency because stopOutput() is executed when
             // the track stop() command is received and at that time the audio track buffer can
             // still contain data that needs to be drained. The latency only covers the audio HAL
             // and kernel buffers. Also the latency does not always include additional delay in the
             // audio path (audio DSP, CODEC ...)
-            setOutputDevice(output, newDevice, false, outputDesc->mLatency*2);
+            setOutputDevice(outputDesc, newDevice, false, outputDesc->latency()*2);
 
             // force restoring the device selection on other active outputs if it differs from the
             // one being selected for this output
             for (size_t i = 0; i < mOutputs.size(); i++) {
                 audio_io_handle_t curOutput = mOutputs.keyAt(i);
                 sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
-                if (curOutput != output &&
+                if (desc != outputDesc &&
                         desc->isActive() &&
                         outputDesc->sharesHwModuleWith(desc) &&
                         (newDevice != desc->device())) {
-                    setOutputDevice(curOutput,
-                                    getNewOutputDevice(curOutput, false /*fromCache*/),
+                    setOutputDevice(desc,
+                                    getNewOutputDevice(desc, false /*fromCache*/),
                                     true,
-                                    outputDesc->mLatency*2);
+                                    outputDesc->latency()*2);
                 }
             }
             // update the outputs if stopping one with a stream that can affect notification routing
@@ -1109,7 +1163,7 @@
         }
         return NO_ERROR;
     } else {
-        ALOGW("stopOutput() refcount is already 0 for output %d", output);
+        ALOGW("stopOutput() refcount is already 0");
         return INVALID_OPERATION;
     }
 }
@@ -1138,7 +1192,10 @@
     }
 #endif //AUDIO_POLICY_TEST
 
-    sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index);
+    // Routing
+    mOutputRoutes.removeRoute(session);
+
+    sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(index);
     if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
         if (desc->mDirectOpenCount <= 0) {
             ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1150,8 +1207,9 @@
             // If effects where present on the output, audioflinger moved them to the primary
             // output by default: move them back to the appropriate output.
             audio_io_handle_t dstOutput = getOutputForEffect();
-            if (dstOutput != mPrimaryOutput) {
-                mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
+            if (dstOutput != mPrimaryOutput->mIoHandle) {
+                mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX,
+                                               mPrimaryOutput->mIoHandle, dstOutput);
             }
             mpClientInterface->onAudioPortListUpdate();
         }
@@ -1189,7 +1247,7 @@
 
     if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
             strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
-        status_t ret = mPolicyMixes.getInputMixForAttr(*attr, policyMix);
+        status_t ret = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
         if (ret != NO_ERROR) {
             return ret;
         }
@@ -1247,48 +1305,54 @@
         }
     }
 
-    sp<IOProfile> profile = getInputProfile(device, address,
-                                            samplingRate, format, channelMask,
-                                            flags);
-    if (profile == 0) {
-        //retry without flags
-        audio_input_flags_t log_flags = flags;
-        flags = AUDIO_INPUT_FLAG_NONE;
+    // find a compatible input profile (not necessarily identical in parameters)
+    sp<IOProfile> profile;
+    // samplingRate and flags may be updated by getInputProfile
+    uint32_t profileSamplingRate = samplingRate;
+    audio_format_t profileFormat = format;
+    audio_channel_mask_t profileChannelMask = channelMask;
+    audio_input_flags_t profileFlags = flags;
+    for (;;) {
         profile = getInputProfile(device, address,
-                                  samplingRate, format, channelMask,
-                                  flags);
-        if (profile == 0) {
+                                  profileSamplingRate, profileFormat, profileChannelMask,
+                                  profileFlags);
+        if (profile != 0) {
+            break; // success
+        } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
+            profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
+        } else { // fail
             ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u,"
                     "format %#x, channelMask 0x%X, flags %#x",
-                    device, samplingRate, format, channelMask, log_flags);
+                    device, samplingRate, format, channelMask, flags);
             return BAD_VALUE;
         }
     }
 
-    if (profile->mModule->mHandle == 0) {
-        ALOGE("getInputForAttr(): HW module %s not opened", profile->mModule->mName);
+    if (profile->getModuleHandle() == 0) {
+        ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
         return NO_INIT;
     }
 
     audio_config_t config = AUDIO_CONFIG_INITIALIZER;
-    config.sample_rate = samplingRate;
-    config.channel_mask = channelMask;
-    config.format = format;
+    config.sample_rate = profileSamplingRate;
+    config.channel_mask = profileChannelMask;
+    config.format = profileFormat;
 
-    status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
+    status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                    input,
                                                    &config,
                                                    &device,
                                                    address,
                                                    halInputSource,
-                                                   flags);
+                                                   profileFlags);
 
     // only accept input with the exact requested set of parameters
     if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE ||
-        (samplingRate != config.sample_rate) ||
-        (format != config.format) ||
-        (channelMask != config.channel_mask)) {
-        ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d, channelMask %x",
+        (profileSamplingRate != config.sample_rate) ||
+        (profileFormat != config.format) ||
+        (profileChannelMask != config.channel_mask)) {
+        ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d,"
+                " channelMask %x",
                 samplingRate, format, channelMask);
         if (*input != AUDIO_IO_HANDLE_NONE) {
             mpClientInterface->closeInput(*input);
@@ -1300,15 +1364,15 @@
     inputDesc->mInputSource = inputSource;
     inputDesc->mRefCount = 0;
     inputDesc->mOpenRefCount = 1;
-    inputDesc->mSamplingRate = samplingRate;
-    inputDesc->mFormat = format;
-    inputDesc->mChannelMask = channelMask;
+    inputDesc->mSamplingRate = profileSamplingRate;
+    inputDesc->mFormat = profileFormat;
+    inputDesc->mChannelMask = profileChannelMask;
     inputDesc->mDevice = device;
     inputDesc->mSessions.add(session);
     inputDesc->mIsSoundTrigger = isSoundTrigger;
     inputDesc->mPolicyMix = policyMix;
 
-    ALOGV("getInputForAttr() returns input type = %d", inputType);
+    ALOGV("getInputForAttr() returns input type = %d", *inputType);
 
     addInput(*input, inputDesc);
     mpClientInterface->onAudioPortListUpdate();
@@ -1505,8 +1569,8 @@
                                                   audio_devices_t device)
 {
 
-    if ((index < mStreams[stream].getVolumeIndexMin()) ||
-            (index > mStreams[stream].getVolumeIndexMax())) {
+    if ((index < mStreams.valueFor(stream).getVolumeIndexMin()) ||
+            (index > mStreams.valueFor(stream).getVolumeIndexMax())) {
         return BAD_VALUE;
     }
     if (!audio_is_output_device(device)) {
@@ -1514,7 +1578,7 @@
     }
 
     // Force max volume if stream cannot be muted
-    if (!mStreams.canBeMuted(stream)) index = mStreams[stream].getVolumeIndexMax();
+    if (!mStreams.canBeMuted(stream)) index = mStreams.valueFor(stream).getVolumeIndexMax();
 
     ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d",
           stream, device, index);
@@ -1543,16 +1607,17 @@
     }
     status_t status = NO_ERROR;
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        audio_devices_t curDevice = Volume::getDeviceForVolume(mOutputs.valueAt(i)->device());
+        sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+        audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
         if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) {
-            status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
+            status_t volStatus = checkAndSetVolume(stream, index, desc, curDevice);
             if (volStatus != NO_ERROR) {
                 status = volStatus;
             }
         }
         if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & accessibilityDevice) != 0)) {
             status_t volStatus = checkAndSetVolume(AUDIO_STREAM_ACCESSIBILITY,
-                                                   index, mOutputs.keyAt(i), curDevice);
+                                                   index, desc, curDevice);
         }
     }
     return status;
@@ -1575,7 +1640,7 @@
     }
     device = Volume::getDeviceForVolume(device);
 
-    *index =  mStreams[stream].getVolumeIndex(device);
+    *index =  mStreams.valueFor(stream).getVolumeIndex(device);
     ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
     return NO_ERROR;
 }
@@ -1599,7 +1664,7 @@
     audio_io_handle_t outputDeepBuffer = 0;
 
     for (size_t i = 0; i < outputs.size(); i++) {
-        sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+        sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
         ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags);
         if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
             outputOffloaded = outputs[i];
@@ -1653,6 +1718,16 @@
     return mEffects.registerEffect(desc, io, strategy, session, id);
 }
 
+bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+    return mOutputs.isStreamActive(stream, inPastMs);
+}
+
+bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+    return mOutputs.isStreamActiveRemotely(stream, inPastMs);
+}
+
 bool AudioPolicyManager::isSourceActive(audio_source_t source) const
 {
     for (size_t i = 0; i < mInputs.size(); i++) {
@@ -1803,7 +1878,7 @@
     snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
     result.append(buffer);
 
-    snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput);
+    snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput->mIoHandle);
     result.append(buffer);
     snprintf(buffer, SIZE, " Phone state: %d\n", mEngine->getPhoneState());
     result.append(buffer);
@@ -2021,7 +2096,7 @@
     }
 
     if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
         if (outputDesc == NULL) {
             ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
             return BAD_VALUE;
@@ -2055,9 +2130,12 @@
                                                            patch->sources[0].sample_rate,
                                                            NULL,  // updatedSamplingRate
                                                            patch->sources[0].format,
+                                                           NULL,  // updatedFormat
                                                            patch->sources[0].channel_mask,
+                                                           NULL,  // updatedChannelMask
                                                            AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
-                ALOGV("createAudioPatch() profile not supported for device %08x", devDesc->type());
+                ALOGV("createAudioPatch() profile not supported for device %08x",
+                        devDesc->type());
                 return INVALID_OPERATION;
             }
             devices.add(devDesc);
@@ -2069,7 +2147,7 @@
         // TODO: reconfigure output format and channels here
         ALOGV("createAudioPatch() setting device %08x on output %d",
               devices.types(), outputDesc->mIoHandle);
-        setOutputDevice(outputDesc->mIoHandle, devices.types(), true, 0, handle);
+        setOutputDevice(outputDesc, devices.types(), true, 0, handle);
         index = mAudioPatches.indexOfKey(*handle);
         if (index >= 0) {
             if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2109,7 +2187,9 @@
                                                           patch->sinks[0].sample_rate,
                                                           NULL, /*updatedSampleRate*/
                                                           patch->sinks[0].format,
+                                                          NULL, /*updatedFormat*/
                                                           patch->sinks[0].channel_mask,
+                                                          NULL, /*updatedChannelMask*/
                                                           // FIXME for the parameter type,
                                                           // and the NONE
                                                           (audio_output_flags_t)
@@ -2163,8 +2243,12 @@
                 }
                 sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
 
-                if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
-                    // only one sink supported when connected devices across HW modules
+                // create a software bridge in PatchPanel if:
+                // - source and sink devices are on differnt HW modules OR
+                // - audio HAL version is < 3.0
+                if ((srcDeviceDesc->getModuleHandle() != sinkDeviceDesc->getModuleHandle()) ||
+                        (srcDeviceDesc->mModule->mHalVersion < AUDIO_DEVICE_API_VERSION_3_0)) {
+                    // support only one sink device for now to simplify output selection logic
                     if (patch->num_sinks > 1) {
                         return INVALID_OPERATION;
                     }
@@ -2181,6 +2265,7 @@
                             return INVALID_OPERATION;
                         }
                         outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
+                        newPatch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
                         newPatch.num_sources = 2;
                     }
                 }
@@ -2242,14 +2327,14 @@
     struct audio_patch *patch = &patchDesc->mPatch;
     patchDesc->mUid = mUidCached;
     if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
         if (outputDesc == NULL) {
             ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
             return BAD_VALUE;
         }
 
-        setOutputDevice(outputDesc->mIoHandle,
-                        getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+        setOutputDevice(outputDesc,
+                        getNewOutputDevice(outputDesc, true /*fromCache*/),
                        true,
                        0,
                        NULL);
@@ -2308,7 +2393,7 @@
     sp<AudioPortConfig> audioPortConfig;
     if (config->type == AUDIO_PORT_TYPE_MIX) {
         if (config->role == AUDIO_PORT_ROLE_SOURCE) {
-            sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
+            sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
             if (outputDesc == NULL) {
                 return BAD_VALUE;
             }
@@ -2390,7 +2475,6 @@
 #ifdef AUDIO_POLICY_TEST
     Thread(false),
 #endif //AUDIO_POLICY_TEST
-    mPrimaryOutput((audio_io_handle_t)0),
     mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
     mA2dpSuspended(false),
     mSpeakerDrcEnabled(false),
@@ -2474,7 +2558,8 @@
             if ((profileType & outputDeviceTypes) == 0) {
                 continue;
             }
-            sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
+            sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
+                                                                                 mpClientInterface);
 
             outputDesc->mDevice = profileType;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -2482,7 +2567,7 @@
             config.channel_mask = outputDesc->mChannelMask;
             config.format = outputDesc->mFormat;
             audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
-            status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle,
+            status_t status = mpClientInterface->openOutput(outProfile->getModuleHandle(),
                                                             &output,
                                                             &config,
                                                             &outputDesc->mDevice,
@@ -2510,10 +2595,10 @@
                 }
                 if (mPrimaryOutput == 0 &&
                         outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
-                    mPrimaryOutput = output;
+                    mPrimaryOutput = outputDesc;
                 }
                 addOutput(output, outputDesc);
-                setOutputDevice(output,
+                setOutputDevice(outputDesc,
                                 outputDesc->mDevice,
                                 true);
             }
@@ -2558,7 +2643,7 @@
             config.channel_mask = inputDesc->mChannelMask;
             config.format = inputDesc->mFormat;
             audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
-            status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle,
+            status_t status = mpClientInterface->openInput(inProfile->getModuleHandle(),
                                                            &input,
                                                            &config,
                                                            &inputDesc->mDevice,
@@ -2620,7 +2705,7 @@
     if (mPrimaryOutput != 0) {
         AudioParameter outputCmd = AudioParameter();
         outputCmd.addInt(String8("set_id"), 0);
-        mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
+        mpClientInterface->setParameters(mPrimaryOutput->mIoHandle, outputCmd.toString());
 
         mTestDevice = AUDIO_DEVICE_OUT_SPEAKER;
         mTestSamplingRate = 44100;
@@ -2760,20 +2845,21 @@
             if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
                 param.remove(String8("test_cmd_policy_reopen"));
 
-                sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
-                mpClientInterface->closeOutput(mPrimaryOutput);
+                mpClientInterface->closeOutput(mpClientInterface->closeOutput(mPrimaryOutput););
 
-                audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle;
+                audio_module_handle_t moduleHandle = mPrimaryOutput->getModuleHandle();
 
-                removeOutput(mPrimaryOutput);
-                sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+                removeOutput(mPrimaryOutput->mIoHandle);
+                sp<SwAudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL,
+                                                                               mpClientInterface);
                 outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER;
                 audio_config_t config = AUDIO_CONFIG_INITIALIZER;
                 config.sample_rate = outputDesc->mSamplingRate;
                 config.channel_mask = outputDesc->mChannelMask;
                 config.format = outputDesc->mFormat;
+                audio_io_handle_t handle;
                 status_t status = mpClientInterface->openOutput(moduleHandle,
-                                                                &mPrimaryOutput,
+                                                                &handle,
                                                                 &config,
                                                                 &outputDesc->mDevice,
                                                                 String8(""),
@@ -2787,10 +2873,11 @@
                     outputDesc->mSamplingRate = config.sample_rate;
                     outputDesc->mChannelMask = config.channel_mask;
                     outputDesc->mFormat = config.format;
+                    mPrimaryOutput = outputDesc;
                     AudioParameter outputCmd = AudioParameter();
                     outputCmd.addInt(String8("set_id"), 0);
-                    mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
-                    addOutput(mPrimaryOutput, outputDesc);
+                    mpClientInterface->setParameters(handle, outputCmd.toString());
+                    addOutput(handle, outputDesc);
                 }
             }
 
@@ -2822,7 +2909,7 @@
 
 // ---
 
-void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc)
 {
     outputDesc->setIoHandle(output);
     mOutputs.add(output, outputDesc);
@@ -2841,7 +2928,7 @@
     nextAudioPortGeneration();
 }
 
-void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+void AudioPolicyManager::findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
         const audio_devices_t device /*in*/,
         const String8 address /*in*/,
         SortedVector<audio_io_handle_t>& outputs /*out*/) {
@@ -2860,7 +2947,7 @@
                                                    const String8 address)
 {
     audio_devices_t device = devDesc->type();
-    sp<AudioOutputDescriptor> desc;
+    sp<SwAudioOutputDescriptor> desc;
     // erase all current sample rates, formats and channel masks
     devDesc->clearCapabilities();
 
@@ -2868,7 +2955,7 @@
         // first list already open outputs that can be routed to this device
         for (size_t i = 0; i < mOutputs.size(); i++) {
             desc = mOutputs.valueAt(i);
-            if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) {
+            if (!desc->isDuplicated() && (desc->supportedDevices() & device)) {
                 if (!device_distinguishes_on_address(device)) {
                     ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i));
                     outputs.add(mOutputs.keyAt(i));
@@ -2927,7 +3014,7 @@
 
             ALOGV("opening output for device %08x with params %s profile %p",
                                                       device, address.string(), profile.get());
-            desc = new AudioOutputDescriptor(profile);
+            desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
             desc->mDevice = device;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
             config.sample_rate = desc->mSamplingRate;
@@ -2937,7 +3024,7 @@
             config.offload_info.channel_mask = desc->mChannelMask;
             config.offload_info.format = desc->mFormat;
             audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
-            status_t status = mpClientInterface->openOutput(profile->mModule->mHandle,
+            status_t status = mpClientInterface->openOutput(profile->getModuleHandle(),
                                                             &output,
                                                             &config,
                                                             &desc->mDevice,
@@ -3007,7 +3094,7 @@
                     config.offload_info.sample_rate = config.sample_rate;
                     config.offload_info.channel_mask = config.channel_mask;
                     config.offload_info.format = config.format;
-                    status = mpClientInterface->openOutput(profile->mModule->mHandle,
+                    status = mpClientInterface->openOutput(profile->getModuleHandle(),
                                                            &output,
                                                            &config,
                                                            &desc->mDevice,
@@ -3032,7 +3119,7 @@
                                   address.string());
                         }
                         policyMix->setOutput(desc);
-                        desc->mPolicyMix = &(policyMix->getMix());
+                        desc->mPolicyMix = policyMix->getMix();
 
                     } else if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) {
                         // no duplicated output for direct outputs and
@@ -3040,28 +3127,29 @@
                         audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
 
                         // set initial stream volume for device
-                        applyStreamVolumes(output, device, 0, true);
+                        applyStreamVolumes(desc, device, 0, true);
 
                         //TODO: configure audio effect output stage here
 
                         // open a duplicating output thread for the new output and the primary output
-                        duplicatedOutput = mpClientInterface->openDuplicateOutput(output,
-                                                                                  mPrimaryOutput);
+                        duplicatedOutput =
+                                mpClientInterface->openDuplicateOutput(output,
+                                                                       mPrimaryOutput->mIoHandle);
                         if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
                             // add duplicated output descriptor
-                            sp<AudioOutputDescriptor> dupOutputDesc =
-                                    new AudioOutputDescriptor(NULL);
-                            dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput);
-                            dupOutputDesc->mOutput2 = mOutputs.valueFor(output);
+                            sp<SwAudioOutputDescriptor> dupOutputDesc =
+                                    new SwAudioOutputDescriptor(NULL, mpClientInterface);
+                            dupOutputDesc->mOutput1 = mPrimaryOutput;
+                            dupOutputDesc->mOutput2 = desc;
                             dupOutputDesc->mSamplingRate = desc->mSamplingRate;
                             dupOutputDesc->mFormat = desc->mFormat;
                             dupOutputDesc->mChannelMask = desc->mChannelMask;
                             dupOutputDesc->mLatency = desc->mLatency;
                             addOutput(duplicatedOutput, dupOutputDesc);
-                            applyStreamVolumes(duplicatedOutput, device, 0, true);
+                            applyStreamVolumes(dupOutputDesc, device, 0, true);
                         } else {
                             ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
-                                    mPrimaryOutput, output);
+                                    mPrimaryOutput->mIoHandle, output);
                             mpClientInterface->closeOutput(output);
                             removeOutput(output);
                             nextAudioPortGeneration();
@@ -3083,7 +3171,7 @@
                 if (device_distinguishes_on_address(device)) {
                     ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)",
                             device, address.string());
-                    setOutputDevice(output, device, true/*force*/, 0/*delay*/,
+                    setOutputDevice(desc, device, true/*force*/, 0/*delay*/,
                             NULL/*patch handle*/, address.string());
                 }
                 ALOGV("checkOutputsForDevice(): adding output %d", output);
@@ -3101,10 +3189,9 @@
             if (!desc->isDuplicated()) {
                 // exact match on device
                 if (device_distinguishes_on_address(device) &&
-                        (desc->mProfile->mSupportedDevices.types() == device)) {
+                        (desc->supportedDevices() == device)) {
                     findIoHandlesByAddress(desc, device, address, outputs);
-                } else if (!(desc->mProfile->mSupportedDevices.types()
-                        & mAvailableOutputDevices.types())) {
+                } else if (!(desc->supportedDevices() & mAvailableOutputDevices.types())) {
                     ALOGV("checkOutputsForDevice(): disconnecting adding output %d",
                             mOutputs.keyAt(i));
                     outputs.add(mOutputs.keyAt(i));
@@ -3212,7 +3299,7 @@
             config.channel_mask = desc->mChannelMask;
             config.format = desc->mFormat;
             audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
-            status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
+            status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                            &input,
                                                            &config,
                                                            &desc->mDevice,
@@ -3339,7 +3426,7 @@
 {
     ALOGV("closeOutput(%d)", output);
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
     if (outputDesc == NULL) {
         ALOGW("closeOutput() unknown output %d", output);
         return;
@@ -3348,7 +3435,7 @@
 
     // look for duplicated outputs connected to the output being removed.
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
+        sp<SwAudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
         if (dupOutputDesc->isDuplicated() &&
                 (dupOutputDesc->mOutput1 == outputDesc ||
                 dupOutputDesc->mOutput2 == outputDesc)) {
@@ -3417,8 +3504,9 @@
     mInputs.removeItem(input);
 }
 
-SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
-                                                                        AudioOutputCollection openOutputs)
+SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(
+                                                                audio_devices_t device,
+                                                                SwAudioOutputCollection openOutputs)
 {
     SortedVector<audio_io_handle_t> outputs;
 
@@ -3459,14 +3547,14 @@
     // associated with policies in the "before" and "after" output vectors
     ALOGVV("checkOutputForStrategy(): policy related outputs");
     for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
-        const sp<AudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
+        const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
         if (desc != 0 && desc->mPolicyMix != NULL) {
             srcOutputs.add(desc->mIoHandle);
             ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
         }
     }
     for (size_t i = 0 ; i < mOutputs.size() ; i++) {
-        const sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+        const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
         if (desc != 0 && desc->mPolicyMix != NULL) {
             dstOutputs.add(desc->mIoHandle);
             ALOGVV(" new outputs: adding %d", desc->mIoHandle);
@@ -3478,10 +3566,10 @@
               strategy, srcOutputs[0], dstOutputs[0]);
         // mute strategy while moving tracks from one output to another
         for (size_t i = 0; i < srcOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
             if (isStrategyActive(desc, strategy)) {
-                setStrategyMute(strategy, true, srcOutputs[i]);
-                setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
+                setStrategyMute(strategy, true, desc);
+                setStrategyMute(strategy, false, desc, MUTE_TIME_MS, newDevice);
             }
         }
 
@@ -3578,12 +3666,11 @@
     }
 }
 
-audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache)
+audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+                                                       bool fromCache)
 {
     audio_devices_t device = AUDIO_DEVICE_NONE;
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
     ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
     if (index >= 0) {
         sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -3761,9 +3848,9 @@
         ALOGV("\t muting %d", mute);
         uint32_t maxLatency = 0;
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
             setStreamMute(AUDIO_STREAM_TTS, mute/*on*/,
-                    desc->mIoHandle,
+                    desc,
                     0 /*delay*/, AUDIO_DEVICE_NONE);
             const uint32_t latency = desc->latency() * 2;
             if (latency > maxLatency) {
@@ -3779,6 +3866,21 @@
 audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                          bool fromCache)
 {
+    // Routing
+    // see if we have an explicit route
+    // scan the whole RouteMap, for each entry, convert the stream type to a strategy
+    // (getStrategy(stream)).
+    // if the strategy from the stream type in the RouteMap is the same as the argument above,
+    // and activity count is non-zero
+    // the device = the device from the descriptor in the RouteMap, and exit.
+    for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
+        sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
+        routing_strategy strat = getStrategy(route->mStreamType);
+        if (strat == strategy && route->mDeviceDescriptor != 0 /*&& route->mActivityCount != 0*/) {
+            return route->mDeviceDescriptor->type();
+        }
+    }
+
     if (fromCache) {
         ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
               strategy, mDeviceForStrategy[strategy]);
@@ -3812,7 +3914,7 @@
 
     for (size_t i = 0; i < NUM_STRATEGIES; i++) {
         audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
-        curDevice = curDevice & outputDesc->mProfile->mSupportedDevices.types();
+        curDevice = curDevice & outputDesc->supportedDevices();
         bool mute = shouldMute && (curDevice & device) && (curDevice != device);
         bool doMute = false;
 
@@ -3831,10 +3933,9 @@
                         == AUDIO_DEVICE_NONE) {
                     continue;
                 }
-                audio_io_handle_t curOutput = mOutputs.keyAt(j);
-                ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d",
-                      mute ? "muting" : "unmuting", i, curDevice, curOutput);
-                setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs);
+                ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x)",
+                      mute ? "muting" : "unmuting", i, curDevice);
+                setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs);
                 if (isStrategyActive(desc, (routing_strategy)i)) {
                     if (mute) {
                         // FIXME: should not need to double latency if volume could be applied
@@ -3859,9 +3960,9 @@
         }
         for (size_t i = 0; i < NUM_STRATEGIES; i++) {
             if (isStrategyActive(outputDesc, (routing_strategy)i)) {
-                setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle);
+                setStrategyMute((routing_strategy)i, true, outputDesc);
                 // do tempMute unmute after twice the mute wait time
-                setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle,
+                setStrategyMute((routing_strategy)i, false, outputDesc,
                                 muteWaitMs *2, device);
             }
         }
@@ -3876,36 +3977,35 @@
     return 0;
 }
 
-uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
+uint32_t AudioPolicyManager::setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                              audio_devices_t device,
                                              bool force,
                                              int delayMs,
                                              audio_patch_handle_t *patchHandle,
                                              const char* address)
 {
-    ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+    ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs);
     AudioParameter param;
     uint32_t muteWaitMs;
 
     if (outputDesc->isDuplicated()) {
-        muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs);
-        muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs);
+        muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);
+        muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);
         return muteWaitMs;
     }
     // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
     // output profile
     if ((device != AUDIO_DEVICE_NONE) &&
-            ((device & outputDesc->mProfile->mSupportedDevices.types()) == 0)) {
+            ((device & outputDesc->supportedDevices()) == 0)) {
         return 0;
     }
 
     // filter devices according to output selected
-    device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices.types());
+    device = (audio_devices_t)(device & outputDesc->supportedDevices());
 
     audio_devices_t prevDevice = outputDesc->mDevice;
 
-    ALOGV("setOutputDevice() prevDevice %04x", prevDevice);
+    ALOGV("setOutputDevice() prevDevice 0x%04x", prevDevice);
 
     if (device != AUDIO_DEVICE_NONE) {
         outputDesc->mDevice = device;
@@ -3918,10 +4018,10 @@
     //  AND force is not specified
     //  AND the output is connected by a valid audio patch.
     // Doing this check here allows the caller to call setOutputDevice() without conditions
-    if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && !force &&
-            outputDesc->mPatchHandle != 0) {
-        ALOGV("setOutputDevice() setting same device %04x or null device for output %d",
-              device, output);
+    if ((device == AUDIO_DEVICE_NONE || device == prevDevice) &&
+        !force &&
+        outputDesc->mPatchHandle != 0) {
+        ALOGV("setOutputDevice() setting same device 0x%04x or null device", device);
         return muteWaitMs;
     }
 
@@ -3929,7 +4029,7 @@
 
     // do the routing
     if (device == AUDIO_DEVICE_NONE) {
-        resetOutputDevice(output, delayMs, NULL);
+        resetOutputDevice(outputDesc, delayMs, NULL);
     } else {
         DeviceVector deviceList = (address == NULL) ?
                 mAvailableOutputDevices.getDevicesFromType(device)
@@ -3996,16 +4096,15 @@
     }
 
     // update stream volumes according to new device
-    applyStreamVolumes(output, device, delayMs);
+    applyStreamVolumes(outputDesc, device, delayMs);
 
     return muteWaitMs;
 }
 
-status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
+status_t AudioPolicyManager::resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                                int delayMs,
                                                audio_patch_handle_t *patchHandle)
 {
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
     ssize_t index;
     if (patchHandle) {
         index = mAudioPatches.indexOfKey(*patchHandle);
@@ -4115,12 +4214,15 @@
 sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,
                                                   String8 address,
                                                   uint32_t& samplingRate,
-                                                  audio_format_t format,
-                                                  audio_channel_mask_t channelMask,
+                                                  audio_format_t& format,
+                                                  audio_channel_mask_t& channelMask,
                                                   audio_input_flags_t flags)
 {
     // Choose an input profile based on the requested capture parameters: select the first available
     // profile supporting all requested parameters.
+    //
+    // TODO: perhaps isCompatibleProfile should return a "matching" score so we can return
+    // the best matching profile, not the first one.
 
     for (size_t i = 0; i < mHwModules.size(); i++)
     {
@@ -4133,7 +4235,11 @@
             // profile->log();
             if (profile->isCompatibleProfile(device, address, samplingRate,
                                              &samplingRate /*updatedSamplingRate*/,
-                                             format, channelMask, (audio_output_flags_t) flags)) {
+                                             format,
+                                             &format /*updatedFormat*/,
+                                             channelMask,
+                                             &channelMask /*updatedChannelMask*/,
+                                             (audio_output_flags_t) flags)) {
 
                 return profile;
             }
@@ -4162,17 +4268,10 @@
 }
 
 float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
-                                        int index,
-                                        audio_io_handle_t output,
-                                        audio_devices_t device)
+                                            int index,
+                                            audio_devices_t device)
 {
-    float volume = 1.0;
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
-    if (device == AUDIO_DEVICE_NONE) {
-        device = outputDesc->device();
-    }
-    volume = mEngine->volIndexToAmpl(Volume::getDeviceCategory(device), stream, index);
+    float volumeDb = mEngine->volIndexToDb(Volume::getDeviceCategory(device), stream, index);
 
     // if a headset is connected, apply the following rules to ring tones and notifications
     // to avoid sound level bursts in user's ears:
@@ -4190,41 +4289,39 @@
                 || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) &&
                     (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
             mStreams.canBeMuted(stream)) {
-        volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
+        volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
         // when the phone is ringing we must consider that music could have been paused just before
         // by the music application and behave as if music was active if the last music track was
         // just stopped
         if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) ||
                 mLimitRingtoneVolume) {
             audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/);
-            float musicVol = computeVolume(AUDIO_STREAM_MUSIC,
-                               mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice),
-                               output,
+            float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC,
+                                 mStreams.valueFor(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice),
                                musicDevice);
-            float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ?
-                                musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
-            if (volume > minVol) {
-                volume = minVol;
-                ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
+            float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
+                    musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB;
+            if (volumeDb > minVolDB) {
+                volumeDb = minVolDB;
+                ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB);
             }
         }
     }
 
-    return volume;
+    return volumeDb;
 }
 
 status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
-                                               int index,
-                                               audio_io_handle_t output,
-                                               audio_devices_t device,
-                                               int delayMs,
-                                               bool force)
+                                                   int index,
+                                                   const sp<AudioOutputDescriptor>& outputDesc,
+                                                   audio_devices_t device,
+                                                   int delayMs,
+                                                   bool force)
 {
-
     // do not change actual stream volume if the stream is muted
-    if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+    if (outputDesc->mMuteCount[stream] != 0) {
         ALOGVV("checkAndSetVolume() stream %d muted count %d",
-              stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+              stream, outputDesc->mMuteCount[stream]);
         return NO_ERROR;
     }
     audio_policy_forced_cfg_t forceUseForComm =
@@ -4237,45 +4334,28 @@
         return INVALID_OPERATION;
     }
 
-    float volume = computeVolume(stream, index, output, device);
-    // unit gain if rerouting to external policy
-    if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
-        ssize_t index = mOutputs.indexOfKey(output);
-        if (index >= 0) {
-            sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
-            if (outputDesc->mPolicyMix != NULL) {
-                ALOGV("max gain when rerouting for output=%d", output);
-                volume = 1.0f;
-            }
-        }
+    if (device == AUDIO_DEVICE_NONE) {
+        device = outputDesc->device();
+    }
 
+    float volumeDb = computeVolume(stream, index, device);
+    if (outputDesc->isFixedVolume(device)) {
+        volumeDb = 0.0f;
     }
-    // We actually change the volume if:
-    // - the float value returned by computeVolume() changed
-    // - the force flag is set
-    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
-            force) {
-        mOutputs.valueFor(output)->mCurVolume[stream] = volume;
-        ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
-        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
-        // enabled
-        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
-            mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs);
-        }
-        mpClientInterface->setStreamVolume(stream, volume, output, delayMs);
-    }
+
+    outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
 
     if (stream == AUDIO_STREAM_VOICE_CALL ||
         stream == AUDIO_STREAM_BLUETOOTH_SCO) {
         float voiceVolume;
         // Force voice volume to max for bluetooth SCO as volume is managed by the headset
         if (stream == AUDIO_STREAM_VOICE_CALL) {
-            voiceVolume = (float)index/(float)mStreams[stream].getVolumeIndexMax();
+            voiceVolume = (float)index/(float)mStreams.valueFor(stream).getVolumeIndexMax();
         } else {
             voiceVolume = 1.0;
         }
 
-        if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) {
+        if (voiceVolume != mLastVoiceVolume && outputDesc == mPrimaryOutput) {
             mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
             mLastVoiceVolume = voiceVolume;
         }
@@ -4284,20 +4364,20 @@
     return NO_ERROR;
 }
 
-void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output,
-                                            audio_devices_t device,
-                                            int delayMs,
-                                            bool force)
+void AudioPolicyManager::applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+                                                audio_devices_t device,
+                                                int delayMs,
+                                                bool force)
 {
-    ALOGVV("applyStreamVolumes() for output %d and device %x", output, device);
+    ALOGVV("applyStreamVolumes() for device %08x", device);
 
     for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
         if (stream == AUDIO_STREAM_PATCH) {
             continue;
         }
         checkAndSetVolume((audio_stream_type_t)stream,
-                          mStreams[stream].getVolumeIndex(device),
-                          output,
+                          mStreams.valueFor((audio_stream_type_t)stream).getVolumeIndex(device),
+                          outputDesc,
                           device,
                           delayMs,
                           force);
@@ -4305,10 +4385,10 @@
 }
 
 void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
-                                         bool on,
-                                         audio_io_handle_t output,
-                                         int delayMs,
-                                         audio_devices_t device)
+                                             bool on,
+                                             const sp<AudioOutputDescriptor>& outputDesc,
+                                             int delayMs,
+                                             audio_devices_t device)
 {
     ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
     for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
@@ -4316,32 +4396,31 @@
             continue;
         }
         if (getStrategy((audio_stream_type_t)stream) == strategy) {
-            setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device);
+            setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device);
         }
     }
 }
 
 void AudioPolicyManager::setStreamMute(audio_stream_type_t stream,
-                                       bool on,
-                                       audio_io_handle_t output,
-                                       int delayMs,
-                                       audio_devices_t device)
+                                           bool on,
+                                           const sp<AudioOutputDescriptor>& outputDesc,
+                                           int delayMs,
+                                           audio_devices_t device)
 {
-    const StreamDescriptor &streamDesc = mStreams[stream];
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+    const StreamDescriptor& streamDesc = mStreams.valueFor(stream);
     if (device == AUDIO_DEVICE_NONE) {
         device = outputDesc->device();
     }
 
-    ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x",
-          stream, on, output, outputDesc->mMuteCount[stream], device);
+    ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
+          stream, on, outputDesc->mMuteCount[stream], device);
 
     if (on) {
         if (outputDesc->mMuteCount[stream] == 0) {
             if (streamDesc.canBeMuted() &&
                     ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) ||
                      (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) {
-                checkAndSetVolume(stream, 0, output, device, delayMs);
+                checkAndSetVolume(stream, 0, outputDesc, device, delayMs);
             }
         }
         // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
@@ -4354,7 +4433,7 @@
         if (--outputDesc->mMuteCount[stream] == 0) {
             checkAndSetVolume(stream,
                               streamDesc.getVolumeIndex(device),
-                              output,
+                              outputDesc,
                               device,
                               delayMs);
         }
@@ -4373,7 +4452,7 @@
     const routing_strategy stream_strategy = getStrategy(stream);
     if ((stream_strategy == STRATEGY_SONIFICATION) ||
             ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
+        sp<SwAudioOutputDescriptor> outputDesc = mPrimaryOutput;
         ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
                 stream, starting, outputDesc->mDevice, stateChange);
         if (outputDesc->mRefCount[stream]) {
@@ -4406,6 +4485,70 @@
     }
 }
 
+// --- SessionRoute class implementation
+void AudioPolicyManager::SessionRoute::log(const char* prefix) {
+    ALOGI("%s[SessionRoute strm:0x%X, sess:0x%X, dev:0x%X refs:%d act:%d",
+          prefix, mStreamType, mSession,
+          mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
+          mRefCount, mActivityCount);
+}
+
+// --- SessionRouteMap class implementation
+bool AudioPolicyManager::SessionRouteMap::hasRoute(audio_session_t session)
+{
+    return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
+}
+
+void AudioPolicyManager::SessionRouteMap::addRoute(audio_session_t session,
+                                                   audio_stream_type_t streamType,
+                                                   sp<DeviceDescriptor> deviceDescriptor)
+{
+    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+    if (route != NULL) {
+        route->mRefCount++;
+        route->mDeviceDescriptor = deviceDescriptor;
+    } else {
+        route = new AudioPolicyManager::SessionRoute(session, streamType, deviceDescriptor);
+        route->mRefCount++;
+        add(session, route);
+    }
+}
+
+void AudioPolicyManager::SessionRouteMap::removeRoute(audio_session_t session)
+{
+    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+    if (route != 0) {
+        ALOG_ASSERT(route->mRefCount > 0);
+        --route->mRefCount;
+        if (route->mRefCount <= 0) {
+            removeItem(session);
+        }
+    }
+}
+
+int AudioPolicyManager::SessionRouteMap::incRouteActivity(audio_session_t session)
+{
+    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+    return route != 0 ? ++(route->mActivityCount) : -1;
+}
+
+int AudioPolicyManager::SessionRouteMap::decRouteActivity(audio_session_t session)
+{
+    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+    if (route != 0 && route->mActivityCount > 0) {
+        return --(route->mActivityCount);
+    } else {
+        return -1;
+    }
+}
+
+void AudioPolicyManager::SessionRouteMap::log(const char* caption) {
+    ALOGI("%s ----", caption);
+    for(size_t index = 0; index < size(); index++) {
+        valueAt(index)->log("  ");
+    }
+}
+
 void AudioPolicyManager::defaultAudioPolicyConfig(void)
 {
     sp<HwModule> module;
@@ -4417,7 +4560,8 @@
 
     module = new HwModule("primary");
 
-    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
+    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE);
+    profile->attach(module);
     profile->mSamplingRates.add(44100);
     profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
     profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4425,7 +4569,8 @@
     profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
     module->mOutputProfiles.add(profile);
 
-    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
+    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK);
+    profile->attach(module);
     profile->mSamplingRates.add(8000);
     profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
     profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 02b678a..fe6b986 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -49,8 +49,11 @@
 
 // Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB
 #define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5
+#define SONIFICATION_HEADSET_VOLUME_FACTOR_DB (-6)
 // Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB
 #define SONIFICATION_HEADSET_VOLUME_MIN  0.016
+#define SONIFICATION_HEADSET_VOLUME_MIN_DB  (-36)
+
 // Time in milliseconds during which we consider that music is still active after a music
 // track was stopped - see computeVolume()
 #define SONIFICATION_HEADSET_MUSIC_DELAY  5000
@@ -110,6 +113,7 @@
                                           audio_format_t format,
                                           audio_channel_mask_t channelMask,
                                           audio_output_flags_t flags,
+                                          audio_port_handle_t selectedDeviceId,
                                           const audio_offload_info_t *offloadInfo);
         virtual status_t startOutput(audio_io_handle_t output,
                                      audio_stream_type_t stream,
@@ -172,19 +176,15 @@
             return mEffects.setEffectEnabled(id, enabled);
         }
 
-        virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const
-        {
-            return mOutputs.isStreamActive(stream, inPastMs);
-        }
+        virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
         // return whether a stream is playing remotely, override to change the definition of
         //   local/remote playback, used for instance by notification manager to not make
         //   media players lose audio focus when not playing locally
         //   For the base implementation, "remotely" means playing during screen mirroring which
         //   uses an output for playback with a non-empty, non "0" address.
-        virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const
-        {
-            return mOutputs.isStreamActiveRemotely(stream, inPastMs);
-        }
+        virtual bool isStreamActiveRemotely(audio_stream_type_t stream,
+                                            uint32_t inPastMs = 0) const;
+
         virtual bool isSourceActive(audio_source_t source) const;
 
         virtual status_t dump(int fd);
@@ -227,6 +227,46 @@
         // return the strategy corresponding to a given stream type
         routing_strategy getStrategy(audio_stream_type_t stream) const;
 
+protected:
+        class SessionRoute : public RefBase
+        {
+        public:
+            friend class SessionRouteMap;
+            SessionRoute(audio_session_t session,
+                         audio_stream_type_t streamType,
+                         sp<DeviceDescriptor> deviceDescriptor)
+                : mSession(session),
+                  mStreamType(streamType),
+                  mDeviceDescriptor(deviceDescriptor),
+                  mRefCount(0),
+                  mActivityCount(0) {}
+
+            audio_session_t         mSession;
+            audio_stream_type_t     mStreamType;
+
+            sp<DeviceDescriptor>    mDeviceDescriptor;
+
+            // "reference" counting
+            int mRefCount;       // +/- on references
+            int mActivityCount;  // +/- on start/stop
+
+            void log(const char* prefix);
+        };
+
+        class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute>>
+        {
+         public:
+            bool hasRoute(audio_session_t session);
+            void addRoute(audio_session_t session, audio_stream_type_t streamType,
+                          sp<DeviceDescriptor> deviceDescriptor);
+            void removeRoute(audio_session_t session);
+
+            int incRouteActivity(audio_session_t session);
+            int decRouteActivity(audio_session_t session);
+
+            void log(const char* caption);
+        };
+
         // From AudioPolicyManagerObserver
         virtual const AudioPatchCollection &getAudioPatches() const
         {
@@ -240,7 +280,7 @@
         {
             return mPolicyMixes;
         }
-        virtual const AudioOutputCollection &getOutputs() const
+        virtual const SwAudioOutputCollection &getOutputs() const
         {
             return mOutputs;
         }
@@ -265,7 +305,7 @@
             return mDefaultOutputDevice;
         }
 protected:
-        void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc);
+        void addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc);
         void removeOutput(audio_io_handle_t output);
         void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc);
 
@@ -288,13 +328,13 @@
 
         // change the route of the specified output. Returns the number of ms we have slept to
         // allow new routing to take effect in certain cases.
-        virtual uint32_t setOutputDevice(audio_io_handle_t output,
+        virtual uint32_t setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                              audio_devices_t device,
                              bool force = false,
                              int delayMs = 0,
                              audio_patch_handle_t *patchHandle = NULL,
                              const char* address = NULL);
-        status_t resetOutputDevice(audio_io_handle_t output,
+        status_t resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                    int delayMs = 0,
                                    audio_patch_handle_t *patchHandle = NULL);
         status_t setInputDevice(audio_io_handle_t input,
@@ -309,29 +349,31 @@
 
         // compute the actual volume for a given stream according to the requested index and a particular
         // device
-        virtual float computeVolume(audio_stream_type_t stream, int index,
-                                    audio_io_handle_t output, audio_devices_t device);
+        virtual float computeVolume(audio_stream_type_t stream,
+                                    int index,
+                                    audio_devices_t device);
 
         // check that volume change is permitted, compute and send new volume to audio hardware
         virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index,
-                                           audio_io_handle_t output,
+                                           const sp<AudioOutputDescriptor>& outputDesc,
                                            audio_devices_t device,
                                            int delayMs = 0, bool force = false);
 
         // apply all stream volumes to the specified output and device
-        void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false);
+        void applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+                                audio_devices_t device, int delayMs = 0, bool force = false);
 
         // Mute or unmute all streams handled by the specified strategy on the specified output
         void setStrategyMute(routing_strategy strategy,
                              bool on,
-                             audio_io_handle_t output,
+                             const sp<AudioOutputDescriptor>& outputDesc,
                              int delayMs = 0,
                              audio_devices_t device = (audio_devices_t)0);
 
         // Mute or unmute the stream on the specified output
         void setStreamMute(audio_stream_type_t stream,
                            bool on,
-                           audio_io_handle_t output,
+                           const sp<AudioOutputDescriptor>& outputDesc,
                            int delayMs = 0,
                            audio_devices_t device = (audio_devices_t)0);
 
@@ -384,7 +426,8 @@
         // must be called every time a condition that affects the device choice for a given output is
         // changed: connected device, phone state, force use, output start, output stop..
         // see getDeviceForStrategy() for the use of fromCache parameter
-        audio_devices_t getNewOutputDevice(audio_io_handle_t output, bool fromCache);
+        audio_devices_t getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+                                           bool fromCache);
 
         // updates cache of device used by all strategies (mDeviceForStrategy[])
         // must be called every time a condition that affects the device choice for a given strategy is
@@ -412,7 +455,7 @@
 #endif //AUDIO_POLICY_TEST
 
         SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device,
-                                                            AudioOutputCollection openOutputs);
+                                                            SwAudioOutputCollection openOutputs);
         bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1,
                                            SortedVector<audio_io_handle_t>& outputs2);
 
@@ -427,12 +470,12 @@
         audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs,
                                        audio_output_flags_t flags,
                                        audio_format_t format);
-        // samplingRate parameter is an in/out and so may be modified
+        // samplingRate, format, channelMask are in/out and so may be modified
         sp<IOProfile> getInputProfile(audio_devices_t device,
                                       String8 address,
                                       uint32_t& samplingRate,
-                                      audio_format_t format,
-                                      audio_channel_mask_t channelMask,
+                                      audio_format_t& format,
+                                      audio_channel_mask_t& channelMask,
                                       audio_input_flags_t flags);
         sp<IOProfile> getProfileForDirectOutput(audio_devices_t device,
                                                        uint32_t samplingRate,
@@ -453,28 +496,39 @@
 
         audio_devices_t availablePrimaryOutputDevices() const
         {
-            return mOutputs.getSupportedDevices(mPrimaryOutput) & mAvailableOutputDevices.types();
+            return mPrimaryOutput->supportedDevices() & mAvailableOutputDevices.types();
         }
         audio_devices_t availablePrimaryInputDevices() const
         {
-            return mAvailableInputDevices.getDevicesFromHwModule(
-                        mOutputs.valueFor(mPrimaryOutput)->getModuleHandle());
+            return mAvailableInputDevices.getDevicesFromHwModule(mPrimaryOutput->getModuleHandle());
         }
 
         void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0);
 
+        status_t startSource(sp<AudioOutputDescriptor> outputDesc,
+                             audio_stream_type_t stream,
+                             audio_devices_t device,
+                             uint32_t *delayMs);
+        status_t stopSource(sp<AudioOutputDescriptor> outputDesc,
+                            audio_stream_type_t stream);
+
         uid_t mUidCached;
         AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
-        audio_io_handle_t mPrimaryOutput;              // primary output handle
+        sp<SwAudioOutputDescriptor> mPrimaryOutput;     // primary output descriptor
         // list of descriptors for outputs currently opened
-        AudioOutputCollection mOutputs;
+
+        SwAudioOutputCollection mOutputs;
         // copy of mOutputs before setDeviceConnectionState() opens new outputs
         // reset to mOutputs when updateDevicesAndOutputs() is called.
-        AudioOutputCollection mPreviousOutputs;
+        SwAudioOutputCollection mPreviousOutputs;
         AudioInputCollection mInputs;     // list of input descriptors
+
         DeviceVector  mAvailableOutputDevices; // all available output devices
         DeviceVector  mAvailableInputDevices;  // all available input devices
 
+        SessionRouteMap mOutputRoutes;
+        SessionRouteMap mInputRoutes;
+
         StreamDescriptorCollection mStreams; // stream descriptors for volume control
         bool    mLimitRingtoneVolume;        // limit ringtone volume to music volume if headset connected
         audio_devices_t mDeviceForStrategy[NUM_STRATEGIES];
@@ -539,7 +593,7 @@
         //   in mProfile->mSupportedDevices) matches the device whose address is to be matched.
         // see deviceDistinguishesOnAddress(audio_devices_t) for whether the device type is one
         //   where addresses are used to distinguish between one connected device and another.
-        void findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+        void findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
                 const audio_devices_t device /*in*/,
                 const String8 address /*in*/,
                 SortedVector<audio_io_handle_t>& outputs /*out*/);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index e9ff8389..a763151 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -150,6 +150,7 @@
                                               audio_format_t format,
                                               audio_channel_mask_t channelMask,
                                               audio_output_flags_t flags,
+                                              int mSelectedDeviceId,
                                               const audio_offload_info_t *offloadInfo)
 {
     if (mAudioPolicyManager == NULL) {
@@ -158,7 +159,7 @@
     ALOGV("getOutput()");
     Mutex::Autolock _l(mLock);
     return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate,
-                                    format, channelMask, flags, offloadInfo);
+                                    format, channelMask, flags, mSelectedDeviceId, offloadInfo);
 }
 
 status_t AudioPolicyService::startOutput(audio_io_handle_t output,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
index 5a91192..372a9fa 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
@@ -569,6 +569,7 @@
                                               audio_format_t format,
                                               audio_channel_mask_t channelMask,
                                               audio_output_flags_t flags,
+                                              int selectedDeviceId __unused,
                                               const audio_offload_info_t *offloadInfo)
 {
     if (attr != NULL) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 0378384..f8dabd3 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -84,6 +84,7 @@
                                       audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                       audio_channel_mask_t channelMask = 0,
                                       audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+                                      int selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
                                       const audio_offload_info_t *offloadInfo = NULL);
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 6f44aee..f53f425 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -121,7 +121,8 @@
         }
         case CAMERA_DEVICE_API_VERSION_3_0:
         case CAMERA_DEVICE_API_VERSION_3_1:
-        case CAMERA_DEVICE_API_VERSION_3_2: {
+        case CAMERA_DEVICE_API_VERSION_3_2:
+        case CAMERA_DEVICE_API_VERSION_3_3: {
             sp<ZslProcessor3> zslProc =
                     new ZslProcessor3(this, mCaptureSequencer);
             mZslProcessor = zslProc;