Merge "Check for overflows when parsing PSSH"
diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp
index 66d6913..4217bc6 100644
--- a/camera/camera2/CaptureRequest.cpp
+++ b/camera/camera2/CaptureRequest.cpp
@@ -81,6 +81,13 @@
         mSurfaceList.push_back(surface);
     }
 
+    int isReprocess = 0;
+    if ((err = parcel->readInt32(&isReprocess)) != OK) {
+        ALOGE("%s: Failed to read reprocessing from parcel", __FUNCTION__);
+        return err;
+    }
+    mIsReprocess = (isReprocess != 0);
+
     return OK;
 }
 
@@ -118,6 +125,8 @@
         parcel->writeStrongBinder(binder);
     }
 
+    parcel->writeInt32(mIsReprocess ? 1 : 0);
+
     return OK;
 }
 
diff --git a/camera/camera2/ICameraDeviceCallbacks.cpp b/camera/camera2/ICameraDeviceCallbacks.cpp
index 4cc7b5d..f599879 100644
--- a/camera/camera2/ICameraDeviceCallbacks.cpp
+++ b/camera/camera2/ICameraDeviceCallbacks.cpp
@@ -37,6 +37,7 @@
     CAMERA_IDLE,
     CAPTURE_STARTED,
     RESULT_RECEIVED,
+    PREPARED
 };
 
 class BpCameraDeviceCallbacks: public BpInterface<ICameraDeviceCallbacks>
@@ -80,7 +81,6 @@
         data.writeNoException();
     }
 
-
     void onResultReceived(const CameraMetadata& metadata,
             const CaptureResultExtras& resultExtras) {
         ALOGV("onResultReceived");
@@ -93,6 +93,17 @@
         remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
         data.writeNoException();
     }
+
+    void onPrepared(int streamId)
+    {
+        ALOGV("onPrepared");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
+        data.writeInt32(streamId);
+        remote()->transact(PREPARED, data, &reply, IBinder::FLAG_ONEWAY);
+        data.writeNoException();
+    }
+
 };
 
 IMPLEMENT_META_INTERFACE(CameraDeviceCallbacks,
@@ -160,6 +171,15 @@
             data.readExceptionCode();
             return NO_ERROR;
         } break;
+        case PREPARED: {
+            ALOGV("onPrepared");
+            CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
+            CaptureResultExtras result;
+            int streamId = data.readInt32();
+            onPrepared(streamId);
+            data.readExceptionCode();
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index 89c6fb7..9700258 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -42,10 +42,13 @@
     END_CONFIGURE,
     DELETE_STREAM,
     CREATE_STREAM,
+    CREATE_INPUT_STREAM,
+    GET_INPUT_SURFACE,
     CREATE_DEFAULT_REQUEST,
     GET_CAMERA_INFO,
     WAIT_UNTIL_IDLE,
-    FLUSH
+    FLUSH,
+    PREPARE
 };
 
 namespace {
@@ -225,6 +228,50 @@
         return reply.readInt32();
     }
 
+    virtual status_t createInputStream(int width, int height, int format)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+        data.writeInt32(width);
+        data.writeInt32(height);
+        data.writeInt32(format);
+
+        remote()->transact(CREATE_INPUT_STREAM, data, &reply);
+
+        reply.readExceptionCode();
+        return reply.readInt32();
+    }
+
+    // get the buffer producer of the input stream
+    virtual status_t getInputBufferProducer(
+            sp<IGraphicBufferProducer> *producer) {
+        if (producer == NULL) {
+            return BAD_VALUE;
+        }
+
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+
+        remote()->transact(GET_INPUT_SURFACE, data, &reply);
+
+        reply.readExceptionCode();
+        status_t result = reply.readInt32() ;
+        if (result != OK) {
+            return result;
+        }
+
+        sp<IGraphicBufferProducer> bp = NULL;
+        if (reply.readInt32() != 0) {
+            String16 name = readMaybeEmptyString16(reply);
+            bp = interface_cast<IGraphicBufferProducer>(
+                    reply.readStrongBinder());
+        }
+
+        *producer = bp;
+
+        return *producer == NULL ? INVALID_OPERATION : OK;
+    }
+
     // Create a request object from a template.
     virtual status_t createDefaultRequest(int templateId,
                                           /*out*/
@@ -302,6 +349,20 @@
         return res;
     }
 
+    virtual status_t prepare(int streamId)
+    {
+        ALOGV("prepare");
+        Parcel data, reply;
+
+        data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+        data.writeInt32(streamId);
+
+        remote()->transact(PREPARE, data, &reply);
+
+        reply.readExceptionCode();
+        return reply.readInt32();
+    }
+
 private:
 
 
@@ -409,7 +470,35 @@
 
             return NO_ERROR;
         } break;
+        case CREATE_INPUT_STREAM: {
+            CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+            int width, height, format;
 
+            width = data.readInt32();
+            height = data.readInt32();
+            format = data.readInt32();
+            status_t ret = createInputStream(width, height, format);
+
+            reply->writeNoException();
+            reply->writeInt32(ret);
+            return NO_ERROR;
+
+        } break;
+        case GET_INPUT_SURFACE: {
+            CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+
+            sp<IGraphicBufferProducer> bp;
+            status_t ret = getInputBufferProducer(&bp);
+            sp<IBinder> b(IInterface::asBinder(ret == OK ? bp : NULL));
+
+            reply->writeNoException();
+            reply->writeInt32(ret);
+            reply->writeInt32(1);
+            reply->writeString16(String16("camera input")); // name of surface
+            reply->writeStrongBinder(b);
+
+            return NO_ERROR;
+        } break;
         case CREATE_DEFAULT_REQUEST: {
             CHECK_INTERFACE(ICameraDeviceUser, data, reply);
 
@@ -471,6 +560,14 @@
             reply->writeInt32(endConfigure());
             return NO_ERROR;
         } break;
+        case PREPARE: {
+            CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+            int streamId = data.readInt32();
+            reply->writeNoException();
+            reply->writeInt32(prepare(streamId));
+            return NO_ERROR;
+        } break;
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 0e3bc68..20c0094 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -17,7 +17,8 @@
 	$(TOP)/frameworks/native/include/media/openmax \
 	external/jpeg \
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -40,7 +41,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -63,7 +65,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -87,7 +90,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -110,7 +114,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -133,7 +138,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -157,7 +163,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -199,7 +206,8 @@
 LOCAL_STATIC_LIBRARIES:= \
 	libstagefright_mediafilter
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
@@ -222,7 +230,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp
index ac1a547..50913cd 100644
--- a/cmds/stagefright/SimplePlayer.cpp
+++ b/cmds/stagefright/SimplePlayer.cpp
@@ -21,6 +21,7 @@
 #include "SimplePlayer.h"
 
 #include <gui/Surface.h>
+
 #include <media/AudioTrack.h>
 #include <media/ICrypto.h>
 #include <media/IMediaHTTPService.h>
@@ -29,7 +30,6 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/NuMediaExtractor.h>
 
 namespace android {
@@ -73,8 +73,7 @@
         surface = new Surface(bufferProducer);
     }
 
-    msg->setObject(
-            "native-window", new NativeWindowWrapper(surface));
+    msg->setObject("surface", surface);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
@@ -132,10 +131,8 @@
                 err = INVALID_OPERATION;
             } else {
                 sp<RefBase> obj;
-                CHECK(msg->findObject("native-window", &obj));
-
-                mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get());
-
+                CHECK(msg->findObject("surface", &obj));
+                mSurface = static_cast<Surface *>(obj.get());
                 err = OK;
             }
 
@@ -324,7 +321,7 @@
 
         err = state->mCodec->configure(
                 format,
-                isVideo ? mNativeWindow->getSurfaceTextureClient() : NULL,
+                isVideo ? mSurface : NULL,
                 NULL /* crypto */,
                 0 /* flags */);
 
@@ -411,7 +408,7 @@
     mStateByTrackIndex.clear();
     mCodecLooper.clear();
     mExtractor.clear();
-    mNativeWindow.clear();
+    mSurface.clear();
     mPath.clear();
 
     return OK;
@@ -428,12 +425,12 @@
             err = state->mCodec->dequeueInputBuffer(&index);
 
             if (err == OK) {
-                ALOGV("dequeued input buffer on track %d",
+                ALOGV("dequeued input buffer on track %zu",
                       mStateByTrackIndex.keyAt(i));
 
                 state->mAvailInputBufferIndices.push_back(index);
             } else {
-                ALOGV("dequeueInputBuffer on track %d returned %d",
+                ALOGV("dequeueInputBuffer on track %zu returned %d",
                       mStateByTrackIndex.keyAt(i), err);
             }
         } while (err == OK);
@@ -448,7 +445,7 @@
                     &info.mFlags);
 
             if (err == OK) {
-                ALOGV("dequeued output buffer on track %d",
+                ALOGV("dequeued output buffer on track %zu",
                       mStateByTrackIndex.keyAt(i));
 
                 state->mAvailOutputBufferInfos.push_back(info);
@@ -459,7 +456,7 @@
                 err = state->mCodec->getOutputBuffers(&state->mBuffers[1]);
                 CHECK_EQ(err, (status_t)OK);
             } else {
-                ALOGV("dequeueOutputBuffer on track %d returned %d",
+                ALOGV("dequeueOutputBuffer on track %zu returned %d",
                       mStateByTrackIndex.keyAt(i), err);
             }
         } while (err == OK
@@ -502,7 +499,7 @@
                     0);
             CHECK_EQ(err, (status_t)OK);
 
-            ALOGV("enqueued input data on track %d", trackIndex);
+            ALOGV("enqueued input data on track %zu", trackIndex);
 
             err = mExtractor->advance();
             CHECK_EQ(err, (status_t)OK);
@@ -528,8 +525,8 @@
                 bool release = true;
 
                 if (lateByUs > 30000ll) {
-                    ALOGI("track %d buffer late by %lld us, dropping.",
-                          mStateByTrackIndex.keyAt(i), lateByUs);
+                    ALOGI("track %zu buffer late by %lld us, dropping.",
+                          mStateByTrackIndex.keyAt(i), (long long)lateByUs);
                     state->mCodec->releaseOutputBuffer(info->mIndex);
                 } else {
                     if (state->mAudioTrack != NULL) {
@@ -558,8 +555,8 @@
                     break;
                 }
             } else {
-                ALOGV("track %d buffer early by %lld us.",
-                      mStateByTrackIndex.keyAt(i), -lateByUs);
+                ALOGV("track %zu buffer early by %lld us.",
+                      mStateByTrackIndex.keyAt(i), (long long)-lateByUs);
                 break;
             }
         }
@@ -569,7 +566,7 @@
 }
 
 status_t SimplePlayer::onOutputFormatChanged(
-        size_t trackIndex, CodecState *state) {
+        size_t trackIndex __unused, CodecState *state) {
     sp<AMessage> format;
     status_t err = state->mCodec->getOutputFormat(&format);
 
@@ -640,7 +637,7 @@
     if (delayUs > 2000ll) {
         ALOGW("AudioTrack::write took %lld us, numFramesAvailableToWrite=%u, "
               "numFramesWritten=%u",
-              delayUs, numFramesAvailableToWrite, numFramesWritten);
+              (long long)delayUs, numFramesAvailableToWrite, numFramesWritten);
     }
 
     info->mOffset += nbytes;
diff --git a/cmds/stagefright/SimplePlayer.h b/cmds/stagefright/SimplePlayer.h
index 0a06059..ae9dfd2 100644
--- a/cmds/stagefright/SimplePlayer.h
+++ b/cmds/stagefright/SimplePlayer.h
@@ -23,10 +23,10 @@
 struct ABuffer;
 struct ALooper;
 struct AudioTrack;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
 struct MediaCodec;
-struct NativeWindowWrapper;
 struct NuMediaExtractor;
+class Surface;
 
 struct SimplePlayer : public AHandler {
     SimplePlayer();
@@ -84,7 +84,7 @@
 
     State mState;
     AString mPath;
-    sp<NativeWindowWrapper> mNativeWindow;
+    sp<Surface> mSurface;
 
     sp<NuMediaExtractor> mExtractor;
     sp<ALooper> mCodecLooper;
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 7b0de24..6e9e6ec 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -18,6 +18,8 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
+#include <utils/String16.h>
+
 #include <binder/ProcessState.h>
 #include <media/mediarecorder.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -34,7 +36,7 @@
 
 static void usage(const char* name)
 {
-    fprintf(stderr, "Usage: %s [-d duration] [-m] [-w] [<output-file>]\n", name);
+    fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [<output-file>]\n", name);
     fprintf(stderr, "Encodes either a sine wave or microphone input to AMR format\n");
     fprintf(stderr, "    -d    duration in seconds, default 5 seconds\n");
     fprintf(stderr, "    -m    use microphone for input, default sine source\n");
@@ -85,6 +87,7 @@
         // talk into the appropriate microphone for the duration
         source = new AudioSource(
                 AUDIO_SOURCE_MIC,
+                String16(),
                 kSampleRate,
                 channels);
     } else {
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index d987250..dae9bbe 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -108,7 +108,7 @@
             continue;
         }
 
-        ALOGV("selecting track %d", i);
+        ALOGV("selecting track %zu", i);
 
         err = extractor->selectTrack(i);
         CHECK_EQ(err, (status_t)OK);
@@ -151,7 +151,7 @@
         CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
         CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers));
 
-        ALOGV("got %d input and %d output buffers",
+        ALOGV("got %zu input and %zu output buffers",
               state->mInBuffers.size(), state->mOutBuffers.size());
     }
 
@@ -172,7 +172,7 @@
                 err = state->mCodec->dequeueInputBuffer(&index, kTimeout);
 
                 if (err == OK) {
-                    ALOGV("filling input buffer %d", index);
+                    ALOGV("filling input buffer %zu", index);
 
                     const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
 
@@ -209,7 +209,7 @@
                         state->mCodec->dequeueInputBuffer(&index, kTimeout);
 
                     if (err == OK) {
-                        ALOGV("signalling input EOS on track %d", i);
+                        ALOGV("signalling input EOS on track %zu", i);
 
                         err = state->mCodec->queueInputBuffer(
                                 index,
@@ -258,8 +258,8 @@
                     kTimeout);
 
             if (err == OK) {
-                ALOGV("draining output buffer %d, time = %lld us",
-                      index, presentationTimeUs);
+                ALOGV("draining output buffer %zu, time = %lld us",
+                      index, (long long)presentationTimeUs);
 
                 ++state->mNumBuffersDecoded;
                 state->mNumBytesDecoded += size;
@@ -293,7 +293,7 @@
                 CHECK_EQ((status_t)OK,
                          state->mCodec->getOutputBuffers(&state->mOutBuffers));
 
-                ALOGV("got %d output buffers", state->mOutBuffers.size());
+                ALOGV("got %zu output buffers", state->mOutBuffers.size());
             } else if (err == INFO_FORMAT_CHANGED) {
                 sp<AMessage> format;
                 CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format));
@@ -313,17 +313,17 @@
         CHECK_EQ((status_t)OK, state->mCodec->release());
 
         if (state->mIsAudio) {
-            printf("track %zu: %" PRId64 " bytes received. %.2f KB/sec\n",
+            printf("track %zu: %lld bytes received. %.2f KB/sec\n",
                    i,
-                   state->mNumBytesDecoded,
+                   (long long)state->mNumBytesDecoded,
                    state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
         } else {
-            printf("track %zu: %" PRId64 " frames decoded, %.2f fps. %" PRId64
+            printf("track %zu: %lld frames decoded, %.2f fps. %lld"
                     " bytes received. %.2f KB/sec\n",
                    i,
-                   state->mNumBuffersDecoded,
+                   (long long)state->mNumBuffersDecoded,
                    state->mNumBuffersDecoded * 1E6 / elapsedTimeUs,
-                   state->mNumBytesDecoded,
+                   (long long)state->mNumBytesDecoded,
                    state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
         }
     }
@@ -418,7 +418,7 @@
         ssize_t displayWidth = info.w;
         ssize_t displayHeight = info.h;
 
-        ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
+        ALOGV("display is %zd x %zd\n", displayWidth, displayHeight);
 
         control = composerClient->createSurface(
                 String8("A Surface"),
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index f77b38b..1183112 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -81,7 +81,7 @@
         return OK;
     }
 
-    status_t handleSetParameters(const sp<AMessage> &msg) {
+    status_t handleSetParameters(const sp<AMessage> &msg __unused) {
         return OK;
     }
 
@@ -101,7 +101,7 @@
         return OK;
     }
 
-    status_t handleSetParameters(const sp<AMessage> &msg) {
+    status_t handleSetParameters(const sp<AMessage> &msg __unused) {
         return OK;
     }
 
@@ -121,7 +121,7 @@
         return OK;
     }
 
-    status_t handleSetParameters(const sp<AMessage> &msg) {
+    status_t handleSetParameters(const sp<AMessage> &msg __unused) {
         return OK;
     }
 
@@ -597,7 +597,7 @@
 
             if (err == OK) {
                 ALOGV("draining decoded buffer %zu, time = %lld us",
-                        frame.index, frame.presentationTimeUs);
+                        frame.index, (long long)frame.presentationTimeUs);
 
                 ++(state->mNumBuffersDecoded);
 
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp
index 461b56c..36fa3b5 100644
--- a/cmds/stagefright/muxer.cpp
+++ b/cmds/stagefright/muxer.cpp
@@ -53,7 +53,6 @@
 using namespace android;
 
 static int muxing(
-        const android::sp<android::ALooper> &looper,
         const char *path,
         bool useAudio,
         bool useVideo,
@@ -137,14 +136,19 @@
             }
         }
 
-        ALOGV("selecting track %d", i);
+        ALOGV("selecting track %zu", i);
 
         err = extractor->selectTrack(i);
         CHECK_EQ(err, (status_t)OK);
 
         ssize_t newTrackIndex = muxer->addTrack(format);
-        CHECK_GE(newTrackIndex, 0);
-        trackIndexMap.add(i, newTrackIndex);
+        if (newTrackIndex < 0) {
+            fprintf(stderr, "%s track (%zu) unsupported by muxer\n",
+                    isAudio ? "audio" : "video",
+                    i);
+        } else {
+            trackIndexMap.add(i, newTrackIndex);
+        }
     }
 
     int64_t muxerStartTimeUs = ALooper::GetNowUs();
@@ -163,7 +167,12 @@
             ALOGV("saw input eos, err %d", err);
             sawInputEOS = true;
             break;
+        } else if (trackIndexMap.indexOfKey(trackIndex) < 0) {
+            // ALOGV("skipping input from unsupported track %zu", trackIndex);
+            extractor->advance();
+            continue;
         } else {
+            // ALOGV("reading sample from track index %zu\n", trackIndex);
             err = extractor->readSampleData(newBuffer);
             CHECK_EQ(err, (status_t)OK);
 
@@ -308,7 +317,7 @@
     sp<ALooper> looper = new ALooper;
     looper->start();
 
-    int result = muxing(looper, argv[0], useAudio, useVideo, outputFileName,
+    int result = muxing(argv[0], useAudio, useVideo, outputFileName,
                         enableTrim, trimStartTimeMs, trimEndTimeMs, rotationDegrees);
 
     looper->stop();
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index fdc352e..594c933 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -32,13 +32,13 @@
 
 using namespace android;
 
+static const int32_t kAudioBitRate = 12200;
+#if 0
 static const int32_t kFramerate = 24;  // fps
 static const int32_t kIFramesIntervalSec = 1;
 static const int32_t kVideoBitRate = 512 * 1024;
-static const int32_t kAudioBitRate = 12200;
 static const int64_t kDurationUs = 10000000LL;  // 10 seconds
 
-#if 0
 class DummySource : public MediaSource {
 
 public:
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 172dc36..0d64d2f 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -38,10 +38,10 @@
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/Utils.h>
 
 #include <gui/SurfaceComposerClient.h>
+#include <gui/Surface.h>
 
 #include "include/ESDS.h"
 
@@ -154,8 +154,7 @@
                 sp<AMessage> format = makeFormat(mSource->getFormat());
 
                 if (mSurface != NULL) {
-                    format->setObject(
-                            "native-window", new NativeWindowWrapper(mSurface));
+                    format->setObject("surface", mSurface);
                 }
 
                 mCodec->initiateSetup(format);
@@ -328,14 +327,14 @@
 
             CHECK(size >= 7);
             CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
-            uint8_t profile = ptr[1];
-            uint8_t level = ptr[3];
+            uint8_t profile __unused = ptr[1];
+            uint8_t level __unused = ptr[3];
 
             // There is decodable content out there that fails the following
             // assertion, let's be lenient for now...
             // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
 
-            size_t lengthSize = 1 + (ptr[4] & 3);
+            size_t lengthSize __unused = 1 + (ptr[4] & 3);
 
             // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
             // violates it...
@@ -491,7 +490,7 @@
 
                 if (sizeNeeded > sizeLeft) {
                     if (outBuffer->size() == 0) {
-                        ALOGE("Unable to fit even a single input buffer of size %d.",
+                        ALOGE("Unable to fit even a single input buffer of size %zu.",
                              sizeNeeded);
                     }
                     CHECK_GT(outBuffer->size(), 0u);
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 318b56d..a9c6eda 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -965,7 +965,7 @@
     OMXClient client;
     status_t err = client.connect();
 
-    for (int k = 0; k < argc; ++k) {
+    for (int k = 0; k < argc && err == OK; ++k) {
         bool syncInfoPresent = true;
 
         const char *filename = argv[k];
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 0566d14..1a40e53 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -269,7 +269,7 @@
         : mEOS(false) {
     }
 
-    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
+    virtual void notify(int msg, int ext1 __unused, int ext2 __unused, const Parcel *obj __unused) {
         Mutex::Autolock autoLock(mLock);
 
         if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) {
@@ -318,7 +318,7 @@
     ssize_t displayWidth = info.w;
     ssize_t displayHeight = info.h;
 
-    ALOGV("display is %d x %d\n", displayWidth, displayHeight);
+    ALOGV("display is %zd x %zd\n", displayWidth, displayHeight);
 
     sp<SurfaceControl> control =
         composerClient->createSurface(
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index 3f62ed7..b90da1b 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -34,6 +34,7 @@
 #include "IDrmManagerService.h"
 
 #define INVALID_BUFFER_LENGTH -1
+#define MAX_BINDER_TRANSACTION_SIZE ((1*1024*1024)-(4096*2))
 
 using namespace android;
 
@@ -933,7 +934,12 @@
 
         //Filling DRM info
         const int infoType = data.readInt32();
-        const int bufferSize = data.readInt32();
+        const uint32_t bufferSize = data.readInt32();
+
+        if (bufferSize > data.dataAvail()) {
+            return BAD_VALUE;
+        }
+
         char* buffer = NULL;
         if (0 < bufferSize) {
             buffer = (char *)data.readInplace(bufferSize);
@@ -986,6 +992,9 @@
 
         const int size = data.readInt32();
         for (int index = 0; index < size; ++index) {
+            if (!data.dataAvail()) {
+                break;
+            }
             const String8 key(data.readString8());
             if (key == String8("FileDescriptorKey")) {
                 char buffer[16];
@@ -1035,7 +1044,12 @@
         const int uniqueId = data.readInt32();
 
         //Filling DRM Rights
-        const int bufferSize = data.readInt32();
+        const uint32_t bufferSize = data.readInt32();
+        if (bufferSize > data.dataAvail()) {
+            reply->writeInt32(BAD_VALUE);
+            return DRM_NO_ERROR;
+        }
+
         const DrmBuffer drmBuffer((char *)data.readInplace(bufferSize), bufferSize);
 
         const String8 mimeType(data.readString8());
@@ -1206,10 +1220,13 @@
         const int convertId = data.readInt32();
 
         //Filling input data
-        const int bufferSize = data.readInt32();
+        const uint32_t bufferSize = data.readInt32();
+        if (bufferSize > data.dataAvail()) {
+            return BAD_VALUE;
+        }
         DrmBuffer* inputData = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
 
-        DrmConvertedStatus*    drmConvertedStatus = convertData(uniqueId, convertId, inputData);
+        DrmConvertedStatus* drmConvertedStatus = convertData(uniqueId, convertId, inputData);
 
         if (NULL != drmConvertedStatus) {
             //Filling Drm Converted Ststus
@@ -1393,7 +1410,12 @@
         const int decryptUnitId = data.readInt32();
 
         //Filling Header info
-        const int bufferSize = data.readInt32();
+        const uint32_t bufferSize = data.readInt32();
+        if (bufferSize > data.dataAvail()) {
+            reply->writeInt32(BAD_VALUE);
+            clearDecryptHandle(&handle);
+            return DRM_NO_ERROR;
+        }
         DrmBuffer* headerInfo = NULL;
         headerInfo = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
 
@@ -1417,9 +1439,17 @@
         readDecryptHandleFromParcelData(&handle, data);
 
         const int decryptUnitId = data.readInt32();
-        const int decBufferSize = data.readInt32();
+        const uint32_t decBufferSize = data.readInt32();
+        const uint32_t encBufferSize = data.readInt32();
 
-        const int encBufferSize = data.readInt32();
+        if (encBufferSize > data.dataAvail() ||
+            decBufferSize > MAX_BINDER_TRANSACTION_SIZE) {
+            reply->writeInt32(BAD_VALUE);
+            reply->writeInt32(0);
+            clearDecryptHandle(&handle);
+            return DRM_NO_ERROR;
+        }
+
         DrmBuffer* encBuffer
             = new DrmBuffer((char *)data.readInplace(encBufferSize), encBufferSize);
 
@@ -1429,8 +1459,10 @@
 
         DrmBuffer* IV = NULL;
         if (0 != data.dataAvail()) {
-            const int ivBufferlength = data.readInt32();
-            IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength);
+            const uint32_t ivBufferlength = data.readInt32();
+            if (ivBufferlength <= data.dataAvail()) {
+                IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength);
+            }
         }
 
         const status_t status
@@ -1477,7 +1509,11 @@
         DecryptHandle handle;
         readDecryptHandleFromParcelData(&handle, data);
 
-        const int numBytes = data.readInt32();
+        const uint32_t numBytes = data.readInt32();
+        if (numBytes > MAX_BINDER_TRANSACTION_SIZE) {
+            reply->writeInt32(BAD_VALUE);
+            return DRM_NO_ERROR;
+        }
         char* buffer = new char[numBytes];
 
         const off64_t offset = data.readInt64();
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
index 9d15835..6a0b3c0 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <pthread.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
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/camera/camera2/CaptureRequest.h b/include/camera/camera2/CaptureRequest.h
index e56d61f..eeab217 100644
--- a/include/camera/camera2/CaptureRequest.h
+++ b/include/camera/camera2/CaptureRequest.h
@@ -30,6 +30,7 @@
 
     CameraMetadata          mMetadata;
     Vector<sp<Surface> >    mSurfaceList;
+    bool                    mIsReprocess;
 
     /**
      * Keep impl up-to-date with CaptureRequest.java in frameworks/base
diff --git a/include/camera/camera2/ICameraDeviceCallbacks.h b/include/camera/camera2/ICameraDeviceCallbacks.h
index 670480b..c57b39f 100644
--- a/include/camera/camera2/ICameraDeviceCallbacks.h
+++ b/include/camera/camera2/ICameraDeviceCallbacks.h
@@ -65,6 +65,9 @@
     // One way
     virtual void            onResultReceived(const CameraMetadata& metadata,
                                              const CaptureResultExtras& resultExtras) = 0;
+
+    // One way
+    virtual void            onPrepared(int streamId) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index e9f1f5a..619b161 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -103,6 +103,19 @@
 
     virtual status_t        createStream(const OutputConfiguration& outputConfiguration) = 0;
 
+    /**
+     * Create an input stream of width, height, and format (one of
+     * HAL_PIXEL_FORMAT_*)
+     *
+     * Return stream ID if it's a non-negative value. status_t if it's a
+     * negative value.
+     */
+    virtual status_t        createInputStream(int width, int height, int format) = 0;
+
+    // get the buffer producer of the input stream
+    virtual status_t        getInputBufferProducer(
+            sp<IGraphicBufferProducer> *producer) = 0;
+
     // Create a request object from a template.
     virtual status_t        createDefaultRequest(int templateId,
                                                  /*out*/
@@ -120,6 +133,11 @@
      */
     virtual status_t        flush(/*out*/
                                   int64_t* lastFrameNumber = NULL) = 0;
+
+    /**
+     * Preallocate buffers for a given output stream asynchronously.
+     */
+    virtual status_t        prepare(int streamId) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/AVSyncSettings.h b/include/media/AVSyncSettings.h
new file mode 100644
index 0000000..10e3bcc
--- /dev/null
+++ b/include/media/AVSyncSettings.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AV_SYNC_SETTINGS_H
+#define ANDROID_AV_SYNC_SETTINGS_H
+
+namespace android {
+
+enum AVSyncSource : unsigned {
+    // let the system decide the best sync source
+    AVSYNC_SOURCE_DEFAULT = 0,
+    // sync to the system clock
+    AVSYNC_SOURCE_SYSTEM_CLOCK = 1,
+    // sync to the audio track
+    AVSYNC_SOURCE_AUDIO = 2,
+    // sync to the display vsync
+    AVSYNC_SOURCE_VSYNC = 3,
+    AVSYNC_SOURCE_MAX,
+};
+
+enum AVSyncAudioAdjustMode : unsigned {
+    // let the system decide the best audio adjust mode
+    AVSYNC_AUDIO_ADJUST_MODE_DEFAULT = 0,
+    // adjust audio by time stretching
+    AVSYNC_AUDIO_ADJUST_MODE_STRETCH = 1,
+    // adjust audio by resampling
+    AVSYNC_AUDIO_ADJUST_MODE_RESAMPLE = 2,
+    AVSYNC_AUDIO_ADJUST_MODE_MAX,
+};
+
+// max tolerance when adjusting playback speed to desired playback speed
+#define AVSYNC_TOLERANCE_MAX 1.0f
+
+struct AVSyncSettings {
+    AVSyncSource mSource;
+    AVSyncAudioAdjustMode mAudioAdjustMode;
+    float mTolerance;
+    AVSyncSettings()
+        : mSource(AVSYNC_SOURCE_DEFAULT),
+          mAudioAdjustMode(AVSYNC_AUDIO_ADJUST_MODE_DEFAULT),
+          mTolerance(.044f) { }
+};
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_AV_SYNC_SETTINGS_H
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 583695d..61da4f2 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -201,8 +201,12 @@
      */
 
     /* Simple Constructor.
+     *
+     * Parameters:
+     *
+     * opPackageName:      The package name used for app op checks.
      */
-    AudioEffect();
+    AudioEffect(const String16& opPackageName);
 
 
     /* Constructor.
@@ -211,6 +215,7 @@
      *
      * type:  type of effect created: can be null if uuid is specified. This corresponds to
      *        the OpenSL ES interface implemented by this effect.
+     * opPackageName:  The package name used for app op checks.
      * uuid:  Uuid of effect created: can be null if type is specified. This uuid corresponds to
      *        a particular implementation of an effect type.
      * priority:    requested priority for effect control: the priority level corresponds to the
@@ -227,6 +232,7 @@
      */
 
     AudioEffect(const effect_uuid_t *type,
+                const String16& opPackageName,
                 const effect_uuid_t *uuid = NULL,
                   int32_t priority = 0,
                   effect_callback_t cbf = NULL,
@@ -239,6 +245,7 @@
      *      Same as above but with type and uuid specified by character strings
      */
     AudioEffect(const char *typeStr,
+                    const String16& opPackageName,
                     const char *uuidStr = NULL,
                     int32_t priority = 0,
                     effect_callback_t cbf = NULL,
@@ -406,7 +413,9 @@
      void*                   mUserData;          // client context for callback function
      effect_descriptor_t     mDescriptor;        // effect descriptor
      int32_t                 mId;                // system wide unique effect engine instance ID
-     Mutex                   mLock;               // Mutex for mEnabled access
+     Mutex                   mLock;              // Mutex for mEnabled access
+
+     String16                mOpPackageName;     // The package name used for app op checks.
 
      // IEffectClient
      virtual void controlStatusChanged(bool controlGranted);
diff --git a/include/media/AudioIoDescriptor.h b/include/media/AudioIoDescriptor.h
new file mode 100644
index 0000000..c94b738
--- /dev/null
+++ b/include/media/AudioIoDescriptor.h
@@ -0,0 +1,71 @@
+/*
+ * 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_AUDIO_IO_DESCRIPTOR_H
+#define ANDROID_AUDIO_IO_DESCRIPTOR_H
+
+namespace android {
+
+enum audio_io_config_event {
+    AUDIO_OUTPUT_OPENED,
+    AUDIO_OUTPUT_CLOSED,
+    AUDIO_OUTPUT_CONFIG_CHANGED,
+    AUDIO_INPUT_OPENED,
+    AUDIO_INPUT_CLOSED,
+    AUDIO_INPUT_CONFIG_CHANGED,
+};
+
+// audio input/output descriptor used to cache output configurations in client process to avoid
+// frequent calls through IAudioFlinger
+class AudioIoDescriptor : public RefBase {
+public:
+    AudioIoDescriptor() :
+        mIoHandle(AUDIO_IO_HANDLE_NONE),
+        mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE),
+        mFrameCount(0), mLatency(0)
+    {
+        memset(&mPatch, 0, sizeof(struct audio_patch));
+    }
+
+    virtual ~AudioIoDescriptor() {}
+
+    audio_port_handle_t getDeviceId() {
+        if (mPatch.num_sources != 0 && mPatch.num_sinks != 0) {
+            if (mPatch.sources[0].type == AUDIO_PORT_TYPE_MIX) {
+                // this is an output mix
+                // FIXME: the API only returns the first device in case of multiple device selection
+                return mPatch.sinks[0].id;
+            } else {
+                // this is an input mix
+                return mPatch.sources[0].id;
+            }
+        }
+        return AUDIO_PORT_HANDLE_NONE;
+    }
+
+    audio_io_handle_t mIoHandle;
+    struct audio_patch mPatch;
+    uint32_t mSamplingRate;
+    audio_format_t mFormat;
+    audio_channel_mask_t mChannelMask;
+    size_t mFrameCount;
+    uint32_t mLatency;
+};
+
+
+};  // namespace android
+
+#endif  /*ANDROID_AUDIO_IO_DESCRIPTOR_H*/
diff --git a/include/media/AudioPolicy.h b/include/media/AudioPolicy.h
index a755e1e..feed402 100644
--- a/include/media/AudioPolicy.h
+++ b/include/media/AudioPolicy.h
@@ -38,8 +38,17 @@
 #define MIX_TYPE_PLAYERS 0
 #define MIX_TYPE_RECORDERS 1
 
-#define ROUTE_FLAG_RENDER 0x1
-#define ROUTE_FLAG_LOOP_BACK (0x1 << 1)
+// definition of the different events that can be reported on a dynamic policy from
+//   AudioSystem's implementation of the AudioPolicyClient interface
+// keep in sync with AudioSystem.java
+#define DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE 0
+
+#define MIX_STATE_DISABLED -1
+#define MIX_STATE_IDLE 0
+#define MIX_STATE_MIXING 1
+
+#define MIX_ROUTE_FLAG_RENDER 0x1
+#define MIX_ROUTE_FLAG_LOOP_BACK (0x1 << 1)
 
 #define MAX_MIXES_PER_POLICY 10
 #define MAX_CRITERIA_PER_MIX 20
@@ -61,11 +70,15 @@
 
 class AudioMix {
 public:
+    // flag on an AudioMix indicating the activity on this mix (IDLE, MIXING)
+    //   must be reported through the AudioPolicyClient interface
+    static const uint32_t kCbFlagNotifyActivity = 0x1;
+
     AudioMix() {}
     AudioMix(Vector<AttributeMatchCriterion> criteria, uint32_t mixType, audio_config_t format,
-             uint32_t routeFlags, String8 registrationId) :
+             uint32_t routeFlags, String8 registrationId, uint32_t flags) :
         mCriteria(criteria), mMixType(mixType), mFormat(format),
-        mRouteFlags(routeFlags), mRegistrationId(registrationId) {}
+        mRouteFlags(routeFlags), mRegistrationId(registrationId), mCbFlags(flags){}
 
     status_t readFromParcel(Parcel *parcel);
     status_t writeToParcel(Parcel *parcel) const;
@@ -75,6 +88,7 @@
     audio_config_t  mFormat;
     uint32_t        mRouteFlags;
     String8         mRegistrationId;
+    uint32_t        mCbFlags; // flags indicating which callbacks to use, see kCbFlag*
 };
 
 }; // namespace android
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 7be2c3e..c4c7b0e 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -129,8 +129,12 @@
 
     /* Constructs an uninitialized AudioRecord. No connection with
      * AudioFlinger takes place.  Use set() after this.
+     *
+     * Parameters:
+     *
+     * opPackageName:      The package name used for app ops.
      */
-                        AudioRecord();
+                        AudioRecord(const String16& opPackageName);
 
     /* Creates an AudioRecord object and registers it with AudioFlinger.
      * Once created, the track needs to be started before it can be used.
@@ -143,6 +147,7 @@
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
      * channelMask:        Channel mask, such that audio_is_input_channel(channelMask) is true.
+     * opPackageName:      The package name used for app ops.
      * frameCount:         Minimum size of track PCM buffer in frames. This defines the
      *                     application's contribution to the
      *                     latency of the track.  The actual size selected by the AudioRecord could
@@ -165,6 +170,7 @@
                                     uint32_t sampleRate,
                                     audio_format_t format,
                                     audio_channel_mask_t channelMask,
+                                    const String16& opPackageName,
                                     size_t frameCount = 0,
                                     callback_t cbf = NULL,
                                     void* user = NULL,
@@ -172,6 +178,8 @@
                                     int sessionId = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+                                    int uid = -1,
+                                    pid_t pid = -1,
                                     const audio_attributes_t* pAttributes = NULL);
 
     /* Terminates the AudioRecord and unregisters it from AudioFlinger.
@@ -208,6 +216,8 @@
                             int sessionId = AUDIO_SESSION_ALLOCATE,
                             transfer_type transferType = TRANSFER_DEFAULT,
                             audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+                            int uid = -1,
+                            pid_t pid = -1,
                             const audio_attributes_t* pAttributes = NULL);
 
     /* Result of constructing the AudioRecord. This must be checked for successful initialization
@@ -335,6 +345,13 @@
      * After draining these frames of data, the caller should release them with releaseBuffer().
      * If the track buffer is not empty, obtainBuffer() returns as many contiguous
      * full frames as are available immediately.
+     *
+     * If nonContig is non-NULL, it is an output parameter that will be set to the number of
+     * additional non-contiguous frames that are predicted to be available immediately,
+     * if the client were to release the first frames and then call obtainBuffer() again.
+     * This value is only a prediction, and needs to be confirmed.
+     * It will be set to zero for an error return.
+     *
      * If the track buffer is empty and track is stopped, obtainBuffer() returns WOULD_BLOCK
      * regardless of the value of waitCount.
      * If the track buffer is empty and track is not stopped, obtainBuffer() blocks with a
@@ -364,11 +381,58 @@
      *  raw         pointer to the buffer
      */
 
-            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
+            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
+                                size_t *nonContig = NULL);
+
+            // Explicit Routing
+    /**
+     * TODO Document this method.
+     */
+            status_t setInputDevice(audio_port_handle_t deviceId);
+
+    /**
+     * TODO Document this method.
+     */
+            audio_port_handle_t getInputDevice();
+
+     /* Returns the ID of the audio device actually used by the input to which this AudioRecord
+      * is attached.
+      * A value of AUDIO_PORT_HANDLE_NONE indicates the AudioRecord is not attached to any input.
+      *
+      * Parameters:
+      *  none.
+      */
+     audio_port_handle_t getRoutedDeviceId();
+
+    /* Add an AudioDeviceCallback. The caller will be notified when the audio device
+     * to which this AudioRecord is routed is updated.
+     * Replaces any previously installed callback.
+     * Parameters:
+     *  callback:  The callback interface
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the same callback is already installed.
+     *         NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+     *         BAD_VALUE if the callback is NULL
+     */
+            status_t addAudioDeviceCallback(
+                    const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+    /* remove an AudioDeviceCallback.
+     * Parameters:
+     *  callback:  The callback interface
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the callback is not installed
+     *         BAD_VALUE if the callback is NULL
+     */
+            status_t removeAudioDeviceCallback(
+                    const sp<AudioSystem::AudioDeviceCallback>& callback);
 
 private:
     /* If nonContig is non-NULL, it is an output parameter that will be set to the number of
-     * additional non-contiguous frames that are available immediately.
+     * additional non-contiguous frames that are predicted to be available immediately,
+     * if the client were to release the first frames and then call obtainBuffer() again.
+     * This value is only a prediction, and needs to be confirmed.
+     * It will be set to zero for an error return.
      * FIXME We could pass an array of Buffers instead of only one Buffer to obtainBuffer(),
      * in case the requested amount of frames is in two or more non-contiguous regions.
      * FIXME requested and elapsed are both relative times.  Consider changing to absolute time.
@@ -462,7 +526,7 @@
 
             // caller must hold lock on mLock for all _l methods
 
-            status_t openRecord_l(size_t epoch);
+            status_t openRecord_l(size_t epoch, const String16& opPackageName);
 
             // FIXME enum is faster than strcmp() for parameter 'from'
             status_t restoreRecord_l(const char *from);
@@ -499,6 +563,8 @@
 
     status_t                mStatus;
 
+    String16                mOpPackageName;         // The package name used for app ops.
+
     size_t                  mFrameCount;            // corresponds to current IAudioRecord, value is
                                                     // reported back by AudioFlinger to the client
     size_t                  mReqFrameCount;         // frame count to request the first or next time
@@ -548,7 +614,14 @@
 
     sp<DeathNotifier>       mDeathNotifier;
     uint32_t                mSequence;              // incremented for each new IAudioRecord attempt
+    int                     mClientUid;
+    pid_t                   mClientPid;
     audio_attributes_t      mAttributes;
+
+    // For Device Selection API
+    //  a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
+    audio_port_handle_t    mSelectedDeviceId;
+    sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
 };
 
 }; // namespace android
diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h
index b705efa..6cf2ca9 100644
--- a/include/media/AudioResamplerPublic.h
+++ b/include/media/AudioResamplerPublic.h
@@ -17,6 +17,11 @@
 #ifndef ANDROID_AUDIO_RESAMPLER_PUBLIC_H
 #define ANDROID_AUDIO_RESAMPLER_PUBLIC_H
 
+#include <stdint.h>
+#include <math.h>
+
+namespace android {
+
 // 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 +31,85 @@
 // 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
+
+// AUDIO_TIMESTRETCH_SPEED_MIN and AUDIO_TIMESTRETCH_SPEED_MAX define the min and max time stretch
+// speeds supported by the system. These are enforced by the system and values outside this range
+// will result in a runtime error.
+// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than
+// the ones specified here
+// AUDIO_TIMESTRETCH_SPEED_MIN_DELTA is the minimum absolute speed difference that might trigger a
+// parameter update
+#define AUDIO_TIMESTRETCH_SPEED_MIN    0.01f
+#define AUDIO_TIMESTRETCH_SPEED_MAX    20.0f
+#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f
+#define AUDIO_TIMESTRETCH_SPEED_MIN_DELTA 0.0001f
+
+// AUDIO_TIMESTRETCH_PITCH_MIN and AUDIO_TIMESTRETCH_PITCH_MAX define the min and max time stretch
+// pitch shifting supported by the system. These are not enforced by the system and values
+// outside this range might result in a pitch different than the one requested.
+// Depending on the AudioPlaybackRate::mStretchMode, the effective limits might be narrower than
+// the ones specified here.
+// AUDIO_TIMESTRETCH_PITCH_MIN_DELTA is the minimum absolute pitch difference that might trigger a
+// parameter update
+#define AUDIO_TIMESTRETCH_PITCH_MIN    0.25f
+#define AUDIO_TIMESTRETCH_PITCH_MAX    4.0f
+#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f
+#define AUDIO_TIMESTRETCH_PITCH_MIN_DELTA 0.0001f
+
+
+//Determines the current algorithm used for stretching
+enum AudioTimestretchStretchMode : int32_t {
+    AUDIO_TIMESTRETCH_STRETCH_DEFAULT            = 0,
+    AUDIO_TIMESTRETCH_STRETCH_SPEECH             = 1,
+    //TODO: add more stretch modes/algorithms
+};
+
+//Limits for AUDIO_TIMESTRETCH_STRETCH_SPEECH mode
+#define TIMESTRETCH_SONIC_SPEED_MIN 0.1f
+#define TIMESTRETCH_SONIC_SPEED_MAX 6.0f
+
+//Determines behavior of Timestretch if current algorithm can't perform
+//with current parameters.
+// FALLBACK_CUT_REPEAT: (internal only) for speed <1.0 will truncate frames
+//    for speed > 1.0 will repeat frames
+// FALLBACK_MUTE: will set all processed frames to zero
+// FALLBACK_FAIL:  will stop program execution and log a fatal error
+enum AudioTimestretchFallbackMode : int32_t {
+    AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT     = -1,
+    AUDIO_TIMESTRETCH_FALLBACK_DEFAULT        = 0,
+    AUDIO_TIMESTRETCH_FALLBACK_MUTE           = 1,
+    AUDIO_TIMESTRETCH_FALLBACK_FAIL           = 2,
+};
+
+struct AudioPlaybackRate {
+    float mSpeed;
+    float mPitch;
+    enum AudioTimestretchStretchMode  mStretchMode;
+    enum AudioTimestretchFallbackMode mFallbackMode;
+};
+
+static const AudioPlaybackRate AUDIO_PLAYBACK_RATE_DEFAULT = {
+        AUDIO_TIMESTRETCH_SPEED_NORMAL,
+        AUDIO_TIMESTRETCH_PITCH_NORMAL,
+        AUDIO_TIMESTRETCH_STRETCH_DEFAULT,
+        AUDIO_TIMESTRETCH_FALLBACK_DEFAULT
+};
+
+static inline bool isAudioPlaybackRateEqual(const AudioPlaybackRate &pr1,
+        const AudioPlaybackRate &pr2) {
+    return fabs(pr1.mSpeed - pr2.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
+           fabs(pr1.mPitch - pr2.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA &&
+           pr2.mStretchMode == pr2.mStretchMode &&
+           pr2.mFallbackMode == pr2.mFallbackMode;
+}
+
+// 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 +123,38 @@
             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
+}
+
+// Identifies sample rates that we associate with music
+// and thus eligible for better resampling and fast capture.
+// This is somewhat less than 44100 to allow for pitch correction
+// involving resampling as well as asynchronous resampling.
+#define AUDIO_PROCESSING_MUSIC_RATE 40000
+
+static inline bool isMusicRate(uint32_t sampleRate) {
+    return sampleRate >= AUDIO_PROCESSING_MUSIC_RATE;
+}
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
 #endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f5db1bb..3241e9c 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
 
 #include <hardware/audio_effect.h>
 #include <media/AudioPolicy.h>
+#include <media/AudioIoDescriptor.h>
 #include <media/IAudioFlingerClient.h>
 #include <media/IAudioPolicyServiceClient.h>
 #include <system/audio.h>
@@ -29,6 +30,7 @@
 namespace android {
 
 typedef void (*audio_error_callback)(status_t err);
+typedef void (*dynamic_policy_callback)(int event, String8 regId, int val);
 
 class IAudioFlinger;
 class IAudioPolicyService;
@@ -89,6 +91,7 @@
     static String8  getParameters(const String8& keys);
 
     static void setErrorCallback(audio_error_callback cb);
+    static void setDynPolicyCallback(dynamic_policy_callback cb);
 
     // helper function to obtain AudioFlinger service handle
     static const sp<IAudioFlinger> get_audio_flinger();
@@ -155,33 +158,6 @@
     // or no HW sync source is used.
     static audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId);
 
-    // types of io configuration change events received with ioConfigChanged()
-    enum io_config_event {
-        OUTPUT_OPENED,
-        OUTPUT_CLOSED,
-        OUTPUT_CONFIG_CHANGED,
-        INPUT_OPENED,
-        INPUT_CLOSED,
-        INPUT_CONFIG_CHANGED,
-        STREAM_CONFIG_CHANGED,
-        NUM_CONFIG_EVENTS
-    };
-
-    // audio output descriptor used to cache output configurations in client process to avoid
-    // frequent calls through IAudioFlinger
-    class OutputDescriptor {
-    public:
-        OutputDescriptor()
-        : samplingRate(0), format(AUDIO_FORMAT_DEFAULT), channelMask(0), frameCount(0), latency(0)
-            {}
-
-        uint32_t samplingRate;
-        audio_format_t format;
-        audio_channel_mask_t channelMask;
-        size_t frameCount;
-        uint32_t latency;
-    };
-
     // Events used to synchronize actions between audio sessions.
     // For instance SYNC_EVENT_PRESENTATION_COMPLETE can be used to delay recording start until
     // playback is complete on another audio session.
@@ -221,14 +197,16 @@
                                         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,
+                                     uid_t uid,
+                                     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);
@@ -244,10 +222,12 @@
     static status_t getInputForAttr(const audio_attributes_t *attr,
                                     audio_io_handle_t *input,
                                     audio_session_t session,
+                                    uid_t uid,
                                     uint32_t samplingRate,
                                     audio_format_t format,
                                     audio_channel_mask_t channelMask,
-                                    audio_input_flags_t flags);
+                                    audio_input_flags_t flags,
+                                    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
 
     static status_t startInput(audio_io_handle_t input,
                                audio_session_t session);
@@ -331,6 +311,12 @@
 
     static status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration);
 
+    static status_t startAudioSource(const struct audio_port_config *source,
+                                      const audio_attributes_t *attributes,
+                                      audio_io_handle_t *handle);
+    static status_t stopAudioSource(audio_io_handle_t handle);
+
+
     // ----------------------------------------------------------------------------
 
     class AudioPortCallback : public RefBase
@@ -346,17 +332,42 @@
 
     };
 
-    static status_t addAudioPortCallback(const sp<AudioPortCallback>& callBack);
-    static status_t removeAudioPortCallback(const sp<AudioPortCallback>& callBack);
+    static status_t addAudioPortCallback(const sp<AudioPortCallback>& callback);
+    static status_t removeAudioPortCallback(const sp<AudioPortCallback>& callback);
+
+    class AudioDeviceCallback : public RefBase
+    {
+    public:
+
+                AudioDeviceCallback() {}
+        virtual ~AudioDeviceCallback() {}
+
+        virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
+                                         audio_port_handle_t deviceId) = 0;
+    };
+
+    static status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+                                           audio_io_handle_t audioIo);
+    static status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+                                              audio_io_handle_t audioIo);
+
+    static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
 
 private:
 
     class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
     {
     public:
-        AudioFlingerClient() {
+        AudioFlingerClient() :
+            mInBuffSize(0), mInSamplingRate(0),
+            mInFormat(AUDIO_FORMAT_DEFAULT), mInChannelMask(AUDIO_CHANNEL_NONE) {
         }
 
+        void clearIoCache();
+        status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
+                                    audio_channel_mask_t channelMask, size_t* buffSize);
+        sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);
+
         // DeathRecipient
         virtual void binderDied(const wp<IBinder>& who);
 
@@ -364,7 +375,27 @@
 
         // indicate a change in the configuration of an output or input: keeps the cached
         // values for output/input parameters up-to-date in client process
-        virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
+        virtual void ioConfigChanged(audio_io_config_event event,
+                                     const sp<AudioIoDescriptor>& ioDesc);
+
+
+        status_t addAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+                                               audio_io_handle_t audioIo);
+        status_t removeAudioDeviceCallback(const sp<AudioDeviceCallback>& callback,
+                                           audio_io_handle_t audioIo);
+
+        audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
+
+    private:
+        Mutex                               mLock;
+        DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> >   mIoDescriptors;
+        DefaultKeyedVector<audio_io_handle_t, Vector < sp<AudioDeviceCallback> > >
+                                                                        mAudioDeviceCallbacks;
+        // cached values for recording getInputBufferSize() queries
+        size_t                              mInBuffSize;    // zero indicates cache is invalid
+        uint32_t                            mInSamplingRate;
+        audio_format_t                      mInFormat;
+        audio_channel_mask_t                mInChannelMask;
     };
 
     class AudioPolicyServiceClient: public IBinder::DeathRecipient,
@@ -374,8 +405,8 @@
         AudioPolicyServiceClient() {
         }
 
-        status_t addAudioPortCallback(const sp<AudioPortCallback>& callBack);
-        status_t removeAudioPortCallback(const sp<AudioPortCallback>& callBack);
+        status_t addAudioPortCallback(const sp<AudioPortCallback>& callback);
+        status_t removeAudioPortCallback(const sp<AudioPortCallback>& callback);
 
         // DeathRecipient
         virtual void binderDied(const wp<IBinder>& who);
@@ -383,23 +414,26 @@
         // IAudioPolicyServiceClient
         virtual void onAudioPortListUpdate();
         virtual void onAudioPatchListUpdate();
+        virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
 
     private:
         Mutex                               mLock;
         Vector <sp <AudioPortCallback> >    mAudioPortCallbacks;
     };
 
+    static const sp<AudioFlingerClient> getAudioFlingerClient();
+    static sp<AudioIoDescriptor> getIoDescriptor(audio_io_handle_t ioHandle);
+
     static sp<AudioFlingerClient> gAudioFlingerClient;
     static sp<AudioPolicyServiceClient> gAudioPolicyServiceClient;
     friend class AudioFlingerClient;
     friend class AudioPolicyServiceClient;
 
     static Mutex gLock;      // protects gAudioFlinger and gAudioErrorCallback,
-    static Mutex gLockCache; // protects gOutputs, gPrevInSamplingRate, gPrevInFormat,
-                             // gPrevInChannelMask and gInBuffSize
     static Mutex gLockAPS;   // protects gAudioPolicyService and gAudioPolicyServiceClient
     static sp<IAudioFlinger> gAudioFlinger;
     static audio_error_callback gAudioErrorCallback;
+    static dynamic_policy_callback gDynPolicyCallback;
 
     static size_t gInBuffSize;
     // previous parameters for recording buffer size queries
@@ -408,10 +442,6 @@
     static audio_channel_mask_t gPrevInChannelMask;
 
     static sp<IAudioPolicyService> gAudioPolicyService;
-
-    // list of output descriptors containing cached parameters
-    // (sampling rate, framecount, channel count...)
-    static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
 };
 
 };  // namespace android
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index d9b7057..458f4b4 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -21,6 +21,7 @@
 #include <media/AudioSystem.h>
 #include <media/AudioTimestamp.h>
 #include <media/IAudioTrack.h>
+#include <media/AudioResamplerPublic.h>
 #include <utils/threads.h>
 
 namespace android {
@@ -359,6 +360,26 @@
     /* Return current source sample rate in Hz */
             uint32_t    getSampleRate() const;
 
+    /* Return the original source sample rate in Hz. This corresponds to the sample rate
+     * if playback rate had normal speed and pitch.
+     */
+            uint32_t    getOriginalSampleRate() 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(const AudioPlaybackRate &playbackRate);
+
+    /* Return current playback rate */
+            const AudioPlaybackRate& getPlaybackRate() const;
+
     /* Enables looping and sets the start and end points of looping.
      * Only supported for static buffer mode.
      *
@@ -477,6 +498,35 @@
             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 selected for this AudioTrack.
+     * A value of AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+     *
+     * Parameters:
+     *  none.
+     */
+     audio_port_handle_t getOutputDevice();
+
+     /* Returns the ID of the audio device actually used by the output to which this AudioTrack is
+      * attached.
+      * A value of AUDIO_PORT_HANDLE_NONE indicates the audio track is not attached to any output.
+      *
+      * Parameters:
+      *  none.
+      */
+     audio_port_handle_t getRoutedDeviceId();
+
     /* Returns the unique session ID associated with this track.
      *
      * Parameters:
@@ -623,6 +673,28 @@
      */
             status_t    getTimestamp(AudioTimestamp& timestamp);
 
+    /* Add an AudioDeviceCallback. The caller will be notified when the audio device to which this
+     * AudioTrack is routed is updated.
+     * Replaces any previously installed callback.
+     * Parameters:
+     *  callback:  The callback interface
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the same callback is already installed.
+     *         NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+     *         BAD_VALUE if the callback is NULL
+     */
+            status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+    /* remove an AudioDeviceCallback.
+     * Parameters:
+     *  callback:  The callback interface
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the callback is not installed
+     *         BAD_VALUE if the callback is NULL
+     */
+            status_t removeAudioDeviceCallback(
+                    const sp<AudioSystem::AudioDeviceCallback>& callback);
+
 protected:
     /* copying audio tracks is not allowed */
                         AudioTrack(const AudioTrack& other);
@@ -699,6 +771,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 +785,8 @@
     float                   mVolume[2];
     float                   mSendLevel;
     mutable uint32_t        mSampleRate;            // mutable because getSampleRate() can update it
+    uint32_t                mOriginalSampleRate;
+    AudioPlaybackRate       mPlaybackRate;
     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
@@ -792,6 +869,10 @@
     int64_t                 mStartUs;               // the start time after flush or stop.
                                                     // only used for offloaded and direct tracks.
 
+    bool                    mPreviousTimestampValid;// true if mPreviousTimestamp is valid
+    bool                    mRetrogradeMotionReported; // reduce log spam
+    AudioTimestamp          mPreviousTimestamp;     // used to detect retrograde motion
+
     audio_output_flags_t    mFlags;
         // const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
         // mLock must be held to read or write those bits reliably.
@@ -817,6 +898,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.
+    audio_port_handle_t     mSelectedDeviceId;
+
 private:
     class DeathNotifier : public IBinder::DeathRecipient {
     public:
@@ -831,6 +916,8 @@
     uint32_t                mSequence;              // incremented for each new IAudioTrack attempt
     int                     mClientUid;
     pid_t                   mClientPid;
+
+    sp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
 };
 
 class TimedAudioTrack : public AudioTrack
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index f927a80..3f7fd09 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -85,9 +85,11 @@
                                 uint32_t sampleRate,
                                 audio_format_t format,
                                 audio_channel_mask_t channelMask,
+                                const String16& callingPackage,
                                 size_t *pFrameCount,
                                 track_flags_t *flags,
                                 pid_t tid,  // -1 means unused, otherwise must be valid non-0
+                                int clientUid,
                                 int *sessionId,
                                 size_t *notificationFrames,
                                 sp<IMemory>& cblk,
@@ -198,6 +200,7 @@
                                     // AudioFlinger doesn't take over handle reference from client
                                     audio_io_handle_t output,
                                     int sessionId,
+                                    const String16& callingPackage,
                                     status_t *status,
                                     int *id,
                                     int *enabled) = 0;
diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h
index 75a9971..0080bc9 100644
--- a/include/media/IAudioFlingerClient.h
+++ b/include/media/IAudioFlingerClient.h
@@ -22,6 +22,7 @@
 #include <binder/IInterface.h>
 #include <utils/KeyedVector.h>
 #include <system/audio.h>
+#include <media/AudioIoDescriptor.h>
 
 namespace android {
 
@@ -33,7 +34,8 @@
     DECLARE_META_INTERFACE(AudioFlingerClient);
 
     // Notifies a change of audio input/output configuration.
-    virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) = 0;
+    virtual void ioConfigChanged(audio_io_config_event event,
+                                 const sp<AudioIoDescriptor>& ioDesc) = 0;
 
 };
 
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index fecc6f1..ee462a0 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -62,10 +62,12 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
+                                        uid_t uid,
                                         uint32_t samplingRate = 0,
                                         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,
@@ -77,12 +79,14 @@
                                audio_stream_type_t stream,
                                audio_session_t session) = 0;
     virtual status_t  getInputForAttr(const audio_attributes_t *attr,
-                                      audio_io_handle_t *input,
-                                      audio_session_t session,
-                                      uint32_t samplingRate,
-                                      audio_format_t format,
-                                      audio_channel_mask_t channelMask,
-                                      audio_input_flags_t flags) = 0;
+                              audio_io_handle_t *input,
+                              audio_session_t session,
+                              uid_t uid,
+                              uint32_t samplingRate,
+                              audio_format_t format,
+                              audio_channel_mask_t channelMask,
+                              audio_input_flags_t flags,
+                              audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE) = 0;
     virtual status_t startInput(audio_io_handle_t input,
                                 audio_session_t session) = 0;
     virtual status_t stopInput(audio_io_handle_t input,
@@ -154,6 +158,11 @@
     virtual audio_mode_t getPhoneState() = 0;
 
     virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration) = 0;
+
+    virtual status_t startAudioSource(const struct audio_port_config *source,
+                                      const audio_attributes_t *attributes,
+                                      audio_io_handle_t *handle) = 0;
+    virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
 };
 
 
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
index 59df046..a7f2cc3 100644
--- a/include/media/IAudioPolicyServiceClient.h
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -35,6 +35,8 @@
     virtual void onAudioPortListUpdate() = 0;
     // Notifies a change of audio patch configuration.
     virtual void onAudioPatchListUpdate() = 0;
+    // Notifies a change in the mixing state of a specific mix in a dynamic audio policy
+    virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0;
 };
 
 
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
index 07742ca..ea316de 100644
--- a/include/media/ICrypto.h
+++ b/include/media/ICrypto.h
@@ -25,6 +25,7 @@
 namespace android {
 
 struct AString;
+class 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/IDataSource.h b/include/media/IDataSource.h
new file mode 100644
index 0000000..07e46f7
--- /dev/null
+++ b/include/media/IDataSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IDATASOURCE_H
+#define ANDROID_IDATASOURCE_H
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class IMemory;
+
+// A binder interface for implementing a stagefright DataSource remotely.
+class IDataSource : public IInterface {
+public:
+    DECLARE_META_INTERFACE(DataSource);
+
+    // Get the memory that readAt writes into.
+    virtual sp<IMemory> getIMemory() = 0;
+    // Read up to |size| bytes into the memory returned by getIMemory(). Returns
+    // the number of bytes read, or -1 on error. |size| must not be larger than
+    // the buffer.
+    virtual ssize_t readAt(off64_t offset, size_t size) = 0;
+    // Get the size, or -1 if the size is unknown.
+    virtual status_t getSize(off64_t* size) = 0;
+    // This should be called before deleting |this|. The other methods may
+    // return errors if they're called after calling close().
+    virtual void close() = 0;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(IDataSource);
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDataSource : public BnInterface<IDataSource> {
+public:
+    virtual status_t onTransact(uint32_t code,
+                                const Parcel& data,
+                                Parcel* reply,
+                                uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IDATASOURCE_H
diff --git a/include/media/IMediaCodecList.h b/include/media/IMediaCodecList.h
index e93ea8b..12b52d7 100644
--- a/include/media/IMediaCodecList.h
+++ b/include/media/IMediaCodecList.h
@@ -21,6 +21,8 @@
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 
+#include <media/stagefright/foundation/AMessage.h>
+
 namespace android {
 
 struct MediaCodecInfo;
@@ -33,6 +35,8 @@
     virtual size_t countCodecs() const = 0;
     virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const = 0;
 
+    virtual const sp<AMessage> getGlobalSettings() const = 0;
+
     virtual ssize_t findCodecByType(
             const char *type, bool encoder, size_t startIndex = 0) const = 0;
 
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 2529800..c90f254 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -26,6 +26,7 @@
 
 namespace android {
 
+class IDataSource;
 struct IMediaHTTPService;
 
 class IMediaMetadataRetriever: public IInterface
@@ -40,6 +41,7 @@
             const KeyedVector<String8, String8> *headers = NULL) = 0;
 
     virtual status_t        setDataSource(int fd, int64_t offset, int64_t length) = 0;
+    virtual status_t        setDataSource(const sp<IDataSource>& dataSource) = 0;
     virtual sp<IMemory>     getFrameAtTime(int64_t timeUs, int option) = 0;
     virtual sp<IMemory>     extractAlbumArt() = 0;
     virtual const char*     extractMetadata(int keyCode) = 0;
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 4153c25..0fd8933 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -31,9 +31,12 @@
 
 class Parcel;
 class Surface;
-class IStreamSource;
+class IDataSource;
+struct IStreamSource;
 class IGraphicBufferProducer;
 struct IMediaHTTPService;
+struct AudioPlaybackRate;
+struct AVSyncSettings;
 
 class IMediaPlayer: public IInterface
 {
@@ -49,6 +52,7 @@
 
     virtual status_t        setDataSource(int fd, int64_t offset, int64_t length) = 0;
     virtual status_t        setDataSource(const sp<IStreamSource>& source) = 0;
+    virtual status_t        setDataSource(const sp<IDataSource>& source) = 0;
     virtual status_t        setVideoSurfaceTexture(
                                     const sp<IGraphicBufferProducer>& bufferProducer) = 0;
     virtual status_t        prepareAsync() = 0;
@@ -56,7 +60,11 @@
     virtual status_t        stop() = 0;
     virtual status_t        pause() = 0;
     virtual status_t        isPlaying(bool* state) = 0;
-    virtual status_t        setPlaybackRate(float rate) = 0;
+    virtual status_t        setPlaybackSettings(const AudioPlaybackRate& rate) = 0;
+    virtual status_t        getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) = 0;
+    virtual status_t        setSyncSettings(const AVSyncSettings& sync, float videoFpsHint) = 0;
+    virtual status_t        getSyncSettings(AVSyncSettings* sync /* nonnull */,
+                                            float* videoFps /* nonnull */) = 0;
     virtual status_t        seekTo(int msec) = 0;
     virtual status_t        getCurrentPosition(int* msec) = 0;
     virtual status_t        getDuration(int* msec) = 0;
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 49a3d61..a316ce2 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -47,7 +47,7 @@
 public:
     DECLARE_META_INTERFACE(MediaPlayerService);
 
-    virtual sp<IMediaRecorder> createMediaRecorder() = 0;
+    virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName) = 0;
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever() = 0;
     virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId = 0)
             = 0;
@@ -65,8 +65,8 @@
     // display client when display connection, disconnection or errors occur.
     // The assumption is that at most one remote display will be connected to the
     // provided interface at a time.
-    virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
-            const String8& iface) = 0;
+    virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
+            const sp<IRemoteDisplayClient>& client, const String8& iface) = 0;
 
     // codecs and audio devices usage tracking for the battery app
     enum BatteryDataBits {
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 509c06b..77ed5d3 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -26,6 +26,7 @@
 class ICamera;
 class ICameraRecordingProxy;
 class IMediaRecorderClient;
+class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
 
 class IMediaRecorder: public IInterface
@@ -55,6 +56,7 @@
     virtual status_t init() = 0;
     virtual status_t close() = 0;
     virtual status_t release() = 0;
+    virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface) = 0;
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() = 0;
 };
 
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 6def65b..d33d142 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -20,6 +20,7 @@
 
 #include <binder/IInterface.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferConsumer.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/List.h>
 #include <utils/String8.h>
@@ -113,6 +114,14 @@
             node_id node, OMX_U32 port_index,
             sp<IGraphicBufferProducer> *bufferProducer) = 0;
 
+    virtual status_t createPersistentInputSurface(
+            sp<IGraphicBufferProducer> *bufferProducer,
+            sp<IGraphicBufferConsumer> *bufferConsumer) = 0;
+
+    virtual status_t setInputSurface(
+            node_id node, OMX_U32 port_index,
+            const sp<IGraphicBufferConsumer> &bufferConsumer) = 0;
+
     virtual status_t signalEndOfInputStream(node_id node) = 0;
 
     // This API clearly only makes sense if the caller lives in the
diff --git a/include/media/IResourceManagerClient.h b/include/media/IResourceManagerClient.h
index 3587aea..aa0cd88 100644
--- a/include/media/IResourceManagerClient.h
+++ b/include/media/IResourceManagerClient.h
@@ -18,6 +18,7 @@
 #define ANDROID_IRESOURCEMANAGERCLIENT_H
 
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 
@@ -29,6 +30,7 @@
     DECLARE_META_INTERFACE(ResourceManagerClient);
 
     virtual bool reclaimResource() = 0;
+    virtual String8 getName() = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 149bd49..4a6aafd 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct AMessage;
-struct IMemory;
+class IMemory;
 struct IStreamListener;
 
 struct IStreamSource : public IInterface {
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index cd56adb..4067b47 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -32,9 +32,11 @@
 namespace android {
 
 struct AMessage;
-struct Parcel;
+class Parcel;
 struct CodecCapabilities;
 
+typedef KeyedVector<AString, AString> CodecSettings;
+
 struct MediaCodecInfo : public RefBase {
     struct ProfileLevel {
         uint32_t mProfile;
@@ -104,6 +106,7 @@
     MediaCodecInfo(AString name, bool encoder, const char *mime);
     void addQuirk(const char *name);
     status_t addMime(const char *mime);
+    status_t updateMime(const char *mime);
     status_t initializeCapabilities(const CodecCapabilities &caps);
     void addDetail(const AString &key, const AString &value);
     void addFeature(const AString &key, int32_t value);
@@ -114,6 +117,7 @@
     DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
 
     friend class MediaCodecList;
+    friend class MediaCodecListOverridesTest;
 };
 
 }  // namespace android
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index 38dbb20..bce6ee3 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -25,6 +25,7 @@
 
 namespace android {
 
+class DataSource;
 struct IMediaHTTPService;
 
 // Abstract base class
@@ -40,6 +41,7 @@
             const KeyedVector<String8, String8> *headers = NULL) = 0;
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
+    virtual status_t setDataSource(const sp<DataSource>& source) = 0;
     virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0;
     virtual MediaAlbumArt* extractAlbumArt() = 0;
     virtual const char* extractMetadata(int keyCode) = 0;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index d6fe390..fa917f9 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -26,8 +26,10 @@
 #include <utils/RefBase.h>
 
 #include <media/mediaplayer.h>
+#include <media/AudioResamplerPublic.h>
 #include <media/AudioSystem.h>
 #include <media/AudioTimestamp.h>
+#include <media/AVSyncSettings.h>
 #include <media/Metadata.h>
 
 // Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -36,6 +38,7 @@
 
 namespace android {
 
+class DataSource;
 class Parcel;
 class Surface;
 class IGraphicBufferProducer;
@@ -131,11 +134,12 @@
         virtual void        pause() = 0;
         virtual void        close() = 0;
 
-        virtual status_t    setPlaybackRatePermille(int32_t rate) { return INVALID_OPERATION; }
+        virtual status_t    setPlaybackRate(const AudioPlaybackRate& rate) = 0;
+        virtual status_t    getPlaybackRate(AudioPlaybackRate* rate /* nonnull */) = 0;
         virtual bool        needsTrailingPadding() { return true; }
 
-        virtual status_t    setParameters(const String8& keyValuePairs) { return NO_ERROR; };
-        virtual String8     getParameters(const String8& keys) { return String8::empty(); };
+        virtual status_t    setParameters(const String8& /* keyValuePairs */) { return NO_ERROR; }
+        virtual String8     getParameters(const String8& /* keys */) { return String8::empty(); }
     };
 
                         MediaPlayerBase() : mCookie(0), mNotify(0) {}
@@ -143,7 +147,7 @@
     virtual status_t    initCheck() = 0;
     virtual bool        hardwareOutput() = 0;
 
-    virtual status_t    setUID(uid_t uid) {
+    virtual status_t    setUID(uid_t /* uid */) {
         return INVALID_OPERATION;
     }
 
@@ -154,7 +158,11 @@
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
 
-    virtual status_t    setDataSource(const sp<IStreamSource> &source) {
+    virtual status_t    setDataSource(const sp<IStreamSource>& /* source */) {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t    setDataSource(const sp<DataSource>& /* source */) {
         return INVALID_OPERATION;
     }
 
@@ -168,7 +176,31 @@
     virtual status_t    stop() = 0;
     virtual status_t    pause() = 0;
     virtual bool        isPlaying() = 0;
-    virtual status_t    setPlaybackRate(float rate) { return INVALID_OPERATION; }
+    virtual status_t    setPlaybackSettings(const AudioPlaybackRate& rate) {
+        // by default, players only support setting rate to the default
+        if (!isAudioPlaybackRateEqual(rate, AUDIO_PLAYBACK_RATE_DEFAULT)) {
+            return BAD_VALUE;
+        }
+        return OK;
+    }
+    virtual status_t    getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) {
+        *rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+        return OK;
+    }
+    virtual status_t    setSyncSettings(const AVSyncSettings& sync, float /* videoFps */) {
+        // By default, players only support setting sync source to default; all other sync
+        // settings are ignored. There is no requirement for getters to return set values.
+        if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
+            return BAD_VALUE;
+        }
+        return OK;
+    }
+    virtual status_t    getSyncSettings(
+                                AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
+        *sync = AVSyncSettings();
+        *videoFps = -1.f;
+        return OK;
+    }
     virtual status_t    seekTo(int msec) = 0;
     virtual status_t    getCurrentPosition(int *msec) = 0;
     virtual status_t    getDuration(int *msec) = 0;
@@ -179,13 +211,13 @@
     virtual status_t    getParameter(int key, Parcel *reply) = 0;
 
     // default no-op implementation of optional extensions
-    virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) {
+    virtual status_t setRetransmitEndpoint(const struct sockaddr_in* /* endpoint */) {
         return INVALID_OPERATION;
     }
-    virtual status_t getRetransmitEndpoint(struct sockaddr_in* endpoint) {
+    virtual status_t getRetransmitEndpoint(struct sockaddr_in* /* endpoint */) {
         return INVALID_OPERATION;
     }
-    virtual status_t setNextPlayer(const sp<MediaPlayerBase>& next) {
+    virtual status_t setNextPlayer(const sp<MediaPlayerBase>& /* next */) {
         return OK;
     }
 
@@ -205,8 +237,8 @@
     //            the known metadata should be returned.
     // @param[inout] records Parcel where the player appends its metadata.
     // @return OK if the call was successful.
-    virtual status_t    getMetadata(const media::Metadata::Filter& ids,
-                                    Parcel *records) {
+    virtual status_t    getMetadata(const media::Metadata::Filter& /* ids */,
+                                    Parcel* /* records */) {
         return INVALID_OPERATION;
     };
 
@@ -229,7 +261,7 @@
         if (notifyCB) notifyCB(cookie, msg, ext1, ext2, obj);
     }
 
-    virtual status_t dump(int fd, const Vector<String16> &args) const {
+    virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
         return INVALID_OPERATION;
     }
 
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index f55063e..d6cc4bb 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -26,10 +26,12 @@
 
 class ICameraRecordingProxy;
 class Surface;
+class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
 
 struct MediaRecorderBase {
-    MediaRecorderBase() {}
+    MediaRecorderBase(const String16 &opPackageName)
+        : mOpPackageName(opPackageName) {}
     virtual ~MediaRecorderBase() {}
 
     virtual status_t init() = 0;
@@ -55,8 +57,13 @@
     virtual status_t reset() = 0;
     virtual status_t getMaxAmplitude(int *max) = 0;
     virtual status_t dump(int fd, const Vector<String16>& args) const = 0;
+    virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface) = 0;
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const = 0;
 
+
+protected:
+    String16 mOpPackageName;
+
 private:
     MediaRecorderBase(const MediaRecorderBase &);
     MediaRecorderBase &operator=(const MediaRecorderBase &);
diff --git a/include/media/MediaResource.h b/include/media/MediaResource.h
index 0b57c84..20f2cad 100644
--- a/include/media/MediaResource.h
+++ b/include/media/MediaResource.h
@@ -25,6 +25,8 @@
 
 extern const char kResourceSecureCodec[];
 extern const char kResourceNonSecureCodec[];
+extern const char kResourceAudioCodec[];
+extern const char kResourceVideoCodec[];
 extern const char kResourceGraphicMemory[];
 
 class MediaResource {
diff --git a/include/media/MediaResourcePolicy.h b/include/media/MediaResourcePolicy.h
index 1e1c341..9bc2eec 100644
--- a/include/media/MediaResourcePolicy.h
+++ b/include/media/MediaResourcePolicy.h
@@ -29,7 +29,7 @@
 class MediaResourcePolicy {
 public:
     MediaResourcePolicy();
-    MediaResourcePolicy(String8 type, uint64_t value);
+    MediaResourcePolicy(String8 type, String8 value);
 
     void readFromParcel(const Parcel &parcel);
     void writeToParcel(Parcel *parcel) const;
@@ -37,7 +37,7 @@
     String8 toString() const;
 
     String8 mType;
-    uint64_t mValue;
+    String8 mValue;
 };
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/include/media/RingBuffer.h
similarity index 100%
rename from services/camera/libcameraservice/utils/RingBuffer.h
rename to include/media/RingBuffer.h
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 6167dd6..b92f816 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -65,7 +65,8 @@
     /* Constructor.
      * See AudioEffect constructor for details on parameters.
      */
-                        Visualizer(int32_t priority = 0,
+                        Visualizer(const String16& opPackageName,
+                                   int32_t priority = 0,
                                    effect_callback_t cbf = NULL,
                                    void* user = NULL,
                                    int sessionId = 0);
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index b35cf32..f655f35 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -25,6 +25,7 @@
 
 namespace android {
 
+class IDataSource;
 struct IMediaHTTPService;
 class IMediaPlayerService;
 class IMediaMetadataRetriever;
@@ -57,6 +58,7 @@
     METADATA_KEY_IS_DRM          = 22,
     METADATA_KEY_LOCATION        = 23,
     METADATA_KEY_VIDEO_ROTATION  = 24,
+    METADATA_KEY_CAPTURE_FRAMERATE = 25,
 
     // Add more here...
 };
@@ -74,6 +76,7 @@
             const KeyedVector<String8, String8> *headers = NULL);
 
     status_t setDataSource(int fd, int64_t offset, int64_t length);
+    status_t setDataSource(const sp<IDataSource>& dataSource);
     sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
     sp<IMemory> extractAlbumArt();
     const char* extractMetadata(int keyCode);
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 808e893..3fe749c 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -20,6 +20,8 @@
 #include <arpa/inet.h>
 
 #include <binder/IMemory.h>
+
+#include <media/AudioResamplerPublic.h>
 #include <media/IMediaPlayerClient.h>
 #include <media/IMediaPlayer.h>
 #include <media/IMediaDeathNotifier.h>
@@ -32,8 +34,9 @@
 
 namespace android {
 
-class Surface;
+struct AVSyncSettings;
 class IGraphicBufferProducer;
+class Surface;
 
 enum media_event_type {
     MEDIA_NOP               = 0, // interface test message
@@ -50,6 +53,7 @@
     MEDIA_ERROR             = 100,
     MEDIA_INFO              = 200,
     MEDIA_SUBTITLE_DATA     = 201,
+    MEDIA_META_DATA         = 202,
 };
 
 // Generic error codes for the media player framework.  Errors are fatal, the
@@ -183,6 +187,7 @@
     MEDIA_TRACK_TYPE_AUDIO = 2,
     MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
     MEDIA_TRACK_TYPE_SUBTITLE = 4,
+    MEDIA_TRACK_TYPE_METADATA = 5,
 };
 
 // ----------------------------------------------------------------------------
@@ -211,6 +216,7 @@
 
             status_t        setDataSource(int fd, int64_t offset, int64_t length);
             status_t        setDataSource(const sp<IStreamSource> &source);
+            status_t        setDataSource(const sp<IDataSource> &source);
             status_t        setVideoSurfaceTexture(
                                     const sp<IGraphicBufferProducer>& bufferProducer);
             status_t        setListener(const sp<MediaPlayerListener>& listener);
@@ -220,7 +226,12 @@
             status_t        stop();
             status_t        pause();
             bool            isPlaying();
-            status_t        setPlaybackRate(float rate);
+            status_t        setPlaybackSettings(const AudioPlaybackRate& rate);
+            status_t        getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
+            status_t        setSyncSettings(const AVSyncSettings& sync, float videoFpsHint);
+            status_t        getSyncSettings(
+                                    AVSyncSettings* sync /* nonnull */,
+                                    float* videoFps /* nonnull */);
             status_t        getVideoWidth(int *w);
             status_t        getVideoHeight(int *h);
             status_t        seekTo(int msec);
@@ -275,7 +286,6 @@
     int                         mVideoWidth;
     int                         mVideoHeight;
     int                         mAudioSessionId;
-    float                       mPlaybackRate;
     float                       mSendLevel;
     struct sockaddr_in          mRetransmitEndpoint;
     bool                        mRetransmitEndpointValid;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 74a6469..15ff82d 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -32,6 +32,7 @@
 class ICamera;
 class ICameraRecordingProxy;
 class IGraphicBufferProducer;
+struct PersistentSurface;
 class Surface;
 
 typedef void (*media_completion_f)(status_t status, void *cookie);
@@ -209,7 +210,7 @@
                       public virtual IMediaDeathNotifier
 {
 public:
-    MediaRecorder();
+    MediaRecorder(const String16& opPackageName);
     ~MediaRecorder();
 
     void        died();
@@ -236,6 +237,7 @@
     status_t    close();
     status_t    release();
     void        notify(int msg, int ext1, int ext2);
+    status_t    setInputSurface(const sp<PersistentSurface>& surface);
     sp<IGraphicBufferProducer>     querySurfaceMediaSourceFromMediaServer();
 
 private:
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
index 86417a5..aa60a19 100644
--- a/include/media/stagefright/AACWriter.h
+++ b/include/media/stagefright/AACWriter.h
@@ -24,7 +24,7 @@
 namespace android {
 
 struct MediaSource;
-struct MetaData;
+class MetaData;
 
 struct AACWriter : public MediaWriter {
     AACWriter(int fd);
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index c1483f3..54c57ad 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -44,9 +44,12 @@
     virtual void initiateAllocateComponent(const sp<AMessage> &msg);
     virtual void initiateConfigureComponent(const sp<AMessage> &msg);
     virtual void initiateCreateInputSurface();
+    virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface);
     virtual void initiateStart();
     virtual void initiateShutdown(bool keepComponentAllocated = false);
 
+    virtual status_t setSurface(const sp<Surface> &surface);
+
     virtual void signalFlush();
     virtual void signalResume();
 
@@ -113,7 +116,9 @@
         kWhatDrainDeferredMessages   = 'drai',
         kWhatAllocateComponent       = 'allo',
         kWhatConfigureComponent      = 'conf',
+        kWhatSetSurface              = 'setS',
         kWhatCreateInputSurface      = 'cisf',
+        kWhatSetInputSurface         = 'sisf',
         kWhatSignalEndOfInputStream  = 'eois',
         kWhatStart                   = 'star',
         kWhatRequestIDRFrame         = 'ridr',
@@ -192,6 +197,7 @@
     List<sp<AMessage> > mDeferredQueue;
 
     bool mSentFormat;
+    bool mIsVideo;
     bool mIsEncoder;
     bool mUseMetadataOnEncoderOutput;
     bool mShutdownInProgress;
@@ -209,6 +215,7 @@
     int32_t mChannelMask;
     unsigned mDequeueCounter;
     bool mStoreMetaDataInOutputBuffers;
+    bool mLegacyAdaptiveExperiment;
     int32_t mMetaDataBuffersToSubmit;
     size_t mNumUndequeuedBuffers;
 
@@ -228,6 +235,9 @@
     status_t freeBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffer(OMX_U32 portIndex, size_t i);
 
+    status_t handleSetSurface(const sp<Surface> &surface);
+    status_t setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */);
+
     status_t configureOutputBuffersFromNativeWindow(
             OMX_U32 *nBufferCount, OMX_U32 *nBufferSize,
             OMX_U32 *nMinUndequeuedBuffers);
@@ -300,6 +310,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);
 
@@ -318,8 +329,6 @@
 
     status_t initNativeWindow();
 
-    status_t pushBlankBuffersToNativeWindow();
-
     // Returns true iff all buffers on the given port have status
     // OWNED_BY_US or OWNED_BY_NATIVE_WINDOW.
     bool allYourBuffersAreBelongToUs(OMX_U32 portIndex);
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index bac878b..b38be55 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -26,7 +26,7 @@
 namespace android {
 
 struct MediaSource;
-struct MetaData;
+class MetaData;
 
 struct AMRWriter : public MediaWriter {
     AMRWriter(int fd);
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 14afb85..e0cd965 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -25,9 +25,10 @@
 
 namespace android {
 
-class MediaSource;
+struct AudioPlaybackRate;
 class AudioTrack;
-class AwesomePlayer;
+struct AwesomePlayer;
+class MediaSource;
 
 class AudioPlayer : public TimeSource {
 public:
@@ -73,7 +74,8 @@
     bool isSeeking();
     bool reachedEOS(status_t *finalStatus);
 
-    status_t setPlaybackRatePermille(int32_t ratePermille);
+    status_t setPlaybackRate(const AudioPlaybackRate &rate);
+    status_t getPlaybackRate(AudioPlaybackRate *rate /* nonnull */);
 
     void notifyAudioEOS();
 
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 4c9aaad..50cf371 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -35,6 +35,7 @@
     // _not_ a bitmask of audio_channels_t constants.
     AudioSource(
             audio_source_t inputSource,
+            const String16 &opPackageName,
             uint32_t sampleRate,
             uint32_t channels = 1);
 
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index dd0a106..96dfd7e 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -188,7 +188,7 @@
     void releaseCamera();
 
 private:
-    friend class CameraSourceListener;
+    friend struct CameraSourceListener;
 
     Mutex mLock;
     Condition mFrameAvailableCondition;
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 1bf27a6..989df4f 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -26,6 +26,7 @@
 namespace android {
 
 struct ABuffer;
+struct PersistentSurface;
 
 struct CodecBase : public AHandler {
     enum {
@@ -39,6 +40,7 @@
         kWhatComponentAllocated  = 'cAll',
         kWhatComponentConfigured = 'cCon',
         kWhatInputSurfaceCreated = 'isfc',
+        kWhatInputSurfaceAccepted = 'isfa',
         kWhatSignaledInputEOS    = 'seos',
         kWhatBuffersAllocated    = 'allc',
     };
@@ -48,12 +50,16 @@
     virtual void initiateAllocateComponent(const sp<AMessage> &msg) = 0;
     virtual void initiateConfigureComponent(const sp<AMessage> &msg) = 0;
     virtual void initiateCreateInputSurface() = 0;
+    virtual void initiateSetInputSurface(
+            const sp<PersistentSurface> &surface) = 0;
     virtual void initiateStart() = 0;
     virtual void initiateShutdown(bool keepComponentAllocated = false) = 0;
 
     // require an explicit message handler
     virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
 
+    virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }
+
     virtual void signalFlush() = 0;
     virtual void signalResume() = 0;
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3630263..dcde36f 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -32,6 +32,7 @@
 
 struct AMessage;
 struct AString;
+class  IDataSource;
 struct IMediaHTTPService;
 class String8;
 struct HTTPBase;
@@ -53,11 +54,15 @@
             HTTPBase *httpSource = NULL);
 
     static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService);
+    static sp<DataSource> CreateFromIDataSource(const sp<IDataSource> &source);
 
     DataSource() {}
 
     virtual status_t initCheck() const = 0;
 
+    // Returns the number of bytes read, or -1 on failure. It's not an error if
+    // this returns zero; it just means the given offset is equal to, or
+    // beyond, the end of the source.
     virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0;
 
     // Convenience methods:
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..6e14fc5 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -20,6 +20,7 @@
 
 #include <gui/IGraphicBufferProducer.h>
 #include <media/hardware/CryptoAPI.h>
+#include <media/MediaResource.h>
 #include <media/stagefright/foundation/AHandler.h>
 #include <utils/Vector.h>
 
@@ -30,8 +31,13 @@
 struct AReplyToken;
 struct AString;
 struct CodecBase;
-struct ICrypto;
 struct IBatteryStats;
+struct ICrypto;
+class IMemory;
+struct MemoryDealer;
+class IResourceManagerClient;
+class IResourceManagerService;
+struct PersistentSurface;
 struct SoftwareRenderer;
 struct Surface;
 
@@ -51,6 +57,7 @@
         CB_OUTPUT_AVAILABLE = 2,
         CB_ERROR = 3,
         CB_OUTPUT_FORMAT_CHANGED = 4,
+        CB_RESOURCE_RECLAIMED = 5,
     };
 
     struct BatteryNotifier;
@@ -61,6 +68,8 @@
     static sp<MediaCodec> CreateByComponentName(
             const sp<ALooper> &looper, const char *name, status_t *err = NULL);
 
+    static sp<PersistentSurface> CreatePersistentInputSurface();
+
     status_t configure(
             const sp<AMessage> &format,
             const sp<Surface> &nativeWindow,
@@ -71,6 +80,8 @@
 
     status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
 
+    status_t setInputSurface(const sp<PersistentSurface> &surface);
+
     status_t start();
 
     // Returns to a state in which the component remains allocated but
@@ -126,6 +137,8 @@
     status_t getOutputFormat(sp<AMessage> *format) const;
     status_t getInputFormat(sp<AMessage> *format) const;
 
+    status_t getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const;
+
     status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
     status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
 
@@ -133,6 +146,8 @@
     status_t getOutputFormat(size_t index, sp<AMessage> *format);
     status_t getInputBuffer(size_t index, sp<ABuffer> *buffer);
 
+    status_t setSurface(const sp<Surface> &nativeWindow);
+
     status_t requestIDRFrame();
 
     // Notification will be posted once there "is something to do", i.e.
@@ -149,6 +164,11 @@
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
 private:
+    // used by ResourceManagerClient
+    status_t reclaim();
+    friend struct ResourceManagerClient;
+
+private:
     enum State {
         UNINITIALIZED,
         INITIALIZING,
@@ -171,7 +191,9 @@
     enum {
         kWhatInit                           = 'init',
         kWhatConfigure                      = 'conf',
+        kWhatSetSurface                     = 'sSur',
         kWhatCreateInputSurface             = 'cisf',
+        kWhatSetInputSurface                = 'sisf',
         kWhatStart                          = 'strt',
         kWhatStop                           = 'stop',
         kWhatRelease                        = 'rele',
@@ -207,18 +229,45 @@
         kFlagGatherCodecSpecificData    = 512,
         kFlagIsAsync                    = 1024,
         kFlagIsComponentAllocated       = 2048,
+        kFlagPushBlankBuffersOnShutdown = 4096,
     };
 
     struct BufferInfo {
         uint32_t mBufferID;
         sp<ABuffer> mData;
         sp<ABuffer> mEncryptedData;
+        sp<IMemory> mSharedEncryptedBuffer;
         sp<AMessage> mNotify;
         sp<AMessage> mFormat;
         bool mOwnedByClient;
     };
 
+    struct ResourceManagerServiceProxy : public IBinder::DeathRecipient {
+        ResourceManagerServiceProxy();
+        ~ResourceManagerServiceProxy();
+
+        void init();
+
+        // implements DeathRecipient
+        virtual void binderDied(const wp<IBinder>& /*who*/);
+
+        void addResource(
+                int pid,
+                int64_t clientId,
+                const sp<IResourceManagerClient> client,
+                const Vector<MediaResource> &resources);
+
+        void removeResource(int64_t clientId);
+
+        bool reclaimResource(int callingPid, const Vector<MediaResource> &resources);
+
+    private:
+        Mutex mLock;
+        sp<IResourceManagerService> mService;
+    };
+
     State mState;
+    bool mReleasedByResourceManager;
     sp<ALooper> mLooper;
     sp<ALooper> mCodecLooper;
     sp<CodecBase> mCodec;
@@ -226,20 +275,29 @@
     sp<AReplyToken> mReplyID;
     uint32_t mFlags;
     status_t mStickyError;
-    sp<Surface> mNativeWindow;
+    sp<Surface> mSurface;
     SoftwareRenderer *mSoftRenderer;
     sp<AMessage> mOutputFormat;
     sp<AMessage> mInputFormat;
     sp<AMessage> mCallback;
+    sp<MemoryDealer> mDealer;
+
+    sp<IResourceManagerClient> mResourceManagerClient;
+    sp<ResourceManagerServiceProxy> mResourceManagerService;
 
     bool mBatteryStatNotified;
     bool mIsVideo;
+    int32_t mVideoWidth;
+    int32_t mVideoHeight;
 
     // initial create parameters
     AString mInitName;
     bool mInitNameIsType;
     bool mInitIsEncoder;
 
+    // configure parameter
+    sp<AMessage> mConfigureMsg;
+
     // Used only to synchronize asynchronous getBufferAndFormat
     // across all the other (synchronous) buffer state change
     // operations, such as de/queueIn/OutputBuffer, start and
@@ -262,13 +320,14 @@
     sp<AMessage> mActivityNotify;
 
     bool mHaveInputSurface;
+    bool mHavePendingInputBuffers;
 
     MediaCodec(const sp<ALooper> &looper);
 
     static status_t PostAndAwaitResponse(
             const sp<AMessage> &msg, sp<AMessage> *response);
 
-    static void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
+    void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
 
     status_t init(const AString &name, bool nameIsType, bool encoder);
 
@@ -291,8 +350,9 @@
     void extractCSD(const sp<AMessage> &format);
     status_t queueCSDInputBuffer(size_t bufferIndex);
 
-    status_t setNativeWindow(
-            const sp<Surface> &surface);
+    status_t handleSetSurface(const sp<Surface> &surface);
+    status_t connectToSurface(const sp<Surface> &surface);
+    status_t disconnectFromSurface();
 
     void postActivityNotificationIfPossible();
 
@@ -307,6 +367,9 @@
     void updateBatteryStat();
     bool isExecuting() const;
 
+    uint64_t getGraphicBufferSize();
+    void addResource(const String8 &type, const String8 &subtype, uint64_t value);
+
     /* called to get the last codec error when the sticky flag is set.
      * if no such codec error is found, returns UNKNOWN_ERROR.
      */
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index c2bbe4d..ce34338 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -48,9 +48,14 @@
         return mCodecInfos.itemAt(index);
     }
 
+    virtual const sp<AMessage> getGlobalSettings() const;
+
     // to be used by MediaPlayerService alone
     static sp<IMediaCodecList> getLocalInstance();
 
+    // only to be used in getLocalInstance
+    void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
+
 private:
     class BinderDeathObserver : public IBinder::DeathRecipient {
         void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -60,6 +65,7 @@
 
     enum Section {
         SECTION_TOPLEVEL,
+        SECTION_SETTINGS,
         SECTION_DECODERS,
         SECTION_DECODER,
         SECTION_DECODER_TYPE,
@@ -74,10 +80,14 @@
 
     status_t mInitCheck;
     Section mCurrentSection;
+    bool mUpdate;
     Vector<Section> mPastSections;
     int32_t mDepth;
     AString mHrefBase;
 
+    sp<AMessage> mGlobalSettings;
+    KeyedVector<AString, CodecSettings> mOverrides;
+
     Vector<sp<MediaCodecInfo> > mCodecInfos;
     sp<MediaCodecInfo> mCurrentInfo;
     sp<IOMX> mOMX;
@@ -87,7 +97,6 @@
 
     status_t initCheck() const;
     void parseXMLFile(const char *path);
-    void parseTopLevelXMLFile(const char *path);
 
     static void StartElementHandlerWrapper(
             void *me, const char *name, const char **attrs);
@@ -98,9 +107,12 @@
     void endElementHandler(const char *name);
 
     status_t includeXMLFile(const char **attrs);
+    status_t addSettingFromAttributes(const char **attrs);
     status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
     void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
 
+    void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+
     status_t addQuirk(const char **attrs);
     status_t addTypeFromAttributes(const char **attrs);
     status_t addLimit(const char **attrs);
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index 7b8f59d..a991b02 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -23,10 +23,11 @@
 
 namespace android {
 
-class ALooper;
+struct ALooper;
 class AMessage;
 struct AReplyToken;
 class IGraphicBufferProducer;
+class IGraphicBufferConsumer;
 class MediaCodec;
 class MetaData;
 
@@ -41,6 +42,7 @@
             const sp<ALooper> &looper,
             const sp<AMessage> &format,
             const sp<MediaSource> &source,
+            const sp<IGraphicBufferConsumer> &consumer = NULL,
             uint32_t flags = 0);
 
     bool isVideo() const { return mIsVideo; }
@@ -79,6 +81,7 @@
             const sp<ALooper> &looper,
             const sp<AMessage> &outputFormat,
             const sp<MediaSource> &source,
+            const sp<IGraphicBufferConsumer> &consumer,
             uint32_t flags = 0);
 
     status_t onStart(MetaData *params);
@@ -107,6 +110,7 @@
     bool mDoMoreWorkPending;
     sp<AMessage> mEncoderActivityNotify;
     sp<IGraphicBufferProducer> mGraphicBufferProducer;
+    sp<IGraphicBufferConsumer> mGraphicBufferConsumer;
     List<MediaBuffer *> mInputBufferQueue;
     List<size_t> mAvailEncoderInputIndices;
     List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index a0036e0..3b58122 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -64,6 +64,7 @@
 extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
 extern const char *MEDIA_MIMETYPE_TEXT_VTT;
 extern const char *MEDIA_MIMETYPE_TEXT_CEA_608;
+extern const char *MEDIA_MIMETYPE_DATA_METADATA;
 
 }  // namespace android
 
diff --git a/include/media/stagefright/MediaFilter.h b/include/media/stagefright/MediaFilter.h
index 7b3f700..d0a572c 100644
--- a/include/media/stagefright/MediaFilter.h
+++ b/include/media/stagefright/MediaFilter.h
@@ -34,6 +34,8 @@
     virtual void initiateAllocateComponent(const sp<AMessage> &msg);
     virtual void initiateConfigureComponent(const sp<AMessage> &msg);
     virtual void initiateCreateInputSurface();
+    virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface);
+
     virtual void initiateStart();
     virtual void initiateShutdown(bool keepComponentAllocated = false);
 
diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h
index e6538d1..fa855a8 100644
--- a/include/media/stagefright/MediaMuxer.h
+++ b/include/media/stagefright/MediaMuxer.h
@@ -29,9 +29,9 @@
 struct ABuffer;
 struct AMessage;
 struct MediaAdapter;
-struct MediaBuffer;
+class MediaBuffer;
 struct MediaSource;
-struct MetaData;
+class MetaData;
 struct MediaWriter;
 
 // MediaMuxer is used to mux multiple tracks into a video. Currently, we only
diff --git a/include/media/stagefright/MediaSync.h b/include/media/stagefright/MediaSync.h
index 8bb8c7f..1eef211 100644
--- a/include/media/stagefright/MediaSync.h
+++ b/include/media/stagefright/MediaSync.h
@@ -20,9 +20,12 @@
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
 
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
 #include <media/stagefright/foundation/AHandler.h>
 
 #include <utils/Condition.h>
+#include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 
 namespace android {
@@ -72,14 +75,11 @@
 
     // Called when MediaSync is used to render video. It should be called
     // before createInputSurface().
-    status_t configureSurface(const sp<IGraphicBufferProducer> &output);
+    status_t setSurface(const sp<IGraphicBufferProducer> &output);
 
     // Called when audio track is used as media clock source. It should be
     // called before updateQueuedAudioData().
-    // |nativeSampleRateInHz| is the sample rate of audio data fed into audio
-    // track. It's the same number used to create AudioTrack.
-    status_t configureAudioTrack(
-            const sp<AudioTrack> &audioTrack, uint32_t nativeSampleRateInHz);
+    status_t setAudioTrack(const sp<AudioTrack> &audioTrack);
 
     // Create a surface for client to render video frames. This is the surface
     // on which the client should render video frames. Those video frames will
@@ -98,21 +98,34 @@
     // Set the consumer name of the input queue.
     void setName(const AString &name);
 
-    // Set the playback in a desired speed.
-    // This method can be called any time.
-    // |rate| is the ratio between desired speed and the normal one, and should
-    // be non-negative. The meaning of rate values:
-    // 1.0 -- normal playback
-    // 0.0 -- stop or pause
-    // larger than 1.0 -- faster than normal speed
-    // between 0.0 and 1.0 -- slower than normal speed
-    status_t setPlaybackRate(float rate);
-
     // Get the media clock used by the MediaSync so that the client can obtain
     // corresponding media time or real time via
     // MediaClock::getMediaTime() and MediaClock::getRealTimeFor().
     sp<const MediaClock> getMediaClock();
 
+    // Set the video frame rate hint - this is used by the video FrameScheduler
+    status_t setVideoFrameRateHint(float rate);
+
+    // Get the video frame rate measurement from the FrameScheduler
+    // returns -1 if there is no measurement
+    float getVideoFrameRate();
+
+    // Set the sync settings parameters.
+    status_t setSyncSettings(const AVSyncSettings &syncSettings);
+
+    // Gets the sync settings parameters.
+    void getSyncSettings(AVSyncSettings *syncSettings /* nonnull */);
+
+    // Sets the playback rate using playback settings.
+    // This method can be called any time.
+    status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+
+    // Gets the playback rate (playback settings parameters).
+    void getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+
+    // Get the play time for pending audio frames in audio sink.
+    status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+
 protected:
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
@@ -156,7 +169,7 @@
     class OutputListener : public BnProducerListener,
                            public IBinder::DeathRecipient {
     public:
-        OutputListener(const sp<MediaSync> &sync);
+        OutputListener(const sp<MediaSync> &sync, const sp<IGraphicBufferProducer> &output);
         virtual ~OutputListener();
 
         // From IProducerListener
@@ -167,6 +180,7 @@
 
     private:
         sp<MediaSync> mSync;
+        sp<IGraphicBufferProducer> mOutput;
     };
 
     // mIsAbandoned is set to true when the input or output dies.
@@ -179,6 +193,7 @@
     size_t mNumOutstandingBuffers;
     sp<IGraphicBufferConsumer> mInput;
     sp<IGraphicBufferProducer> mOutput;
+    int mUsageFlagsFromOutput;
 
     sp<AudioTrack> mAudioTrack;
     uint32_t mNativeSampleRateInHz;
@@ -187,9 +202,25 @@
 
     int64_t mNextBufferItemMediaUs;
     List<BufferItem> mBufferItems;
+
+    // Keep track of buffers received from |mInput|. This is needed because
+    // it's possible the consumer of |mOutput| could return a different
+    // GraphicBuffer::handle (e.g., due to passing buffers through IPC),
+    // and that could cause problem if the producer of |mInput| only
+    // supports pre-registered buffers.
+    KeyedVector<uint64_t, sp<GraphicBuffer> > mBuffersFromInput;
+
+    // Keep track of buffers sent to |mOutput|. When a new output surface comes
+    // in, those buffers will be returned to input and old output surface will
+    // be disconnected immediately.
+    KeyedVector<uint64_t, sp<GraphicBuffer> > mBuffersSentToOutput;
+
     sp<ALooper> mLooper;
     float mPlaybackRate;
 
+    AudioPlaybackRate mPlaybackSettings;
+    AVSyncSettings mSyncSettings;
+
     sp<MediaClock> mMediaClock;
 
     MediaSync();
@@ -218,7 +249,7 @@
     // It gets called from an OutputListener.
     // During this callback, we detach the buffer from the output, and release
     // it to the input. A blocked onFrameAvailable call will be allowed to proceed.
-    void onBufferReleasedByOutput();
+    void onBufferReleasedByOutput(sp<IGraphicBufferProducer> &output);
 
     // Return |buffer| back to the input.
     void returnBufferToInput_l(const sp<GraphicBuffer> &buffer, const sp<Fence> &fence);
@@ -228,6 +259,22 @@
     // up. This must be called with mMutex locked.
     void onAbandoned_l(bool isInput);
 
+    // Set the playback in a desired speed.
+    // This method can be called any time.
+    // |rate| is the ratio between desired speed and the normal one, and should
+    // be non-negative. The meaning of rate values:
+    // 1.0 -- normal playback
+    // 0.0 -- stop or pause
+    // larger than 1.0 -- faster than normal speed
+    // between 0.0 and 1.0 -- slower than normal speed
+    void updatePlaybackRate_l(float rate);
+
+    // apply new sync settings
+    void resync_l();
+
+    // apply playback settings only - without resyncing or updating playback rate
+    status_t setPlaybackSettings_l(const AudioPlaybackRate &rate);
+
     // helper.
     bool isPlaying() { return mPlaybackRate != 0.0; }
 
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index e27ea1d..8e02506 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -24,7 +24,7 @@
 namespace android {
 
 struct MediaSource;
-struct MetaData;
+class MetaData;
 
 struct MediaWriter : public RefBase {
     MediaWriter()
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 087d016..ca80123 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -75,6 +75,8 @@
     kKeyDecoderComponent  = 'decC',  // cstring
     kKeyBufferID          = 'bfID',
     kKeyMaxInputSize      = 'inpS',
+    kKeyMaxWidth          = 'maxW',
+    kKeyMaxHeight         = 'maxH',
     kKeyThumbnailTime     = 'thbT',  // int64_t (usecs)
     kKeyTrackID           = 'trID',
     kKeyIsDRM             = 'idrm',  // int32_t (bool)
@@ -98,6 +100,7 @@
     kKeyCompilation       = 'cpil',  // cstring
     kKeyLocation          = 'loc ',  // cstring
     kKeyTimeScale         = 'tmsl',  // int32_t
+    kKeyCaptureFramerate  = 'capF',  // float (capture fps)
 
     // video profile and level
     kKeyVideoProfile      = 'vprf',  // int32_t
@@ -173,6 +176,9 @@
     kKeyTrackIsDefault    = 'dflt', // bool (int32_t)
     // Similar to MediaFormat.KEY_IS_FORCED_SUBTITLE but pertains to av tracks as well.
     kKeyTrackIsForced     = 'frcd', // bool (int32_t)
+
+    // H264 supplemental enhancement information offsets/sizes
+    kKeySEI               = 'sei ', // raw data
 };
 
 enum {
diff --git a/include/media/stagefright/NativeWindowWrapper.h b/include/media/stagefright/NativeWindowWrapper.h
deleted file mode 100644
index cfeec22..0000000
--- a/include/media/stagefright/NativeWindowWrapper.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 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 NATIVE_WINDOW_WRAPPER_H_
-
-#define NATIVE_WINDOW_WRAPPER_H_
-
-#include <gui/Surface.h>
-
-namespace android {
-
-// Surface derives from ANativeWindow which derives from multiple
-// base classes, in order to carry it in AMessages, we'll temporarily wrap it
-// into a NativeWindowWrapper.
-
-struct NativeWindowWrapper : RefBase {
-    NativeWindowWrapper(
-            const sp<Surface> &surfaceTextureClient) :
-        mSurfaceTextureClient(surfaceTextureClient) { }
-
-    sp<ANativeWindow> getNativeWindow() const {
-        return mSurfaceTextureClient;
-    }
-
-    sp<Surface> getSurfaceTextureClient() const {
-        return mSurfaceTextureClient;
-    }
-
-private:
-    const sp<Surface> mSurfaceTextureClient;
-
-    DISALLOW_EVIL_CONSTRUCTORS(NativeWindowWrapper);
-};
-
-}  // namespace android
-
-#endif  // NATIVE_WINDOW_WRAPPER_H_
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 402e7f8..fd74452 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -30,12 +30,12 @@
 
 struct ABuffer;
 struct AMessage;
-struct DataSource;
+class DataSource;
 struct IMediaHTTPService;
-struct MediaBuffer;
+class MediaBuffer;
 struct MediaExtractor;
 struct MediaSource;
-struct MetaData;
+class MetaData;
 
 struct NuMediaExtractor : public RefBase {
     enum SampleFlags {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 84b1b1a..7fabcb3 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -298,7 +298,6 @@
     status_t queueBufferToNativeWindow(BufferInfo *info);
     status_t cancelBufferToNativeWindow(BufferInfo *info);
     BufferInfo* dequeueBufferFromNativeWindow();
-    status_t pushBlankBuffersToNativeWindow();
 
     status_t freeBuffersOnPort(
             OMX_U32 portIndex, bool onlyThoseWeOwn = false);
@@ -347,7 +346,6 @@
 
     status_t configureCodec(const sp<MetaData> &meta);
 
-    status_t applyRotation();
     status_t waitForBufferFilled_l();
 
     int64_t getDecodingTimeUs();
diff --git a/include/media/stagefright/PersistentSurface.h b/include/media/stagefright/PersistentSurface.h
new file mode 100644
index 0000000..a35b9f1
--- /dev/null
+++ b/include/media/stagefright/PersistentSurface.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PERSISTENT_SURFACE_H_
+
+#define PERSISTENT_SURFACE_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct PersistentSurface : public RefBase {
+    PersistentSurface(
+            const sp<IGraphicBufferProducer>& bufferProducer,
+            const sp<IGraphicBufferConsumer>& bufferConsumer) :
+        mBufferProducer(bufferProducer),
+        mBufferConsumer(bufferConsumer) { }
+
+    sp<IGraphicBufferProducer> getBufferProducer() const {
+        return mBufferProducer;
+    }
+
+    sp<IGraphicBufferConsumer> getBufferConsumer() const {
+        return mBufferConsumer;
+    }
+
+private:
+    const sp<IGraphicBufferProducer> mBufferProducer;
+    const sp<IGraphicBufferConsumer> mBufferConsumer;
+
+    DISALLOW_EVIL_CONSTRUCTORS(PersistentSurface);
+};
+
+}  // namespace android
+
+#endif  // PERSISTENT_SURFACE_H_
diff --git a/include/media/stagefright/SurfaceUtils.h b/include/media/stagefright/SurfaceUtils.h
new file mode 100644
index 0000000..c1a9c0a
--- /dev/null
+++ b/include/media/stagefright/SurfaceUtils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SURFACE_UTILS_H_
+
+#define SURFACE_UTILS_H_
+
+#include <utils/Errors.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+status_t setNativeWindowSizeFormatAndUsage(
+        ANativeWindow *nativeWindow /* nonnull */,
+        int width, int height, int format, int rotation, int usage);
+status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */);
+
+} // namespace android
+
+#endif  // SURFACE_UTILS_H_
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
index ec3a10e..5e9d7d4 100644
--- a/include/media/stagefright/Utils.h
+++ b/include/media/stagefright/Utils.h
@@ -41,7 +41,7 @@
 uint64_t ntoh64(uint64_t x);
 uint64_t hton64(uint64_t x);
 
-struct MetaData;
+class MetaData;
 struct AMessage;
 status_t convertMetaDataToMessage(
         const sp<MetaData> &meta, sp<AMessage> *format);
@@ -71,11 +71,20 @@
     sp<AMessage> mMeta;
 
     HLSTime(const sp<AMessage> &meta = NULL);
-    int64_t getSegmentTimeUs(bool midpoint = false) const;
+    int64_t getSegmentTimeUs() const;
 };
 
 bool operator <(const HLSTime &t0, const HLSTime &t1);
 
+// read and write various object to/from AMessage
+
+void writeToAMessage(sp<AMessage> msg, const AudioPlaybackRate &rate);
+void readFromAMessage(const sp<AMessage> &msg, AudioPlaybackRate *rate /* nonnull */);
+
+void writeToAMessage(sp<AMessage> msg, const AVSyncSettings &sync, float videoFpsHint);
+void readFromAMessage(
+        const sp<AMessage> &msg, AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
+
 }  // namespace android
 
 #endif  // UTILS_H_
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 1d0e2cb..a97dd9b 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -108,6 +108,26 @@
     // remove redundant segments of a codec name, and return a newly allocated
     // string suitable for debugging
     static char *GetDebugName(const char *name);
+
+    inline static bool isExperimentEnabled(
+            const char *name __unused /* nonnull */, bool allow __unused = true) {
+#ifdef ENABLE_STAGEFRIGHT_EXPERIMENTS
+        if (!strcmp(name, "legacy-adaptive")) {
+            return getExperimentFlag(allow, name, 2, 1); // every other day
+        } else if (!strcmp(name, "legacy-setsurface")) {
+            return getExperimentFlag(allow, name, 3, 1); // every third day
+        } else {
+            ALOGE("unknown experiment '%s' (disabled)", name);
+        }
+#endif
+        return false;
+    }
+
+private:
+    // pass in allow, so we can print in the log if the experiment is disabled
+    static bool getExperimentFlag(
+            bool allow, const char *name, uint64_t modulo, uint64_t limit,
+            uint64_t plus = 0, uint64_t timeDivisor = 24 * 60 * 60 /* 1 day */);
 };
 
 }  // namespace android
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 4c6bd21..83b9444 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -28,7 +28,7 @@
 struct ABuffer;
 struct AHandler;
 struct AString;
-struct Parcel;
+class Parcel;
 
 struct AReplyToken : public RefBase {
     AReplyToken(const sp<ALooper> &looper)
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
index 822dbb3..2f6d532 100644
--- a/include/media/stagefright/foundation/AString.h
+++ b/include/media/stagefright/foundation/AString.h
@@ -24,7 +24,7 @@
 namespace android {
 
 class String8;
-struct Parcel;
+class Parcel;
 
 struct AString {
     AString();
diff --git a/include/media/stagefright/foundation/AUtils.h b/include/media/stagefright/foundation/AUtils.h
index d7ecf50..47444c1 100644
--- a/include/media/stagefright/foundation/AUtils.h
+++ b/include/media/stagefright/foundation/AUtils.h
@@ -61,6 +61,28 @@
     return a > b ? a : b;
 }
 
+template<class T>
+void ENSURE_UNSIGNED_TYPE() {
+    T TYPE_MUST_BE_UNSIGNED[(T)-1 < 0 ? -1 : 0] __unused;
+}
+
+// needle is in range [hayStart, hayStart + haySize)
+template<class T, class U>
+inline static bool isInRange(const T &hayStart, const U &haySize, const T &needle) {
+    ENSURE_UNSIGNED_TYPE<U>();
+    return (T)(hayStart + haySize) >= hayStart && needle >= hayStart && (U)(needle - hayStart) < haySize;
+}
+
+// [needleStart, needleStart + needleSize) is in range [hayStart, hayStart + haySize)
+template<class T, class U>
+inline static bool isInRange(
+        const T &hayStart, const U &haySize, const T &needleStart, const U &needleSize) {
+    ENSURE_UNSIGNED_TYPE<U>();
+    return isInRange(hayStart, haySize, needleStart)
+            && (T)(needleStart + needleSize) >= needleStart
+            && (U)(needleStart + needleSize - hayStart) <= haySize;
+}
+
 /* T must be integer type, period must be positive */
 template<class T>
 inline static T periodicError(const T &val, const T &period) {
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index 37ef674..6f7c693 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-class ALooper;
+struct ALooper;
 struct IMediaHTTPService;
 class MediaPlayerBase;
 class MediaSource;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 5644428..1e5064f 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,8 @@
                     mPosLoopQueue;
 };
 
+typedef SingleStateQueue<AudioPlaybackRate> PlaybackRateQueue;
+
 // ----------------------------------------------------------------------------
 
 // Important: do not add any virtual methods, including ~
@@ -159,6 +162,8 @@
                 uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                             // or 0 == default. Write-only client, read-only server.
 
+                PlaybackRateQueue::Shared mPlaybackRateQueue;
+
                 // client write-only, server read-only
                 uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
 
@@ -313,7 +318,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 +339,10 @@
         mCblk->mSampleRate = sampleRate;
     }
 
+    void        setPlaybackRate(const AudioPlaybackRate& playbackRate) {
+        mPlaybackRateMutator.push(playbackRate);
+    }
+
     virtual void flush();
 
     virtual uint32_t    getUnderrunFrames() const {
@@ -344,6 +354,9 @@
     bool        getStreamEndDone() const;
 
     status_t    waitStreamEndDone(const struct timespec *requested);
+
+private:
+    PlaybackRateQueue::Mutator   mPlaybackRateMutator;
 };
 
 class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -458,8 +471,10 @@
 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 = AUDIO_PLAYBACK_RATE_DEFAULT;
     }
 protected:
     virtual ~AudioTrackServerProxy() { }
@@ -493,6 +508,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.
+    AudioPlaybackRate getPlaybackRate();
+
+private:
+    AudioPlaybackRate             mPlaybackRate;  // last observed playback rate
+    PlaybackRateQueue::Observer   mPlaybackRateObserver;
 };
 
 class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 3b260d6..0c18828 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -7,6 +7,9 @@
 LOCAL_MODULE:= libmedia_helper
 LOCAL_MODULE_TAGS := optional
 
+LOCAL_C_FLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -19,6 +22,7 @@
     IAudioTrack.cpp \
     IAudioRecord.cpp \
     ICrypto.cpp \
+    IDataSource.cpp \
     IDrm.cpp \
     IDrmClient.cpp \
     IHDCP.cpp \
@@ -83,5 +87,8 @@
     $(call include-path-for, audio-effects) \
     $(call include-path-for, audio-utils)
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 7d8222f..bbeb854 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -35,13 +35,14 @@
 
 // ---------------------------------------------------------------------------
 
-AudioEffect::AudioEffect()
-    : mStatus(NO_INIT)
+AudioEffect::AudioEffect(const String16& opPackageName)
+    : mStatus(NO_INIT), mOpPackageName(opPackageName)
 {
 }
 
 
 AudioEffect::AudioEffect(const effect_uuid_t *type,
+                const String16& opPackageName,
                 const effect_uuid_t *uuid,
                 int32_t priority,
                 effect_callback_t cbf,
@@ -49,12 +50,13 @@
                 int sessionId,
                 audio_io_handle_t io
                 )
-    : mStatus(NO_INIT)
+    : mStatus(NO_INIT), mOpPackageName(opPackageName)
 {
     mStatus = set(type, uuid, priority, cbf, user, sessionId, io);
 }
 
 AudioEffect::AudioEffect(const char *typeStr,
+                const String16& opPackageName,
                 const char *uuidStr,
                 int32_t priority,
                 effect_callback_t cbf,
@@ -62,7 +64,7 @@
                 int sessionId,
                 audio_io_handle_t io
                 )
-    : mStatus(NO_INIT)
+    : mStatus(NO_INIT), mOpPackageName(opPackageName)
 {
     effect_uuid_t type;
     effect_uuid_t *pType = NULL;
@@ -128,7 +130,7 @@
     mIEffectClient = new EffectClient(this);
 
     iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
-            mIEffectClient, priority, io, mSessionId, &mStatus, &mId, &enabled);
+            mIEffectClient, priority, io, mSessionId, mOpPackageName, &mStatus, &mId, &enabled);
 
     if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
         ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
diff --git a/media/libmedia/AudioPolicy.cpp b/media/libmedia/AudioPolicy.cpp
index c7dafcb..9d07011 100644
--- a/media/libmedia/AudioPolicy.cpp
+++ b/media/libmedia/AudioPolicy.cpp
@@ -68,6 +68,7 @@
     mFormat.format = (audio_format_t)parcel->readInt32();
     mRouteFlags = parcel->readInt32();
     mRegistrationId = parcel->readString8();
+    mCbFlags = (uint32_t)parcel->readInt32();
     size_t size = (size_t)parcel->readInt32();
     if (size > MAX_CRITERIA_PER_MIX) {
         size = MAX_CRITERIA_PER_MIX;
@@ -89,6 +90,7 @@
     parcel->writeInt32(mFormat.format);
     parcel->writeInt32(mRouteFlags);
     parcel->writeString8(mRegistrationId);
+    parcel->writeInt32(mCbFlags);
     size_t size = mCriteria.size();
     if (size > MAX_CRITERIA_PER_MIX) {
         size = MAX_CRITERIA_PER_MIX;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 100a914..3868f13 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -65,9 +65,10 @@
 
 // ---------------------------------------------------------------------------
 
-AudioRecord::AudioRecord()
-    : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE),
-      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
+AudioRecord::AudioRecord(const String16 &opPackageName)
+    : mStatus(NO_INIT), mOpPackageName(opPackageName), mSessionId(AUDIO_SESSION_ALLOCATE),
+      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
+      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
 {
 }
 
@@ -76,6 +77,7 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
+        const String16& opPackageName,
         size_t frameCount,
         callback_t cbf,
         void* user,
@@ -83,15 +85,20 @@
         int sessionId,
         transfer_type transferType,
         audio_input_flags_t flags,
+        int uid,
+        pid_t pid,
         const audio_attributes_t* pAttributes)
-    : mStatus(NO_INIT), mSessionId(AUDIO_SESSION_ALLOCATE),
+    : mStatus(NO_INIT),
+      mOpPackageName(opPackageName),
+      mSessionId(AUDIO_SESSION_ALLOCATE),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
-      mProxy(NULL)
+      mProxy(NULL),
+      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
 {
     mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
             notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
-            pAttributes);
+            uid, pid, pAttributes);
 }
 
 AudioRecord::~AudioRecord()
@@ -107,6 +114,10 @@
             mAudioRecordThread->requestExitAndWait();
             mAudioRecordThread.clear();
         }
+        // No lock here: worst case we remove a NULL callback which will be a nop
+        if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
+            AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+        }
         IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
         mAudioRecord.clear();
         mCblkMemory.clear();
@@ -131,12 +142,15 @@
         int sessionId,
         transfer_type transferType,
         audio_input_flags_t flags,
+        int uid,
+        pid_t pid,
         const audio_attributes_t* pAttributes)
 {
     ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
-          "notificationFrames %u, sessionId %d, transferType %d, flags %#x",
+          "notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s "
+          "uid %d, pid %d",
           inputSource, sampleRate, format, channelMask, frameCount, notificationFrames,
-          sessionId, transferType, flags);
+          sessionId, transferType, flags, String8(mOpPackageName).string(), uid, pid);
 
     switch (transferType) {
     case TRANSFER_DEFAULT:
@@ -189,13 +203,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;
@@ -227,6 +237,19 @@
     }
     ALOGV("set(): mSessionId %d", mSessionId);
 
+    int callingpid = IPCThreadState::self()->getCallingPid();
+    int mypid = getpid();
+    if (uid == -1 || (callingpid != mypid)) {
+        mClientUid = IPCThreadState::self()->getCallingUid();
+    } else {
+        mClientUid = uid;
+    }
+    if (pid == -1 || (callingpid != mypid)) {
+        mClientPid = callingpid;
+    } else {
+        mClientPid = pid;
+    }
+
     mFlags = flags;
     mCbf = cbf;
 
@@ -237,7 +260,7 @@
     }
 
     // create the IAudioRecord
-    status_t status = openRecord_l(0 /*epoch*/);
+    status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
 
     if (status != NO_ERROR) {
         if (mAudioRecordThread != 0) {
@@ -285,6 +308,8 @@
     mNewPosition = mProxy->getPosition() + mUpdatePeriod;
     int32_t flags = android_atomic_acquire_load(&mCblk->mFlags);
 
+    mActive = true;
+
     status_t status = NO_ERROR;
     if (!(flags & CBLK_INVALID)) {
         status = mAudioRecord->start(event, triggerSession);
@@ -297,9 +322,9 @@
     }
 
     if (status != NO_ERROR) {
+        mActive = false;
         ALOGE("start() status %d", status);
     } else {
-        mActive = true;
         sp<AudioRecordThread> t = mAudioRecordThread;
         if (t != 0) {
             t->resume();
@@ -419,10 +444,38 @@
     return AudioSystem::getInputFramesLost(getInputPrivate());
 }
 
+// ---- Explicit Routing ---------------------------------------------------
+status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) {
+    AutoMutex lock(mLock);
+    if (mSelectedDeviceId != deviceId) {
+        mSelectedDeviceId = deviceId;
+        // stop capture so that audio policy manager does not reject the new instance start request
+        // as only one capture can be active at a time.
+        if (mAudioRecord != 0 && mActive) {
+            mAudioRecord->stop();
+        }
+        android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+    }
+    return NO_ERROR;
+}
+
+audio_port_handle_t AudioRecord::getInputDevice() {
+    AutoMutex lock(mLock);
+    return mSelectedDeviceId;
+}
+
+audio_port_handle_t AudioRecord::getRoutedDeviceId() {
+    AutoMutex lock(mLock);
+    if (mInput == AUDIO_IO_HANDLE_NONE) {
+        return AUDIO_PORT_HANDLE_NONE;
+    }
+    return AudioSystem::getDeviceIdForIo(mInput);
+}
+
 // -------------------------------------------------------------------------
 
 // must be called with mLock held
-status_t AudioRecord::openRecord_l(size_t epoch)
+status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName)
 {
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
     if (audioFlinger == 0) {
@@ -462,10 +515,16 @@
         }
     }
 
+    if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
+        AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+    }
+
     audio_io_handle_t input;
     status_t status = AudioSystem::getInputForAttr(&mAttributes, &input,
                                         (audio_session_t)mSessionId,
-                                        mSampleRate, mFormat, mChannelMask, mFlags);
+                                        IPCThreadState::self()->getCallingUid(),
+                                        mSampleRate, mFormat, mChannelMask,
+                                        mFlags, mSelectedDeviceId);
 
     if (status != NO_ERROR) {
         ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, "
@@ -488,11 +547,14 @@
     sp<IMemory> iMem;           // for cblk
     sp<IMemory> bufferMem;
     sp<IAudioRecord> record = audioFlinger->openRecord(input,
-                                                       mSampleRate, mFormat,
+                                                       mSampleRate,
+                                                       mFormat,
                                                        mChannelMask,
+                                                       opPackageName,
                                                        &temp,
                                                        &trackFlags,
                                                        tid,
+                                                       mClientUid,
                                                        &mSessionId,
                                                        &notificationFrames,
                                                        iMem,
@@ -589,6 +651,10 @@
     mDeathNotifier = new DeathNotifier(this);
     IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
 
+    if (mDeviceCallback != 0) {
+        AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput);
+    }
+
     return NO_ERROR;
     }
 
@@ -600,15 +666,21 @@
     return status;
 }
 
-status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
+status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
 {
     if (audioBuffer == NULL) {
+        if (nonContig != NULL) {
+            *nonContig = 0;
+        }
         return BAD_VALUE;
     }
     if (mTransfer != TRANSFER_OBTAIN) {
         audioBuffer->frameCount = 0;
         audioBuffer->size = 0;
         audioBuffer->raw = NULL;
+        if (nonContig != NULL) {
+            *nonContig = 0;
+        }
         return INVALID_OPERATION;
     }
 
@@ -627,7 +699,7 @@
         ALOGE("%s invalid waitCount %d", __func__, waitCount);
         requested = NULL;
     }
-    return obtainBuffer(audioBuffer, requested);
+    return obtainBuffer(audioBuffer, requested, NULL /*elapsed*/, nonContig);
 }
 
 status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
@@ -1012,7 +1084,7 @@
     // It will also delete the strong references on previous IAudioRecord and IMemory
     size_t position = mProxy->getPosition();
     mNewPosition = position + mUpdatePeriod;
-    status_t result = openRecord_l(position);
+    status_t result = openRecord_l(position, mOpPackageName);
     if (result == NO_ERROR) {
         if (mActive) {
             // callback thread or sync event hasn't changed
@@ -1028,6 +1100,48 @@
     return result;
 }
 
+status_t AudioRecord::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+    if (callback == 0) {
+        ALOGW("%s adding NULL callback!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    AutoMutex lock(mLock);
+    if (mDeviceCallback == callback) {
+        ALOGW("%s adding same callback!", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    status_t status = NO_ERROR;
+    if (mInput != AUDIO_IO_HANDLE_NONE) {
+        if (mDeviceCallback != 0) {
+            ALOGW("%s callback already present!", __FUNCTION__);
+            AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+        }
+        status = AudioSystem::addAudioDeviceCallback(callback, mInput);
+    }
+    mDeviceCallback = callback;
+    return status;
+}
+
+status_t AudioRecord::removeAudioDeviceCallback(
+        const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+    if (callback == 0) {
+        ALOGW("%s removing NULL callback!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    AutoMutex lock(mLock);
+    if (mDeviceCallback != callback) {
+        ALOGW("%s removing different callback!", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    if (mInput != AUDIO_IO_HANDLE_NONE) {
+        AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
+    }
+    mDeviceCallback = 0;
+    return NO_ERROR;
+}
+
 // =========================================================================
 
 void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 9150a94..4c2e77b 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -32,20 +32,12 @@
 
 // client singleton for AudioFlinger binder interface
 Mutex AudioSystem::gLock;
-Mutex AudioSystem::gLockCache;
 Mutex AudioSystem::gLockAPS;
 sp<IAudioFlinger> AudioSystem::gAudioFlinger;
 sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
 audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
+dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL;
 
-// Cached values for output handles
-DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(NULL);
-
-// Cached values for recording queries, all protected by gLock
-uint32_t AudioSystem::gPrevInSamplingRate;
-audio_format_t AudioSystem::gPrevInFormat;
-audio_channel_mask_t AudioSystem::gPrevInChannelMask;
-size_t AudioSystem::gInBuffSize = 0;    // zero indicates cache is invalid
 
 // establish binder interface to AudioFlinger service
 const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
@@ -84,6 +76,25 @@
     return af;
 }
 
+const sp<AudioSystem::AudioFlingerClient> AudioSystem::getAudioFlingerClient()
+{
+    // calling get_audio_flinger() will initialize gAudioFlingerClient if needed
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) return 0;
+    Mutex::Autolock _l(gLock);
+    return gAudioFlingerClient;
+}
+
+sp<AudioIoDescriptor> AudioSystem::getIoDescriptor(audio_io_handle_t ioHandle)
+{
+    sp<AudioIoDescriptor> desc;
+    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+    if (afc != 0) {
+        desc = afc->getIoDescriptor(ioHandle);
+    }
+    return desc;
+}
+
 /* static */ status_t AudioSystem::checkAudioFlinger()
 {
     if (defaultServiceManager()->checkService(String16("media.audio_flinger")) != 0) {
@@ -257,18 +268,13 @@
 {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
-
-    Mutex::Autolock _l(gLockCache);
-
-    OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == NULL) {
+    sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
+    if (outputDesc == 0) {
         ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
-        gLockCache.unlock();
         *samplingRate = af->sampleRate(output);
-        gLockCache.lock();
     } else {
         ALOGV("getOutputSamplingRate() reading from output desc");
-        *samplingRate = outputDesc->samplingRate;
+        *samplingRate = outputDesc->mSamplingRate;
     }
     if (*samplingRate == 0) {
         ALOGE("AudioSystem::getSamplingRate failed for output %d", output);
@@ -301,16 +307,11 @@
 {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
-
-    Mutex::Autolock _l(gLockCache);
-
-    OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == NULL) {
-        gLockCache.unlock();
+    sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
+    if (outputDesc == 0) {
         *frameCount = af->frameCount(output);
-        gLockCache.lock();
     } else {
-        *frameCount = outputDesc->frameCount;
+        *frameCount = outputDesc->mFrameCount;
     }
     if (*frameCount == 0) {
         ALOGE("AudioSystem::getFrameCount failed for output %d", output);
@@ -343,16 +344,11 @@
 {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
-
-    Mutex::Autolock _l(gLockCache);
-
-    OutputDescriptor *outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == NULL) {
-        gLockCache.unlock();
+    sp<AudioIoDescriptor> outputDesc = getIoDescriptor(output);
+    if (outputDesc == 0) {
         *latency = af->latency(output);
-        gLockCache.lock();
     } else {
-        *latency = outputDesc->latency;
+        *latency = outputDesc->mLatency;
     }
 
     ALOGV("getLatency() output %d, latency %d", output, *latency);
@@ -363,34 +359,11 @@
 status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
         audio_channel_mask_t channelMask, size_t* buffSize)
 {
-    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
-    if (af == 0) {
-        return PERMISSION_DENIED;
+    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+    if (afc == 0) {
+        return NO_INIT;
     }
-    Mutex::Autolock _l(gLockCache);
-    // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
-    size_t inBuffSize = gInBuffSize;
-    if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
-        || (channelMask != gPrevInChannelMask)) {
-        gLockCache.unlock();
-        inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
-        gLockCache.lock();
-        if (inBuffSize == 0) {
-            ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
-                    sampleRate, format, channelMask);
-            return BAD_VALUE;
-        }
-        // A benign race is possible here: we could overwrite a fresher cache entry
-        // save the request params
-        gPrevInSamplingRate = sampleRate;
-        gPrevInFormat = format;
-        gPrevInChannelMask = channelMask;
-
-        gInBuffSize = inBuffSize;
-    }
-    *buffSize = inBuffSize;
-
-    return NO_ERROR;
+    return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize);
 }
 
 status_t AudioSystem::setVoiceVolume(float value)
@@ -452,6 +425,17 @@
 
 // ---------------------------------------------------------------------------
 
+
+void AudioSystem::AudioFlingerClient::clearIoCache()
+{
+    Mutex::Autolock _l(mLock);
+    mIoDescriptors.clear();
+    mInBuffSize = 0;
+    mInSamplingRate = 0;
+    mInFormat = AUDIO_FORMAT_DEFAULT;
+    mInChannelMask = AUDIO_CHANNEL_NONE;
+}
+
 void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who __unused)
 {
     audio_error_callback cb = NULL;
@@ -461,11 +445,8 @@
         cb = gAudioErrorCallback;
     }
 
-    {
-        // clear output handles and stream to output map caches
-        Mutex::Autolock _l(gLockCache);
-        AudioSystem::gOutputs.clear();
-    }
+    // clear output handles and stream to output map caches
+    clearIoCache();
 
     if (cb) {
         cb(DEAD_OBJECT);
@@ -473,76 +454,189 @@
     ALOGW("AudioFlinger server died!");
 }
 
-void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle_t ioHandle,
-        const void *param2) {
+void AudioSystem::AudioFlingerClient::ioConfigChanged(audio_io_config_event event,
+                                                      const sp<AudioIoDescriptor>& ioDesc) {
     ALOGV("ioConfigChanged() event %d", event);
-    const OutputDescriptor *desc;
-    audio_stream_type_t stream;
 
-    if (ioHandle == AUDIO_IO_HANDLE_NONE) return;
+    if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
 
-    Mutex::Autolock _l(AudioSystem::gLockCache);
+    audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+    Vector < sp<AudioDeviceCallback> > callbacks;
 
-    switch (event) {
-    case STREAM_CONFIG_CHANGED:
-        break;
-    case OUTPUT_OPENED: {
-        if (gOutputs.indexOfKey(ioHandle) >= 0) {
-            ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
-            break;
-        }
-        if (param2 == NULL) break;
-        desc = (const OutputDescriptor *)param2;
+    {
+        Mutex::Autolock _l(mLock);
 
-        OutputDescriptor *outputDesc =  new OutputDescriptor(*desc);
-        gOutputs.add(ioHandle, outputDesc);
-        ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x "
-                "frameCount %zu latency %d",
-                outputDesc->samplingRate, outputDesc->format, outputDesc->channelMask,
-                outputDesc->frameCount, outputDesc->latency);
+        switch (event) {
+        case AUDIO_OUTPUT_OPENED:
+        case AUDIO_INPUT_OPENED: {
+            if (getIoDescriptor(ioDesc->mIoHandle) != 0) {
+                ALOGV("ioConfigChanged() opening already existing output! %d", ioDesc->mIoHandle);
+                break;
+            }
+            mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
+
+            if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+                deviceId = ioDesc->getDeviceId();
+                ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+                if (ioIndex >= 0) {
+                    callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+                }
+            }
+            ALOGV("ioConfigChanged() new %s opened %d samplingRate %u, format %#x channel mask %#x "
+                    "frameCount %zu deviceId %d", event == AUDIO_OUTPUT_OPENED ? "output" : "input",
+                    ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, ioDesc->mChannelMask,
+                    ioDesc->mFrameCount, ioDesc->getDeviceId());
+            } break;
+        case AUDIO_OUTPUT_CLOSED:
+        case AUDIO_INPUT_CLOSED: {
+            if (getIoDescriptor(ioDesc->mIoHandle) == 0) {
+                ALOGW("ioConfigChanged() closing unknown %s %d",
+                      event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
+                break;
+            }
+            ALOGV("ioConfigChanged() %s %d closed",
+                  event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
+
+            mIoDescriptors.removeItem(ioDesc->mIoHandle);
+            mAudioDeviceCallbacks.removeItem(ioDesc->mIoHandle);
+            } break;
+
+        case AUDIO_OUTPUT_CONFIG_CHANGED:
+        case AUDIO_INPUT_CONFIG_CHANGED: {
+            sp<AudioIoDescriptor> oldDesc = getIoDescriptor(ioDesc->mIoHandle);
+            if (oldDesc == 0) {
+                ALOGW("ioConfigChanged() modifying unknown output! %d", ioDesc->mIoHandle);
+                break;
+            }
+
+            deviceId = oldDesc->getDeviceId();
+            mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
+
+            if (deviceId != ioDesc->getDeviceId()) {
+                deviceId = ioDesc->getDeviceId();
+                ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+                if (ioIndex >= 0) {
+                    callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+                }
+            }
+            ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
+                    "channel mask %#x frameCount %zu deviceId %d",
+                    event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input",
+                    ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat,
+                    ioDesc->mChannelMask, ioDesc->mFrameCount, ioDesc->getDeviceId());
+
         } break;
-    case OUTPUT_CLOSED: {
-        if (gOutputs.indexOfKey(ioHandle) < 0) {
-            ALOGW("ioConfigChanged() closing unknown output! %d", ioHandle);
-            break;
         }
-        ALOGV("ioConfigChanged() output %d closed", ioHandle);
-
-        gOutputs.removeItem(ioHandle);
-        } break;
-
-    case OUTPUT_CONFIG_CHANGED: {
-        int index = gOutputs.indexOfKey(ioHandle);
-        if (index < 0) {
-            ALOGW("ioConfigChanged() modifying unknown output! %d", ioHandle);
-            break;
-        }
-        if (param2 == NULL) break;
-        desc = (const OutputDescriptor *)param2;
-
-        ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x "
-                "channel mask %#x frameCount %zu latency %d",
-                ioHandle, desc->samplingRate, desc->format,
-                desc->channelMask, desc->frameCount, desc->latency);
-        OutputDescriptor *outputDesc = gOutputs.valueAt(index);
-        delete outputDesc;
-        outputDesc =  new OutputDescriptor(*desc);
-        gOutputs.replaceValueFor(ioHandle, outputDesc);
-    } break;
-    case INPUT_OPENED:
-    case INPUT_CLOSED:
-    case INPUT_CONFIG_CHANGED:
-        break;
-
+    }
+    // callbacks.size() != 0 =>  ioDesc->mIoHandle and deviceId are valid
+    for (size_t i = 0; i < callbacks.size(); i++) {
+        callbacks[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
     }
 }
 
-void AudioSystem::setErrorCallback(audio_error_callback cb)
+status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
+                                                uint32_t sampleRate, audio_format_t format,
+                                                audio_channel_mask_t channelMask, size_t* buffSize)
+{
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) {
+        return PERMISSION_DENIED;
+    }
+    Mutex::Autolock _l(mLock);
+    // Do we have a stale mInBuffSize or are we requesting the input buffer size for new values
+    if ((mInBuffSize == 0) || (sampleRate != mInSamplingRate) || (format != mInFormat)
+        || (channelMask != mInChannelMask)) {
+        size_t inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
+        if (inBuffSize == 0) {
+            ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
+                    sampleRate, format, channelMask);
+            return BAD_VALUE;
+        }
+        // A benign race is possible here: we could overwrite a fresher cache entry
+        // save the request params
+        mInSamplingRate = sampleRate;
+        mInFormat = format;
+        mInChannelMask = channelMask;
+
+        mInBuffSize = inBuffSize;
+    }
+
+    *buffSize = mInBuffSize;
+
+    return NO_ERROR;
+}
+
+sp<AudioIoDescriptor> AudioSystem::AudioFlingerClient::getIoDescriptor(audio_io_handle_t ioHandle)
+{
+    sp<AudioIoDescriptor> desc;
+    ssize_t index = mIoDescriptors.indexOfKey(ioHandle);
+    if (index >= 0) {
+        desc = mIoDescriptors.valueAt(index);
+    }
+    return desc;
+}
+
+status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
+        const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+    Mutex::Autolock _l(mLock);
+    Vector < sp<AudioDeviceCallback> > callbacks;
+    ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
+    if (ioIndex >= 0) {
+        callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+    }
+
+    for (size_t cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
+        if (callbacks[cbIndex] == callback) {
+            return INVALID_OPERATION;
+        }
+    }
+    callbacks.add(callback);
+
+    mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
+    return NO_ERROR;
+}
+
+status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
+        const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+    Mutex::Autolock _l(mLock);
+    ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
+    if (ioIndex < 0) {
+        return INVALID_OPERATION;
+    }
+    Vector < sp<AudioDeviceCallback> > callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+
+    size_t cbIndex;
+    for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
+        if (callbacks[cbIndex] == callback) {
+            break;
+        }
+    }
+    if (cbIndex == callbacks.size()) {
+        return INVALID_OPERATION;
+    }
+    callbacks.removeAt(cbIndex);
+    if (callbacks.size() != 0) {
+        mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
+    } else {
+        mAudioDeviceCallbacks.removeItem(audioIo);
+    }
+    return NO_ERROR;
+}
+
+/* static */ void AudioSystem::setErrorCallback(audio_error_callback cb)
 {
     Mutex::Autolock _l(gLock);
     gAudioErrorCallback = cb;
 }
 
+/*static*/ void AudioSystem::setDynPolicyCallback(dynamic_policy_callback cb)
+{
+    Mutex::Autolock _l(gLock);
+    gDynPolicyCallback = cb;
+}
+
 // client singleton for AudioPolicyService binder interface
 // protected by gLockAPS
 sp<IAudioPolicyService> AudioSystem::gAudioPolicyService;
@@ -654,17 +748,19 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
+                                        uid_t uid,
                                         uint32_t samplingRate,
                                         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,
+    return aps->getOutputForAttr(attr, output, session, stream, uid,
                                  samplingRate, format, channelMask,
-                                 flags, offloadInfo);
+                                 flags, selectedDeviceId, offloadInfo);
 }
 
 status_t AudioSystem::startOutput(audio_io_handle_t output,
@@ -697,14 +793,17 @@
 status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr,
                                 audio_io_handle_t *input,
                                 audio_session_t session,
+                                uid_t uid,
                                 uint32_t samplingRate,
                                 audio_format_t format,
                                 audio_channel_mask_t channelMask,
-                                audio_input_flags_t flags)
+                                audio_input_flags_t flags,
+                                audio_port_handle_t selectedDeviceId)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return NO_INIT;
-    return aps->getInputForAttr(attr, input, session, samplingRate, format, channelMask, flags);
+    return aps->getInputForAttr(
+            attr, input, session, uid, samplingRate, format, channelMask, flags, selectedDeviceId);
 }
 
 status_t AudioSystem::startInput(audio_io_handle_t input,
@@ -859,11 +958,10 @@
     // called by restoreTrack_l(), which needs new IAudioFlinger and IAudioPolicyService instances
     ALOGV("clearAudioConfigCache()");
     {
-        Mutex::Autolock _l(gLockCache);
-        gOutputs.clear();
-    }
-    {
         Mutex::Autolock _l(gLock);
+        if (gAudioFlingerClient != 0) {
+            gAudioFlingerClient->clearIoCache();
+        }
         gAudioFlinger.clear();
     }
     {
@@ -929,7 +1027,7 @@
     return aps->setAudioPortConfig(config);
 }
 
-status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callBack)
+status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callback)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
@@ -938,10 +1036,11 @@
     if (gAudioPolicyServiceClient == 0) {
         return NO_INIT;
     }
-    return gAudioPolicyServiceClient->addAudioPortCallback(callBack);
+    return gAudioPolicyServiceClient->addAudioPortCallback(callback);
 }
 
-status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callBack)
+/*static*/
+status_t AudioSystem::removeAudioPortCallback(const sp<AudioPortCallback>& callback)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
@@ -950,9 +1049,39 @@
     if (gAudioPolicyServiceClient == 0) {
         return NO_INIT;
     }
-    return gAudioPolicyServiceClient->removeAudioPortCallback(callBack);
+    return gAudioPolicyServiceClient->removeAudioPortCallback(callback);
 }
 
+status_t AudioSystem::addAudioDeviceCallback(
+        const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+    if (afc == 0) {
+        return NO_INIT;
+    }
+    return afc->addAudioDeviceCallback(callback, audioIo);
+}
+
+status_t AudioSystem::removeAudioDeviceCallback(
+        const sp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
+{
+    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+    if (afc == 0) {
+        return NO_INIT;
+    }
+    return afc->removeAudioDeviceCallback(callback, audioIo);
+}
+
+audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo)
+{
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) return PERMISSION_DENIED;
+    const sp<AudioIoDescriptor> desc = getIoDescriptor(audioIo);
+    if (desc == 0) {
+        return AUDIO_PORT_HANDLE_NONE;
+    }
+    return desc->getDeviceId();
+}
 
 status_t AudioSystem::acquireSoundTriggerSession(audio_session_t *session,
                                        audio_io_handle_t *ioHandle,
@@ -984,28 +1113,44 @@
     return aps->registerPolicyMixes(mixes, registration);
 }
 
+status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
+                                       const audio_attributes_t *attributes,
+                                       audio_io_handle_t *handle)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->startAudioSource(source, attributes, handle);
+}
+
+status_t AudioSystem::stopAudioSource(audio_io_handle_t handle)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->stopAudioSource(handle);
+}
+
 // ---------------------------------------------------------------------------
 
 status_t AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
-        const sp<AudioPortCallback>& callBack)
+        const sp<AudioPortCallback>& callback)
 {
     Mutex::Autolock _l(mLock);
     for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
-        if (mAudioPortCallbacks[i] == callBack) {
+        if (mAudioPortCallbacks[i] == callback) {
             return INVALID_OPERATION;
         }
     }
-    mAudioPortCallbacks.add(callBack);
+    mAudioPortCallbacks.add(callback);
     return NO_ERROR;
 }
 
 status_t AudioSystem::AudioPolicyServiceClient::removeAudioPortCallback(
-        const sp<AudioPortCallback>& callBack)
+        const sp<AudioPortCallback>& callback)
 {
     Mutex::Autolock _l(mLock);
     size_t i;
     for (i = 0; i < mAudioPortCallbacks.size(); i++) {
-        if (mAudioPortCallbacks[i] == callBack) {
+        if (mAudioPortCallbacks[i] == callback) {
             break;
         }
     }
@@ -1016,6 +1161,7 @@
     return NO_ERROR;
 }
 
+
 void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
 {
     Mutex::Autolock _l(mLock);
@@ -1032,6 +1178,21 @@
     }
 }
 
+void AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(
+        String8 regId, int32_t state)
+{
+    ALOGV("AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate(%s, %d)", regId.string(), state);
+    dynamic_policy_callback cb = NULL;
+    {
+        Mutex::Autolock _l(AudioSystem::gLock);
+        cb = gDynPolicyCallback;
+    }
+
+    if (cb != NULL) {
+        cb(DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE, regId, state);
+    }
+}
+
 void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
 {
     {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index ce30c62..db316b0 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -56,6 +56,42 @@
     return convertTimespecToUs(tv);
 }
 
+// FIXME: we don't use the pitch setting in the time stretcher (not working);
+// instead we emulate it using our sample rate converter.
+static const bool kFixPitch = true; // enable pitch fix
+static inline uint32_t adjustSampleRate(uint32_t sampleRate, float pitch)
+{
+    return kFixPitch ? (sampleRate * pitch + 0.5) : sampleRate;
+}
+
+static inline float adjustSpeed(float speed, float pitch)
+{
+    return kFixPitch ? (speed / pitch) : speed;
+}
+
+static inline float adjustPitch(float pitch)
+{
+    return kFixPitch ? AUDIO_TIMESTRETCH_PITCH_NORMAL : pitch;
+}
+
+// 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 +130,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 +142,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 +154,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 +183,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 +212,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,
@@ -198,6 +234,10 @@
             mAudioTrackThread->requestExitAndWait();
             mAudioTrackThread.clear();
         }
+        // No lock here: worst case we remove a NULL callback which will be a nop
+        if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
+            AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+        }
         IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
         mAudioTrack.clear();
         mCblkMemory.clear();
@@ -357,6 +397,8 @@
         return BAD_VALUE;
     }
     mSampleRate = sampleRate;
+    mOriginalSampleRate = sampleRate;
+    mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
 
     // Make copy of input parameter offloadInfo so that in the future:
     //  (a) createTrack_l doesn't need it as an input parameter
@@ -433,6 +475,7 @@
     mSequence = 1;
     mObservedSequence = mSequence;
     mInUnderrun = false;
+    mPreviousTimestampValid = false;
 
     return NO_ERROR;
 }
@@ -459,6 +502,8 @@
     if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
         // reset current position as seen by client to 0
         mPosition = 0;
+        mPreviousTimestampValid = false;
+
         // For offloaded tracks, we don't know if the hardware counters are really zero here,
         // since the flush is asynchronous and stop may not fully drain.
         // We save the time when the track is started to later verify whether
@@ -683,12 +728,15 @@
     if (AudioSystem::getSamplingRate(mOutput, &afSamplingRate) != NO_ERROR) {
         return NO_INIT;
     }
-    if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
+    // pitch is emulated by adjusting speed and sampleRate
+    const uint32_t effectiveSampleRate = adjustSampleRate(rate, mPlaybackRate.mPitch);
+    if (rate == 0 || effectiveSampleRate > 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);
+    mProxy->setSampleRate(effectiveSampleRate);
 
     return NO_ERROR;
 }
@@ -716,6 +764,61 @@
     return mSampleRate;
 }
 
+uint32_t AudioTrack::getOriginalSampleRate() const
+{
+    if (mIsTimed) {
+        return 0;
+    }
+
+    return mOriginalSampleRate;
+}
+
+status_t AudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate)
+{
+    AutoMutex lock(mLock);
+    if (isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
+        return NO_ERROR;
+    }
+    if (mIsTimed || isOffloadedOrDirect_l()) {
+        return INVALID_OPERATION;
+    }
+    if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+        return INVALID_OPERATION;
+    }
+    // pitch is emulated by adjusting speed and sampleRate
+    const uint32_t effectiveRate = adjustSampleRate(mSampleRate, playbackRate.mPitch);
+    const float effectiveSpeed = adjustSpeed(playbackRate.mSpeed, playbackRate.mPitch);
+    const float effectivePitch = adjustPitch(playbackRate.mPitch);
+    if (effectiveSpeed < AUDIO_TIMESTRETCH_SPEED_MIN
+            || effectiveSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
+            || effectivePitch < AUDIO_TIMESTRETCH_PITCH_MIN
+            || effectivePitch > AUDIO_TIMESTRETCH_PITCH_MAX) {
+        return BAD_VALUE;
+        //TODO: add function in AudioResamplerPublic.h to check for validity.
+    }
+    // Check if the buffer size is compatible.
+    if (!isSampleRateSpeedAllowed_l(effectiveRate, effectiveSpeed)) {
+        ALOGV("setPlaybackRate(%f, %f) failed", playbackRate.mSpeed, playbackRate.mPitch);
+        return BAD_VALUE;
+    }
+    mPlaybackRate = playbackRate;
+    mProxy->setPlaybackRate(playbackRate);
+
+    //modify this
+    AudioPlaybackRate playbackRateTemp = playbackRate;
+    playbackRateTemp.mSpeed = effectiveSpeed;
+    playbackRateTemp.mPitch = effectivePitch;
+    mProxy->setPlaybackRate(playbackRateTemp);
+    mProxy->setSampleRate(effectiveRate); // FIXME: not quite "atomic" with setPlaybackRate
+    return NO_ERROR;
+}
+
+const AudioPlaybackRate& AudioTrack::getPlaybackRate() const
+{
+    AutoMutex lock(mLock);
+    return mPlaybackRate;
+}
+
 status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
 {
     if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -909,6 +1012,7 @@
     mNewPosition = mUpdatePeriod;
     (void) updateAndGetPosition_l();
     mPosition = 0;
+    mPreviousTimestampValid = false;
 #if 0
     // The documentation is not clear on the behavior of reload() and the restoration
     // of loop count. Historically we have not restored loop count, start, end,
@@ -928,6 +1032,28 @@
     return mOutput;
 }
 
+status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+    AutoMutex lock(mLock);
+    if (mSelectedDeviceId != deviceId) {
+        mSelectedDeviceId = deviceId;
+        android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+    }
+    return NO_ERROR;
+}
+
+audio_port_handle_t AudioTrack::getOutputDevice() {
+    AutoMutex lock(mLock);
+    return mSelectedDeviceId;
+}
+
+audio_port_handle_t AudioTrack::getRoutedDeviceId() {
+    AutoMutex lock(mLock);
+    if (mOutput == AUDIO_IO_HANDLE_NONE) {
+        return AUDIO_PORT_HANDLE_NONE;
+    }
+    return AudioSystem::getDeviceIdForIo(mOutput);
+}
+
 status_t AudioTrack::attachAuxEffect(int effectId)
 {
     AutoMutex lock(mLock);
@@ -957,14 +1083,18 @@
         return NO_INIT;
     }
 
+    if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
+        AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+    }
     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, mClientUid,
+                                           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,"
@@ -1001,6 +1131,7 @@
     }
     if (mSampleRate == 0) {
         mSampleRate = afSampleRate;
+        mOriginalSampleRate = afSampleRate;
     }
     // Client decides whether the track is TIMED (see below), but can only express a preference
     // for FAST.  Server will perform additional tests.
@@ -1067,8 +1198,17 @@
         // 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,
+                    mPlaybackRate.mSpeed);
+            if (frameCount < minFrameCount) {
+                frameCount = minFrameCount;
+            }
+        }
     }
 
     IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -1211,6 +1351,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;
 
@@ -1235,12 +1376,24 @@
             gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT])));
 
     mProxy->setSendLevel(mSendLevel);
-    mProxy->setSampleRate(mSampleRate);
+    const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPlaybackRate.mPitch);
+    const float effectiveSpeed = adjustSpeed(mPlaybackRate.mSpeed, mPlaybackRate.mPitch);
+    const float effectivePitch = adjustPitch(mPlaybackRate.mPitch);
+    mProxy->setSampleRate(effectiveSampleRate);
+
+    AudioPlaybackRate playbackRateTemp = mPlaybackRate;
+    playbackRateTemp.mSpeed = effectiveSpeed;
+    playbackRateTemp.mPitch = effectivePitch;
+    mProxy->setPlaybackRate(playbackRateTemp);
     mProxy->setMinimum(mNotificationFramesAct);
 
     mDeathNotifier = new DeathNotifier(this);
     IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this);
 
+    if (mDeviceCallback != 0) {
+        AudioSystem::addAudioDeviceCallback(mDeviceCallback, mOutput);
+    }
+
     return NO_ERROR;
     }
 
@@ -1255,12 +1408,18 @@
 status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig)
 {
     if (audioBuffer == NULL) {
+        if (nonContig != NULL) {
+            *nonContig = 0;
+        }
         return BAD_VALUE;
     }
     if (mTransfer != TRANSFER_OBTAIN) {
         audioBuffer->frameCount = 0;
         audioBuffer->size = 0;
         audioBuffer->raw = NULL;
+        if (nonContig != NULL) {
+            *nonContig = 0;
+        }
         return INVALID_OPERATION;
     }
 
@@ -1546,7 +1705,8 @@
         // AudioSystem cache. We should not exit here but after calling the callback so
         // that the upper layers can recreate the track
         if (!isOffloadedOrDirect_l() || (mSequence == mObservedSequence)) {
-            status_t status = restoreTrack_l("processAudioBuffer");
+            status_t status __unused = restoreTrack_l("processAudioBuffer");
+            // FIXME unused status
             // after restoration, continue below to make sure that the loop and buffer events
             // are notified because they have been cleared from mCblk->mFlags above.
         }
@@ -1598,6 +1758,7 @@
 
     // Cache other fields that will be needed soon
     uint32_t sampleRate = mSampleRate;
+    float speed = mPlaybackRate.mSpeed;
     uint32_t notificationFrames = mNotificationFramesAct;
     if (mRefreshRemaining) {
         mRefreshRemaining = false;
@@ -1726,7 +1887,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 +1928,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 +1984,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 +2079,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);
@@ -1926,6 +2123,11 @@
 status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
 {
     AutoMutex lock(mLock);
+
+    bool previousTimestampValid = mPreviousTimestampValid;
+    // Set false here to cover all the error return cases.
+    mPreviousTimestampValid = false;
+
     // FIXME not implemented for fast tracks; should use proxy and SSQ
     if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
         return INVALID_OPERATION;
@@ -1982,7 +2184,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 * mPlaybackRate.mSpeed);
 
                 if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
                     // Verify that the counter can't count faster than the sample rate
@@ -2023,6 +2226,46 @@
         // IAudioTrack.  And timestamp.mPosition is initially in server's
         // point of view, so we need to apply the same fudge factor to it.
     }
+
+    // Prevent retrograde motion in timestamp.
+    // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
+    if (status == NO_ERROR) {
+        if (previousTimestampValid) {
+#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec)
+            const uint64_t previousTimeNanos = TIME_TO_NANOS(mPreviousTimestamp.mTime);
+            const uint64_t currentTimeNanos = TIME_TO_NANOS(timestamp.mTime);
+#undef TIME_TO_NANOS
+            if (currentTimeNanos < previousTimeNanos) {
+                ALOGW("retrograde timestamp time");
+                // FIXME Consider blocking this from propagating upwards.
+            }
+
+            // Looking at signed delta will work even when the timestamps
+            // are wrapping around.
+            int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition
+                    - mPreviousTimestamp.mPosition);
+            // position can bobble slightly as an artifact; this hides the bobble
+            static const int32_t MINIMUM_POSITION_DELTA = 8;
+            if (deltaPosition < 0) {
+                // Only report once per position instead of spamming the log.
+                if (!mRetrogradeMotionReported) {
+                    ALOGW("retrograde timestamp position corrected, %d = %u - %u",
+                            deltaPosition,
+                            timestamp.mPosition,
+                            mPreviousTimestamp.mPosition);
+                    mRetrogradeMotionReported = true;
+                }
+            } else {
+                mRetrogradeMotionReported = false;
+            }
+            if (deltaPosition < MINIMUM_POSITION_DELTA) {
+                timestamp = mPreviousTimestamp;  // Use last valid timestamp.
+            }
+        }
+        mPreviousTimestamp = timestamp;
+        mPreviousTimestampValid = true;
+    }
+
     return status;
 }
 
@@ -2069,7 +2312,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, mPlaybackRate.mSpeed, mStatus);
     result.append(buffer);
     snprintf(buffer, 255, "  state(%d), latency (%d)\n", mState, mLatency);
     result.append(buffer);
@@ -2083,6 +2327,48 @@
     return mProxy->getUnderrunFrames();
 }
 
+status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+    if (callback == 0) {
+        ALOGW("%s adding NULL callback!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    AutoMutex lock(mLock);
+    if (mDeviceCallback == callback) {
+        ALOGW("%s adding same callback!", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    status_t status = NO_ERROR;
+    if (mOutput != AUDIO_IO_HANDLE_NONE) {
+        if (mDeviceCallback != 0) {
+            ALOGW("%s callback already present!", __FUNCTION__);
+            AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+        }
+        status = AudioSystem::addAudioDeviceCallback(callback, mOutput);
+    }
+    mDeviceCallback = callback;
+    return status;
+}
+
+status_t AudioTrack::removeAudioDeviceCallback(
+        const sp<AudioSystem::AudioDeviceCallback>& callback)
+{
+    if (callback == 0) {
+        ALOGW("%s removing NULL callback!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    AutoMutex lock(mLock);
+    if (mDeviceCallback != callback) {
+        ALOGW("%s removing different callback!", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    if (mOutput != AUDIO_IO_HANDLE_NONE) {
+        AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mOutput);
+    }
+    mDeviceCallback = 0;
+    return NO_ERROR;
+}
+
 // =========================================================================
 
 void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 6d5f1af..1d7aed2 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -619,8 +619,9 @@
             // Rather than shutting down on a corrupt flush, just treat it as a full flush
             if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
                 ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, "
-                        "filled %d=%#x",
-                        mFlush, flush, front, rear, mask, newFront, filled, filled);
+                        "filled %zd=%#x",
+                        mFlush, flush, front, rear,
+                        (unsigned)mask, newFront, filled, (unsigned)filled);
                 newFront = rear;
             }
             mFlush = flush;
@@ -793,6 +794,12 @@
     (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
 }
 
+AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate()
+{   // do not call from multiple threads without holding lock
+    mPlaybackRateObserver.poll(mPlaybackRate);
+    return mPlaybackRate;
+}
+
 // ---------------------------------------------------------------------------
 
 StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp
index 41994dc..3020136 100644
--- a/media/libmedia/CharacterEncodingDetector.cpp
+++ b/media/libmedia/CharacterEncodingDetector.cpp
@@ -89,7 +89,6 @@
         // try combined detection of artist/album/title etc.
         char buf[1024];
         buf[0] = 0;
-        int idx;
         bool allprintable = true;
         for (int i = 0; i < size; i++) {
             const char *name = mNames.getEntry(i);
@@ -169,7 +168,6 @@
             const char *name = mNames.getEntry(i);
             uint8_t* src = (uint8_t *)mValues.getEntry(i);
             int len = strlen((char *)src);
-            uint8_t* dest = src;
 
             ALOGV("@@@ checking %s", name);
             const char *s = mValues.getEntry(i);
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 38055f9..d722fe9 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -174,9 +174,11 @@
                                 uint32_t sampleRate,
                                 audio_format_t format,
                                 audio_channel_mask_t channelMask,
+                                const String16& opPackageName,
                                 size_t *pFrameCount,
                                 track_flags_t *flags,
                                 pid_t tid,
+                                int clientUid,
                                 int *sessionId,
                                 size_t *notificationFrames,
                                 sp<IMemory>& cblk,
@@ -190,11 +192,13 @@
         data.writeInt32(sampleRate);
         data.writeInt32(format);
         data.writeInt32(channelMask);
+        data.writeString16(opPackageName);
         size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
         data.writeInt64(frameCount);
         track_flags_t lFlags = flags != NULL ? *flags : (track_flags_t) TRACK_DEFAULT;
         data.writeInt32(lFlags);
         data.writeInt32((int32_t) tid);
+        data.writeInt32((int32_t) clientUid);
         int lSessionId = AUDIO_SESSION_ALLOCATE;
         if (sessionId != NULL) {
             lSessionId = *sessionId;
@@ -702,6 +706,7 @@
                                     int32_t priority,
                                     audio_io_handle_t output,
                                     int sessionId,
+                                    const String16& opPackageName,
                                     status_t *status,
                                     int *id,
                                     int *enabled)
@@ -722,6 +727,7 @@
         data.writeInt32(priority);
         data.writeInt32((int32_t) output);
         data.writeInt32(sessionId);
+        data.writeString16(opPackageName);
 
         status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
         if (lStatus != NO_ERROR) {
@@ -950,18 +956,19 @@
             uint32_t sampleRate = data.readInt32();
             audio_format_t format = (audio_format_t) data.readInt32();
             audio_channel_mask_t channelMask = data.readInt32();
+            const String16& opPackageName = data.readString16();
             size_t frameCount = data.readInt64();
             track_flags_t flags = (track_flags_t) data.readInt32();
             pid_t tid = (pid_t) data.readInt32();
+            int clientUid = data.readInt32();
             int sessionId = data.readInt32();
             size_t notificationFrames = data.readInt64();
             sp<IMemory> cblk;
             sp<IMemory> buffers;
             status_t status;
             sp<IAudioRecord> record = openRecord(input,
-                    sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId,
-                    &notificationFrames,
-                    cblk, buffers, &status);
+                    sampleRate, format, channelMask, opPackageName, &frameCount, &flags, tid,
+                    clientUid, &sessionId, &notificationFrames, cblk, buffers, &status);
             LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
             reply->writeInt64(frameCount);
             reply->writeInt32(flags);
@@ -1247,12 +1254,13 @@
             int32_t priority = data.readInt32();
             audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
             int sessionId = data.readInt32();
+            const String16 opPackageName = data.readString16();
             status_t status;
             int id;
             int enabled;
 
             sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId,
-                    &status, &id, &enabled);
+                    opPackageName, &status, &id, &enabled);
             reply->writeInt32(status);
             reply->writeInt32(id);
             reply->writeInt32(enabled);
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 641e6c1..3429d36 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -39,25 +39,18 @@
     {
     }
 
-    void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2)
+    void ioConfigChanged(audio_io_config_event event, const sp<AudioIoDescriptor>& ioDesc)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
         data.writeInt32(event);
-        data.writeInt32((int32_t) ioHandle);
-        if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
-            uint32_t stream = *(const uint32_t *)param2;
-            ALOGV("ioConfigChanged stream %d", stream);
-            data.writeInt32(stream);
-        } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
-            const AudioSystem::OutputDescriptor *desc =
-                    (const AudioSystem::OutputDescriptor *)param2;
-            data.writeInt32(desc->samplingRate);
-            data.writeInt32(desc->format);
-            data.writeInt32(desc->channelMask);
-            data.writeInt64(desc->frameCount);
-            data.writeInt32(desc->latency);
-        }
+        data.writeInt32((int32_t)ioDesc->mIoHandle);
+        data.write(&ioDesc->mPatch, sizeof(struct audio_patch));
+        data.writeInt32(ioDesc->mSamplingRate);
+        data.writeInt32(ioDesc->mFormat);
+        data.writeInt32(ioDesc->mChannelMask);
+        data.writeInt64(ioDesc->mFrameCount);
+        data.writeInt32(ioDesc->mLatency);
         remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
     }
 };
@@ -72,24 +65,16 @@
     switch (code) {
     case IO_CONFIG_CHANGED: {
             CHECK_INTERFACE(IAudioFlingerClient, data, reply);
-            int event = data.readInt32();
-            audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
-            const void *param2 = NULL;
-            AudioSystem::OutputDescriptor desc;
-            uint32_t stream;
-            if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
-                stream = data.readInt32();
-                param2 = &stream;
-                ALOGV("STREAM_CONFIG_CHANGED stream %d", stream);
-            } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
-                desc.samplingRate = data.readInt32();
-                desc.format = (audio_format_t) data.readInt32();
-                desc.channelMask = (audio_channel_mask_t) data.readInt32();
-                desc.frameCount = data.readInt64();
-                desc.latency = data.readInt32();
-                param2 = &desc;
-            }
-            ioConfigChanged(event, ioHandle, param2);
+            audio_io_config_event event = (audio_io_config_event)data.readInt32();
+            sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
+            ioDesc->mIoHandle = (audio_io_handle_t) data.readInt32();
+            data.read(&ioDesc->mPatch, sizeof(struct audio_patch));
+            ioDesc->mSamplingRate = data.readInt32();
+            ioDesc->mFormat = (audio_format_t) data.readInt32();
+            ioDesc->mChannelMask = (audio_channel_mask_t) data.readInt32();
+            ioDesc->mFrameCount = data.readInt64();
+            ioDesc->mLatency = data.readInt32();
+            ioConfigChanged(event, ioDesc);
             return NO_ERROR;
         } break;
         default:
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 39374d8..fd18f17 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -71,6 +71,8 @@
     RELEASE_SOUNDTRIGGER_SESSION,
     GET_PHONE_STATE,
     REGISTER_POLICY_MIXES,
+    START_AUDIO_SOURCE,
+    STOP_AUDIO_SOURCE
 };
 
 #define MAX_ITEMS_PER_LIST 1024
@@ -169,10 +171,12 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
+                                        uid_t uid,
                                         uint32_t samplingRate,
                                         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;
@@ -204,10 +208,12 @@
                 data.writeInt32(1);
                 data.writeInt32(*stream);
             }
+            data.writeInt32(uid);
             data.writeInt32(samplingRate);
             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);
@@ -271,10 +277,12 @@
     virtual status_t getInputForAttr(const audio_attributes_t *attr,
                                      audio_io_handle_t *input,
                                      audio_session_t session,
+                                     uid_t uid,
                                      uint32_t samplingRate,
                                      audio_format_t format,
                                      audio_channel_mask_t channelMask,
-                                     audio_input_flags_t flags)
+                                     audio_input_flags_t flags,
+                                     audio_port_handle_t selectedDeviceId)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -288,10 +296,12 @@
         }
         data.write(attr, sizeof(audio_attributes_t));
         data.writeInt32(session);
+        data.writeInt32(uid);
         data.writeInt32(samplingRate);
         data.writeInt32(static_cast <uint32_t>(format));
         data.writeInt32(channelMask);
         data.writeInt32(flags);
+        data.writeInt32(selectedDeviceId);
         status_t status = remote()->transact(GET_INPUT_FOR_ATTR, data, &reply);
         if (status != NO_ERROR) {
             return status;
@@ -712,6 +722,42 @@
         }
         return status;
     }
+
+    virtual status_t startAudioSource(const struct audio_port_config *source,
+                                      const audio_attributes_t *attributes,
+                                      audio_io_handle_t *handle)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        if (source == NULL || attributes == NULL || handle == NULL) {
+            return BAD_VALUE;
+        }
+        data.write(source, sizeof(struct audio_port_config));
+        data.write(attributes, sizeof(audio_attributes_t));
+        status_t status = remote()->transact(START_AUDIO_SOURCE, data, &reply);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        status = (status_t)reply.readInt32();
+        if (status != NO_ERROR) {
+            return status;
+        }
+        *handle = (audio_io_handle_t)reply.readInt32();
+        return status;
+    }
+
+    virtual status_t stopAudioSource(audio_io_handle_t handle)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        status_t status = remote()->transact(STOP_AUDIO_SOURCE, data, &reply);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        status = (status_t)reply.readInt32();
+        return status;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -810,11 +856,13 @@
             if (hasStream) {
                 stream = (audio_stream_type_t)data.readInt32();
             }
+            uid_t uid = (uid_t)data.readInt32();
             uint32_t samplingRate = data.readInt32();
             audio_format_t format = (audio_format_t) data.readInt32();
             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) {
@@ -822,9 +870,9 @@
             }
             audio_io_handle_t output;
             status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
-                    &output, session, &stream,
+                    &output, session, &stream, uid,
                     samplingRate, format, channelMask,
-                    flags, hasOffloadInfo ? &offloadInfo : NULL);
+                    flags, selectedDeviceId, hasOffloadInfo ? &offloadInfo : NULL);
             reply->writeInt32(status);
             reply->writeInt32(output);
             reply->writeInt32(stream);
@@ -869,14 +917,16 @@
             audio_attributes_t attr;
             data.read(&attr, sizeof(audio_attributes_t));
             audio_session_t session = (audio_session_t)data.readInt32();
+            uid_t uid = (uid_t)data.readInt32();
             uint32_t samplingRate = data.readInt32();
             audio_format_t format = (audio_format_t) data.readInt32();
             audio_channel_mask_t channelMask = data.readInt32();
             audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
+            audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
             audio_io_handle_t input;
-            status_t status = getInputForAttr(&attr, &input, session,
+            status_t status = getInputForAttr(&attr, &input, session, uid,
                                               samplingRate, format, channelMask,
-                                              flags);
+                                              flags, selectedDeviceId);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
                 reply->writeInt32(input);
@@ -1221,6 +1271,27 @@
             return NO_ERROR;
         } break;
 
+        case START_AUDIO_SOURCE: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            struct audio_port_config source;
+            data.read(&source, sizeof(struct audio_port_config));
+            audio_attributes_t attributes;
+            data.read(&attributes, sizeof(audio_attributes_t));
+            audio_io_handle_t handle;
+            status_t status = startAudioSource(&source, &attributes, &handle);
+            reply->writeInt32(status);
+            reply->writeInt32(handle);
+            return NO_ERROR;
+        } break;
+
+        case STOP_AUDIO_SOURCE: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            audio_io_handle_t handle = (audio_io_handle_t)data.readInt32();
+            status_t status = stopAudioSource(handle);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        } break;
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
index 7c65878..65cc7d6 100644
--- a/media/libmedia/IAudioPolicyServiceClient.cpp
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -29,7 +29,8 @@
 
 enum {
     PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
-    PATCH_LIST_UPDATE
+    PATCH_LIST_UPDATE,
+    MIX_STATE_UPDATE
 };
 
 class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
@@ -53,6 +54,15 @@
         data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
         remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+        data.writeString8(regId);
+        data.writeInt32(state);
+        remote()->transact(MIX_STATE_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
@@ -73,6 +83,13 @@
             onAudioPatchListUpdate();
             return NO_ERROR;
         } break;
+    case MIX_STATE_UPDATE: {
+            CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+            String8 regId = data.readString8();
+            int32_t state = data.readInt32();
+            onDynamicPolicyMixStateUpdate(regId, state);
+            return NO_ERROR;
+    }
     default:
         return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index c26c5bf..2f440fe 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);
@@ -139,7 +142,7 @@
 
         ssize_t result = reply.readInt32();
 
-        if (result >= ERROR_DRM_VENDOR_MIN && result <= ERROR_DRM_VENDOR_MAX) {
+        if (isCryptoError(result)) {
             errorDetailMsg->setTo(reply.readCString());
         }
 
@@ -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,20 +301,25 @@
             }
 
             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);
 
-            if (result >= ERROR_DRM_VENDOR_MIN
-                && result <= ERROR_DRM_VENDOR_MAX) {
+            if (isCryptoError(result)) {
                 reply->writeCString(errorDetailMsg.c_str());
             }
 
@@ -294,9 +335,6 @@
             delete[] subSamples;
             subSamples = NULL;
 
-            free(srcData);
-            srcData = NULL;
-
             return OK;
         }
 
@@ -311,6 +349,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/IDataSource.cpp b/media/libmedia/IDataSource.cpp
new file mode 100644
index 0000000..76d1d68
--- /dev/null
+++ b/media/libmedia/IDataSource.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IDataSource"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#include <media/IDataSource.h>
+
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+enum {
+    GET_IMEMORY = IBinder::FIRST_CALL_TRANSACTION,
+    READ_AT,
+    GET_SIZE,
+    CLOSE,
+};
+
+struct BpDataSource : public BpInterface<IDataSource> {
+    BpDataSource(const sp<IBinder>& impl) : BpInterface<IDataSource>(impl) {}
+
+    virtual sp<IMemory> getIMemory() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+        remote()->transact(GET_IMEMORY, data, &reply);
+        sp<IBinder> binder = reply.readStrongBinder();
+        return interface_cast<IMemory>(binder);
+    }
+
+    virtual ssize_t readAt(off64_t offset, size_t size) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+        data.writeInt64(offset);
+        data.writeInt64(size);
+        remote()->transact(READ_AT, data, &reply);
+        return reply.readInt64();
+    }
+
+    virtual status_t getSize(off64_t* size) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+        remote()->transact(GET_SIZE, data, &reply);
+        status_t err = reply.readInt32();
+        *size = reply.readInt64();
+        return err;
+    }
+
+    virtual void close() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+        remote()->transact(CLOSE, data, &reply);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(DataSource, "android.media.IDataSource");
+
+status_t BnDataSource::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    switch (code) {
+        case GET_IMEMORY: {
+            CHECK_INTERFACE(IDataSource, data, reply);
+            reply->writeStrongBinder(IInterface::asBinder(getIMemory()));
+            return NO_ERROR;
+        } break;
+        case READ_AT: {
+            CHECK_INTERFACE(IDataSource, data, reply);
+            off64_t offset = (off64_t) data.readInt64();
+            size_t size = (size_t) data.readInt64();
+            reply->writeInt64(readAt(offset, size));
+            return NO_ERROR;
+        } break;
+        case GET_SIZE: {
+            CHECK_INTERFACE(IDataSource, data, reply);
+            off64_t size;
+            status_t err = getSize(&size);
+            reply->writeInt32(err);
+            reply->writeInt64(size);
+            return NO_ERROR;
+        } break;
+        case CLOSE: {
+            CHECK_INTERFACE(IDataSource, data, reply);
+            close();
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}  // namespace android
diff --git a/media/libmedia/IHDCP.cpp b/media/libmedia/IHDCP.cpp
index 9122f75..f3a8902 100644
--- a/media/libmedia/IHDCP.cpp
+++ b/media/libmedia/IHDCP.cpp
@@ -241,8 +241,19 @@
         case HDCP_ENCRYPT:
         {
             size_t size = data.readInt32();
+            size_t bufSize = 2 * size;
 
-            void *inData = malloc(2 * size);
+            // watch out for overflow
+            void *inData = NULL;
+            if (bufSize > size) {
+                inData = malloc(bufSize);
+            }
+
+            if (inData == NULL) {
+                reply->writeInt32(ERROR_OUT_OF_RANGE);
+                return OK;
+            }
+
             void *outData = (uint8_t *)inData + size;
 
             data.read(inData, size);
@@ -273,11 +284,17 @@
             size_t offset = data.readInt32();
             size_t size = data.readInt32();
             uint32_t streamCTR = data.readInt32();
-            void *outData = malloc(size);
+            void *outData = NULL;
             uint64_t inputCTR;
 
-            status_t err = encryptNative(graphicBuffer, offset, size,
-                                         streamCTR, &inputCTR, outData);
+            status_t err = ERROR_OUT_OF_RANGE;
+
+            outData = malloc(size);
+
+            if (outData != NULL) {
+                err = encryptNative(graphicBuffer, offset, size,
+                                             streamCTR, &inputCTR, outData);
+            }
 
             reply->writeInt32(err);
 
@@ -295,8 +312,19 @@
         case HDCP_DECRYPT:
         {
             size_t size = data.readInt32();
+            size_t bufSize = 2 * size;
 
-            void *inData = malloc(2 * size);
+            // watch out for overflow
+            void *inData = NULL;
+            if (bufSize > size) {
+                inData = malloc(bufSize);
+            }
+
+            if (inData == NULL) {
+                reply->writeInt32(ERROR_OUT_OF_RANGE);
+                return OK;
+            }
+
             void *outData = (uint8_t *)inData + size;
 
             data.read(inData, size);
diff --git a/media/libmedia/IMediaCodecList.cpp b/media/libmedia/IMediaCodecList.cpp
index 80020db..e2df104 100644
--- a/media/libmedia/IMediaCodecList.cpp
+++ b/media/libmedia/IMediaCodecList.cpp
@@ -30,6 +30,7 @@
     CREATE = IBinder::FIRST_CALL_TRANSACTION,
     COUNT_CODECS,
     GET_CODEC_INFO,
+    GET_GLOBAL_SETTINGS,
     FIND_CODEC_BY_TYPE,
     FIND_CODEC_BY_NAME,
 };
@@ -64,6 +65,19 @@
         }
     }
 
+    virtual const sp<AMessage> getGlobalSettings() const
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaCodecList::getInterfaceDescriptor());
+        remote()->transact(GET_GLOBAL_SETTINGS, data, &reply);
+        status_t err = reply.readInt32();
+        if (err == OK) {
+            return AMessage::FromParcel(reply);
+        } else {
+            return NULL;
+        }
+    }
+
     virtual ssize_t findCodecByType(
             const char *type, bool encoder, size_t startIndex = 0) const
     {
@@ -125,6 +139,20 @@
         }
         break;
 
+        case GET_GLOBAL_SETTINGS:
+        {
+            CHECK_INTERFACE(IMediaCodecList, data, reply);
+            const sp<AMessage> info = getGlobalSettings();
+            if (info != NULL) {
+                reply->writeInt32(OK);
+                info->writeToParcel(reply);
+            } else {
+                reply->writeInt32(-ERANGE);
+            }
+            return NO_ERROR;
+        }
+        break;
+
         case FIND_CODEC_BY_TYPE:
         {
             CHECK_INTERFACE(IMediaCodecList, data, reply);
diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp
index 2ff7658..09137ef 100644
--- a/media/libmedia/IMediaHTTPConnection.cpp
+++ b/media/libmedia/IMediaHTTPConnection.cpp
@@ -24,6 +24,7 @@
 #include <binder/Parcel.h>
 #include <utils/String8.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
 
 namespace android {
 
@@ -106,11 +107,18 @@
             return UNKNOWN_ERROR;
         }
 
-        int32_t len = reply.readInt32();
+        size_t len = reply.readInt32();
 
-        if (len > 0) {
-            memcpy(buffer, mMemory->pointer(), len);
+        if (len > size) {
+            ALOGE("requested %zu, got %zu", size, len);
+            return ERROR_OUT_OF_RANGE;
         }
+        if (len > mMemory->size()) {
+            ALOGE("got %zu, but memory has %zu", len, mMemory->size());
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        memcpy(buffer, mMemory->pointer(), len);
 
         return len;
     }
diff --git a/media/libmedia/IMediaHTTPService.cpp b/media/libmedia/IMediaHTTPService.cpp
index f30d0f3..0c16a2b 100644
--- a/media/libmedia/IMediaHTTPService.cpp
+++ b/media/libmedia/IMediaHTTPService.cpp
@@ -44,6 +44,7 @@
         status_t err = reply.readInt32();
 
         if (err != OK) {
+            ALOGE("Unable to make HTTP connection (err = %d)", err);
             return NULL;
         }
 
diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp
index 230749e..1536337 100644
--- a/media/libmedia/IMediaLogService.cpp
+++ b/media/libmedia/IMediaLogService.cpp
@@ -45,7 +45,7 @@
         data.writeStrongBinder(IInterface::asBinder(shared));
         data.writeInt64((int64_t) size);
         data.writeCString(name);
-        status_t status = remote()->transact(REGISTER_WRITER, data, &reply);
+        status_t status __unused = remote()->transact(REGISTER_WRITER, data, &reply);
         // FIXME ignores status
     }
 
@@ -53,7 +53,7 @@
         Parcel data, reply;
         data.writeInterfaceToken(IMediaLogService::getInterfaceDescriptor());
         data.writeStrongBinder(IInterface::asBinder(shared));
-        status_t status = remote()->transact(UNREGISTER_WRITER, data, &reply);
+        status_t status __unused = remote()->transact(UNREGISTER_WRITER, data, &reply);
         // FIXME ignores status
     }
 
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 551cffe..dbf524e 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <binder/Parcel.h>
+#include <media/IDataSource.h>
 #include <media/IMediaHTTPService.h>
 #include <media/IMediaMetadataRetriever.h>
 #include <utils/String8.h>
@@ -65,6 +66,7 @@
     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
     SET_DATA_SOURCE_URL,
     SET_DATA_SOURCE_FD,
+    SET_DATA_SOURCE_CALLBACK,
     GET_FRAME_AT_TIME,
     EXTRACT_ALBUM_ART,
     EXTRACT_METADATA,
@@ -125,6 +127,15 @@
         return reply.readInt32();
     }
 
+    status_t setDataSource(const sp<IDataSource>& source)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
+        data.writeStrongBinder(IInterface::asBinder(source));
+        remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
+        return reply.readInt32();
+    }
+
     sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
     {
         ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
@@ -229,12 +240,19 @@
         } break;
         case SET_DATA_SOURCE_FD: {
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
-            int fd = dup(data.readFileDescriptor());
+            int fd = data.readFileDescriptor();
             int64_t offset = data.readInt64();
             int64_t length = data.readInt64();
             reply->writeInt32(setDataSource(fd, offset, length));
             return NO_ERROR;
         } break;
+        case SET_DATA_SOURCE_CALLBACK: {
+            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
+            sp<IDataSource> source =
+                interface_cast<IDataSource>(data.readStrongBinder());
+            reply->writeInt32(setDataSource(source));
+            return NO_ERROR;
+        } break;
         case GET_FRAME_AT_TIME: {
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
             int64_t timeUs = data.readInt64();
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index ce3009a..bde35f2 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -21,6 +21,10 @@
 
 #include <binder/Parcel.h>
 
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
+
+#include <media/IDataSource.h>
 #include <media/IMediaHTTPService.h>
 #include <media/IMediaPlayer.h>
 #include <media/IStreamSource.h>
@@ -35,11 +39,15 @@
     SET_DATA_SOURCE_URL,
     SET_DATA_SOURCE_FD,
     SET_DATA_SOURCE_STREAM,
+    SET_DATA_SOURCE_CALLBACK,
     PREPARE_ASYNC,
     START,
     STOP,
     IS_PLAYING,
-    SET_PLAYBACK_RATE,
+    SET_PLAYBACK_SETTINGS,
+    GET_PLAYBACK_SETTINGS,
+    SET_SYNC_SETTINGS,
+    GET_SYNC_SETTINGS,
     PAUSE,
     SEEK_TO,
     GET_CURRENT_POSITION,
@@ -121,6 +129,14 @@
         return reply.readInt32();
     }
 
+    status_t setDataSource(const sp<IDataSource> &source) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        data.writeStrongBinder(IInterface::asBinder(source));
+        remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
+        return reply.readInt32();
+    }
+
     // pass the buffered IGraphicBufferProducer to the media player service
     status_t setVideoSurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer)
     {
@@ -165,15 +181,63 @@
         return reply.readInt32();
     }
 
-    status_t setPlaybackRate(float rate)
+    status_t setPlaybackSettings(const AudioPlaybackRate& rate)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-        data.writeFloat(rate);
-        remote()->transact(SET_PLAYBACK_RATE, data, &reply);
+        data.writeFloat(rate.mSpeed);
+        data.writeFloat(rate.mPitch);
+        data.writeInt32((int32_t)rate.mFallbackMode);
+        data.writeInt32((int32_t)rate.mStretchMode);
+        remote()->transact(SET_PLAYBACK_SETTINGS, data, &reply);
         return reply.readInt32();
     }
 
+    status_t getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        remote()->transact(GET_PLAYBACK_SETTINGS, data, &reply);
+        status_t err = reply.readInt32();
+        if (err == OK) {
+            *rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+            rate->mSpeed = reply.readFloat();
+            rate->mPitch = reply.readFloat();
+            rate->mFallbackMode = (AudioTimestretchFallbackMode)reply.readInt32();
+            rate->mStretchMode = (AudioTimestretchStretchMode)reply.readInt32();
+        }
+        return err;
+    }
+
+    status_t setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        data.writeInt32((int32_t)sync.mSource);
+        data.writeInt32((int32_t)sync.mAudioAdjustMode);
+        data.writeFloat(sync.mTolerance);
+        data.writeFloat(videoFpsHint);
+        remote()->transact(SET_SYNC_SETTINGS, data, &reply);
+        return reply.readInt32();
+    }
+
+    status_t getSyncSettings(AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        remote()->transact(GET_SYNC_SETTINGS, data, &reply);
+        status_t err = reply.readInt32();
+        if (err == OK) {
+            AVSyncSettings settings;
+            settings.mSource = (AVSyncSource)reply.readInt32();
+            settings.mAudioAdjustMode = (AVSyncAudioAdjustMode)reply.readInt32();
+            settings.mTolerance = reply.readFloat();
+            *sync = settings;
+            *videoFps = reply.readFloat();
+        }
+        return err;
+    }
+
     status_t pause()
     {
         Parcel data, reply;
@@ -406,6 +470,13 @@
             reply->writeInt32(setDataSource(source));
             return NO_ERROR;
         }
+        case SET_DATA_SOURCE_CALLBACK: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            sp<IDataSource> source =
+                interface_cast<IDataSource>(data.readStrongBinder());
+            reply->writeInt32(setDataSource(source));
+            return NO_ERROR;
+        }
         case SET_VIDEO_SURFACETEXTURE: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
             sp<IGraphicBufferProducer> bufferProducer =
@@ -436,9 +507,51 @@
             reply->writeInt32(ret);
             return NO_ERROR;
         } break;
-        case SET_PLAYBACK_RATE: {
+        case SET_PLAYBACK_SETTINGS: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
-            reply->writeInt32(setPlaybackRate(data.readFloat()));
+            AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+            rate.mSpeed = data.readFloat();
+            rate.mPitch = data.readFloat();
+            rate.mFallbackMode = (AudioTimestretchFallbackMode)data.readInt32();
+            rate.mStretchMode = (AudioTimestretchStretchMode)data.readInt32();
+            reply->writeInt32(setPlaybackSettings(rate));
+            return NO_ERROR;
+        } break;
+        case GET_PLAYBACK_SETTINGS: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+            status_t err = getPlaybackSettings(&rate);
+            reply->writeInt32(err);
+            if (err == OK) {
+                reply->writeFloat(rate.mSpeed);
+                reply->writeFloat(rate.mPitch);
+                reply->writeInt32((int32_t)rate.mFallbackMode);
+                reply->writeInt32((int32_t)rate.mStretchMode);
+            }
+            return NO_ERROR;
+        } break;
+        case SET_SYNC_SETTINGS: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            AVSyncSettings sync;
+            sync.mSource = (AVSyncSource)data.readInt32();
+            sync.mAudioAdjustMode = (AVSyncAudioAdjustMode)data.readInt32();
+            sync.mTolerance = data.readFloat();
+            float videoFpsHint = data.readFloat();
+            reply->writeInt32(setSyncSettings(sync, videoFpsHint));
+            return NO_ERROR;
+        } break;
+        case GET_SYNC_SETTINGS: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            AVSyncSettings sync;
+            float videoFps;
+            status_t err = getSyncSettings(&sync, &videoFps);
+            reply->writeInt32(err);
+            if (err == OK) {
+                reply->writeInt32((int32_t)sync.mSource);
+                reply->writeInt32((int32_t)sync.mAudioAdjustMode);
+                reply->writeFloat(sync.mTolerance);
+                reply->writeFloat(videoFps);
+            }
             return NO_ERROR;
         } break;
         case PAUSE: {
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index aa7b2e1..05f8670 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -78,10 +78,11 @@
         return interface_cast<IMediaPlayer>(reply.readStrongBinder());
     }
 
-    virtual sp<IMediaRecorder> createMediaRecorder()
+    virtual sp<IMediaRecorder> createMediaRecorder(const String16 &opPackageName)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
         remote()->transact(CREATE_MEDIA_RECORDER, data, &reply);
         return interface_cast<IMediaRecorder>(reply.readStrongBinder());
     }
@@ -128,11 +129,12 @@
         return remote()->transact(PULL_BATTERY_DATA, data, reply);
     }
 
-    virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
-            const String8& iface)
+    virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
+            const sp<IRemoteDisplayClient>& client, const String8& iface)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
         data.writeStrongBinder(IInterface::asBinder(client));
         data.writeString8(iface);
         remote()->transact(LISTEN_FOR_REMOTE_DISPLAY, data, &reply);
@@ -166,7 +168,8 @@
         } break;
         case CREATE_MEDIA_RECORDER: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
-            sp<IMediaRecorder> recorder = createMediaRecorder();
+            const String16 opPackageName = data.readString16();
+            sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
             reply->writeStrongBinder(IInterface::asBinder(recorder));
             return NO_ERROR;
         } break;
@@ -214,10 +217,11 @@
         } break;
         case LISTEN_FOR_REMOTE_DISPLAY: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
+            const String16 opPackageName = data.readString16();
             sp<IRemoteDisplayClient> client(
                     interface_cast<IRemoteDisplayClient>(data.readStrongBinder()));
             String8 iface(data.readString8());
-            sp<IRemoteDisplay> display(listenForRemoteDisplay(client, iface));
+            sp<IRemoteDisplay> display(listenForRemoteDisplay(opPackageName, client, iface));
             reply->writeStrongBinder(IInterface::asBinder(display));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 8ca256c..ee3b584 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -35,6 +35,7 @@
     RELEASE = IBinder::FIRST_CALL_TRANSACTION,
     INIT,
     CLOSE,
+    SET_INPUT_SURFACE,
     QUERY_SURFACE_MEDIASOURCE,
     RESET,
     STOP,
@@ -75,6 +76,16 @@
         return reply.readInt32();
     }
 
+    status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface)
+    {
+        ALOGV("setInputSurface(%p)", surface.get());
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+        data.writeStrongBinder(IInterface::asBinder(surface));
+        remote()->transact(SET_INPUT_SURFACE, data, &reply);
+        return reply.readInt32();
+    }
+
     sp<IGraphicBufferProducer> querySurfaceMediaSource()
     {
         ALOGV("Query SurfaceMediaSource");
@@ -442,6 +453,14 @@
             reply->writeInt32(setCamera(camera, proxy));
             return NO_ERROR;
         } break;
+        case SET_INPUT_SURFACE: {
+            ALOGV("SET_INPUT_SURFACE");
+            CHECK_INTERFACE(IMediaRecorder, data, reply);
+            sp<IGraphicBufferConsumer> surface = interface_cast<IGraphicBufferConsumer>(
+                    data.readStrongBinder());
+            reply->writeInt32(setInputSurface(surface));
+            return NO_ERROR;
+        } break;
         case QUERY_SURFACE_MEDIASOURCE: {
             ALOGV("QUERY_SURFACE_MEDIASOURCE");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index e208df9..c14debf 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -41,6 +41,8 @@
     USE_BUFFER,
     USE_GRAPHIC_BUFFER,
     CREATE_INPUT_SURFACE,
+    CREATE_PERSISTENT_INPUT_SURFACE,
+    SET_INPUT_SURFACE,
     SIGNAL_END_OF_INPUT_STREAM,
     STORE_META_DATA_IN_BUFFERS,
     PREPARE_FOR_ADAPTIVE_PLAYBACK,
@@ -326,6 +328,50 @@
         return err;
     }
 
+    virtual status_t createPersistentInputSurface(
+            sp<IGraphicBufferProducer> *bufferProducer,
+            sp<IGraphicBufferConsumer> *bufferConsumer) {
+        Parcel data, reply;
+        status_t err;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        err = remote()->transact(CREATE_PERSISTENT_INPUT_SURFACE, data, &reply);
+        if (err != OK) {
+            ALOGW("binder transaction failed: %d", err);
+            return err;
+        }
+
+        err = reply.readInt32();
+        if (err != OK) {
+            return err;
+        }
+
+        *bufferProducer = IGraphicBufferProducer::asInterface(
+                reply.readStrongBinder());
+        *bufferConsumer = IGraphicBufferConsumer::asInterface(
+                reply.readStrongBinder());
+
+        return err;
+    }
+
+    virtual status_t setInputSurface(
+            node_id node, OMX_U32 port_index,
+            const sp<IGraphicBufferConsumer> &bufferConsumer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        status_t err;
+        data.writeInt32((int32_t)node);
+        data.writeInt32(port_index);
+        data.writeStrongBinder(IInterface::asBinder(bufferConsumer));
+
+        err = remote()->transact(SET_INPUT_SURFACE, data, &reply);
+
+        if (err != OK) {
+            ALOGW("binder transaction failed: %d", err);
+            return err;
+        }
+        return reply.readInt32();
+    }
+
     virtual status_t signalEndOfInputStream(node_id node) {
         Parcel data, reply;
         status_t err;
@@ -781,6 +827,41 @@
             return NO_ERROR;
         }
 
+        case CREATE_PERSISTENT_INPUT_SURFACE:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            sp<IGraphicBufferProducer> bufferProducer;
+            sp<IGraphicBufferConsumer> bufferConsumer;
+            status_t err = createPersistentInputSurface(
+                    &bufferProducer, &bufferConsumer);
+
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                reply->writeStrongBinder(IInterface::asBinder(bufferProducer));
+                reply->writeStrongBinder(IInterface::asBinder(bufferConsumer));
+            }
+
+            return NO_ERROR;
+        }
+
+        case SET_INPUT_SURFACE:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (node_id)data.readInt32();
+            OMX_U32 port_index = data.readInt32();
+
+            sp<IGraphicBufferConsumer> bufferConsumer =
+                    interface_cast<IGraphicBufferConsumer>(data.readStrongBinder());
+
+            status_t err = setInputSurface(node, port_index, bufferConsumer);
+
+            reply->writeInt32(err);
+            return NO_ERROR;
+        }
+
         case SIGNAL_END_OF_INPUT_STREAM:
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
diff --git a/media/libmedia/IResourceManagerClient.cpp b/media/libmedia/IResourceManagerClient.cpp
index 6fa56fc..b3f56e8 100644
--- a/media/libmedia/IResourceManagerClient.cpp
+++ b/media/libmedia/IResourceManagerClient.cpp
@@ -25,6 +25,7 @@
 
 enum {
     RECLAIM_RESOURCE = IBinder::FIRST_CALL_TRANSACTION,
+    GET_NAME,
 };
 
 class BpResourceManagerClient: public BpInterface<IResourceManagerClient>
@@ -46,6 +47,19 @@
         }
         return ret;
     }
+
+    virtual String8 getName() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IResourceManagerClient::getInterfaceDescriptor());
+
+        String8 ret;
+        status_t status = remote()->transact(GET_NAME, data, &reply);
+        if (status == NO_ERROR) {
+            ret = reply.readString8();
+        }
+        return ret;
+    }
+
 };
 
 IMPLEMENT_META_INTERFACE(ResourceManagerClient, "android.media.IResourceManagerClient");
@@ -62,6 +76,12 @@
             reply->writeInt32(ret);
             return NO_ERROR;
         } break;
+        case GET_NAME: {
+            CHECK_INTERFACE(IResourceManagerClient, data, reply);
+            String8 ret = getName();
+            reply->writeString8(ret);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IResourceManagerService.cpp b/media/libmedia/IResourceManagerService.cpp
index 95a2d1c..7ae946d 100644
--- a/media/libmedia/IResourceManagerService.cpp
+++ b/media/libmedia/IResourceManagerService.cpp
@@ -38,11 +38,9 @@
 template <typename T>
 static void writeToParcel(Parcel *data, const Vector<T> &items) {
     size_t size = items.size();
-    size_t sizePosition = data->dataPosition();
     // truncates size, but should be okay for this usecase
     data->writeUint32(static_cast<uint32_t>(size));
     for (size_t i = 0; i < size; i++) {
-        size_t position = data->dataPosition();
         items[i].writeToParcel(data);
     }
 }
@@ -121,7 +119,6 @@
     switch (code) {
         case CONFIG: {
             CHECK_INTERFACE(IResourceManagerService, data, reply);
-            int pid = data.readInt32();
             sp<IResourceManagerClient> client(
                     interface_cast<IResourceManagerClient>(data.readStrongBinder()));
             Vector<MediaResourcePolicy> policies;
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 7b4c4e2..8d3fa7b 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -206,6 +206,17 @@
     return OK;
 }
 
+status_t MediaCodecInfo::updateMime(const char *mime) {
+    ssize_t ix = getCapabilityIndex(mime);
+    if (ix < 0) {
+        ALOGE("updateMime mime not found %s", mime);
+        return -EINVAL;
+    }
+
+    mCurrentCaps = mCaps.valueAt(ix);
+    return OK;
+}
+
 void MediaCodecInfo::removeMime(const char *mime) {
     ssize_t ix = getCapabilityIndex(mime);
     if (ix >= 0) {
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 47f9258..ae0061f 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -532,7 +532,6 @@
         CHECK(refIndex != -1);
         RequiredProfileRefInfo *info;
         camcorder_quality refQuality;
-        VideoCodec *codec = NULL;
 
         // Check high and low from either camcorder profile, timelapse profile
         // or high speed profile, but not all of them. Default, check camcorder profile
diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp
index 8be01bc..40ec0cb 100644
--- a/media/libmedia/MediaResource.cpp
+++ b/media/libmedia/MediaResource.cpp
@@ -23,6 +23,8 @@
 
 const char kResourceSecureCodec[] = "secure-codec";
 const char kResourceNonSecureCodec[] = "non-secure-codec";
+const char kResourceAudioCodec[] = "audio-codec";
+const char kResourceVideoCodec[] = "video-codec";
 const char kResourceGraphicMemory[] = "graphic-memory";
 
 MediaResource::MediaResource() : mValue(0) {}
@@ -50,7 +52,7 @@
 
 String8 MediaResource::toString() const {
     String8 str;
-    str.appendFormat("%s/%s:%llu", mType.string(), mSubType.string(), mValue);
+    str.appendFormat("%s/%s:%llu", mType.string(), mSubType.string(), (unsigned long long)mValue);
     return str;
 }
 
diff --git a/media/libmedia/MediaResourcePolicy.cpp b/media/libmedia/MediaResourcePolicy.cpp
index 2bb996a..5210825 100644
--- a/media/libmedia/MediaResourcePolicy.cpp
+++ b/media/libmedia/MediaResourcePolicy.cpp
@@ -24,25 +24,25 @@
 const char kPolicySupportsMultipleSecureCodecs[] = "supports-multiple-secure-codecs";
 const char kPolicySupportsSecureWithNonSecureCodec[] = "supports-secure-with-non-secure-codec";
 
-MediaResourcePolicy::MediaResourcePolicy() : mValue(0) {}
+MediaResourcePolicy::MediaResourcePolicy() {}
 
-MediaResourcePolicy::MediaResourcePolicy(String8 type, uint64_t value)
+MediaResourcePolicy::MediaResourcePolicy(String8 type, String8 value)
         : mType(type),
           mValue(value) {}
 
 void MediaResourcePolicy::readFromParcel(const Parcel &parcel) {
     mType = parcel.readString8();
-    mValue = parcel.readUint64();
+    mValue = parcel.readString8();
 }
 
 void MediaResourcePolicy::writeToParcel(Parcel *parcel) const {
     parcel->writeString8(mType);
-    parcel->writeUint64(mValue);
+    parcel->writeString8(mValue);
 }
 
 String8 MediaResourcePolicy::toString() const {
     String8 str;
-    str.appendFormat("%s:%llu", mType.string(), mValue);
+    str.appendFormat("%s:%s", mType.string(), mValue.string());
     return str;
 }
 
diff --git a/media/libmedia/MemoryLeakTrackUtil.cpp b/media/libmedia/MemoryLeakTrackUtil.cpp
index d31f721..554dbae 100644
--- a/media/libmedia/MemoryLeakTrackUtil.cpp
+++ b/media/libmedia/MemoryLeakTrackUtil.cpp
@@ -173,7 +173,7 @@
 
 #else
 // Does nothing
-void dumpMemoryAddresses(int fd) {}
+void dumpMemoryAddresses(int fd __unused) {}
 
 #endif
 }  // namespace android
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 2cc4685..6da5348 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -984,7 +984,6 @@
             if ((mStartTime.tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) {
                 time_t sec = stopTime.tv_sec - mStartTime.tv_sec;
                 long nsec = stopTime.tv_nsec - mStartTime.tv_nsec;
-                long durationMs;
                 if (nsec < 0) {
                     --sec;
                     nsec += 1000000000;
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 9d69b6a..dc46038 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -34,11 +34,12 @@
 
 // ---------------------------------------------------------------------------
 
-Visualizer::Visualizer (int32_t priority,
+Visualizer::Visualizer (const String16& opPackageName,
+         int32_t priority,
          effect_callback_t cbf,
          void* user,
          int sessionId)
-    :   AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId),
+    :   AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId),
         mCaptureRate(CAPTURE_RATE_DEF),
         mCaptureSize(CAPTURE_SIZE_DEF),
         mSampleRate(44100000),
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 873808a..9a76f58 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -129,6 +129,18 @@
     return mRetriever->setDataSource(fd, offset, length);
 }
 
+status_t MediaMetadataRetriever::setDataSource(
+    const sp<IDataSource>& dataSource)
+{
+    ALOGV("setDataSource(IDataSource)");
+    Mutex::Autolock _l(mLock);
+    if (mRetriever == 0) {
+        ALOGE("retriever is not initialized");
+        return INVALID_OPERATION;
+    }
+    return mRetriever->setDataSource(dataSource);
+}
+
 sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option)
 {
     ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d)", timeUs, option);
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 5dd8c02..81a5e8c 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -32,7 +32,10 @@
 #include <gui/Surface.h>
 
 #include <media/mediaplayer.h>
+#include <media/AudioResamplerPublic.h>
 #include <media/AudioSystem.h>
+#include <media/AVSyncSettings.h>
+#include <media/IDataSource.h>
 
 #include <binder/MemoryBase.h>
 
@@ -59,7 +62,6 @@
     mLoop = false;
     mLeftVolume = mRightVolume = 1.0;
     mVideoWidth = mVideoHeight = 0;
-    mPlaybackRate = 1.0;
     mLockThreadId = 0;
     mAudioSessionId = AudioSystem::newAudioUniqueId();
     AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
@@ -195,6 +197,22 @@
     return err;
 }
 
+status_t MediaPlayer::setDataSource(const sp<IDataSource> &source)
+{
+    ALOGV("setDataSource(IDataSource)");
+    status_t err = UNKNOWN_ERROR;
+    const sp<IMediaPlayerService>& service(getMediaPlayerService());
+    if (service != 0) {
+        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
+        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+            (NO_ERROR != player->setDataSource(source))) {
+            player.clear();
+        }
+        err = attachNewPlayer(player);
+    }
+    return err;
+}
+
 status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply)
 {
     Mutex::Autolock _l(mLock);
@@ -372,6 +390,9 @@
         if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) {
             ALOGE("internal/external state mismatch corrected");
             mCurrentState = MEDIA_PLAYER_PAUSED;
+        } else if ((mCurrentState & MEDIA_PLAYER_PAUSED) && temp) {
+            ALOGE("internal/external state mismatch corrected");
+            mCurrentState = MEDIA_PLAYER_STARTED;
         }
         return temp;
     }
@@ -379,22 +400,50 @@
     return false;
 }
 
-status_t MediaPlayer::setPlaybackRate(float rate)
+status_t MediaPlayer::setPlaybackSettings(const AudioPlaybackRate& rate)
 {
-    ALOGV("setPlaybackRate: %f", rate);
-    if (rate <= 0.0) {
+    ALOGV("setPlaybackSettings: %f %f %d %d",
+            rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+    // Negative speed and pitch does not make sense. Further validation will
+    // be done by the respective mediaplayers.
+    if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
         return BAD_VALUE;
     }
     Mutex::Autolock _l(mLock);
-    if (mPlayer != 0) {
-        if (mPlaybackRate == rate) {
-            return NO_ERROR;
+    if (mPlayer == 0) return INVALID_OPERATION;
+    status_t err = mPlayer->setPlaybackSettings(rate);
+    if (err == OK) {
+        if (rate.mSpeed == 0.f && mCurrentState == MEDIA_PLAYER_STARTED) {
+            mCurrentState = MEDIA_PLAYER_PAUSED;
+        } else if (rate.mSpeed != 0.f && mCurrentState == MEDIA_PLAYER_PAUSED) {
+            mCurrentState = MEDIA_PLAYER_STARTED;
         }
-        mPlaybackRate = rate;
-        return mPlayer->setPlaybackRate(rate);
     }
-    ALOGV("setPlaybackRate: no active player");
-    return INVALID_OPERATION;
+    return err;
+}
+
+status_t MediaPlayer::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPlayer == 0) return INVALID_OPERATION;
+    return mPlayer->getPlaybackSettings(rate);
+}
+
+status_t MediaPlayer::setSyncSettings(const AVSyncSettings& sync, float videoFpsHint)
+{
+    ALOGV("setSyncSettings: %u %u %f %f",
+            sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
+    Mutex::Autolock _l(mLock);
+    if (mPlayer == 0) return INVALID_OPERATION;
+    return mPlayer->setSyncSettings(sync, videoFpsHint);
+}
+
+status_t MediaPlayer::getSyncSettings(
+        AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPlayer == 0) return INVALID_OPERATION;
+    return mPlayer->getSyncSettings(sync, videoFps);
 }
 
 status_t MediaPlayer::getVideoWidth(int *w)
@@ -840,6 +889,9 @@
     case MEDIA_SUBTITLE_DATA:
         ALOGV("Received subtitle data message");
         break;
+    case MEDIA_META_DATA:
+        ALOGV("Received timed metadata message");
+        break;
     default:
         ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
         break;
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index a2d6e53..8bbd8f1 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -27,6 +27,7 @@
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
 #include <media/mediaplayer.h>  // for MEDIA_ERROR_SERVER_DIED
+#include <media/stagefright/PersistentSurface.h>
 #include <gui/IGraphicBufferProducer.h>
 
 namespace android {
@@ -344,6 +345,24 @@
 
 
 
+status_t MediaRecorder::setInputSurface(const sp<PersistentSurface>& surface)
+{
+    ALOGV("setInputSurface");
+    if (mMediaRecorder == NULL) {
+        ALOGE("media recorder is not initialized yet");
+        return INVALID_OPERATION;
+    }
+    bool isInvalidState = (mCurrentState &
+                           (MEDIA_RECORDER_PREPARED |
+                            MEDIA_RECORDER_RECORDING));
+    if (isInvalidState) {
+        ALOGE("setInputSurface is called in an invalid state: %d", mCurrentState);
+        return INVALID_OPERATION;
+    }
+
+    return mMediaRecorder->setInputSurface(surface->getBufferConsumer());
+}
+
 status_t MediaRecorder::setVideoFrameRate(int frames_per_second)
 {
     ALOGV("setVideoFrameRate(%d)", frames_per_second);
@@ -594,13 +613,13 @@
     return INVALID_OPERATION;
 }
 
-MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)
+MediaRecorder::MediaRecorder(const String16& opPackageName) : mSurfaceMediaSource(NULL)
 {
     ALOGV("constructor");
 
     const sp<IMediaPlayerService>& service(getMediaPlayerService());
     if (service != NULL) {
-        mMediaRecorder = service->createMediaRecorder();
+        mMediaRecorder = service->createMediaRecorder(opPackageName);
     }
     if (mMediaRecorder != NULL) {
         mCurrentState = MEDIA_RECORDER_IDLE;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 4b31715..2c4e719 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -54,6 +54,9 @@
     $(TOP)/frameworks/native/include/media/openmax                  \
     $(TOP)/external/tremolo/Tremolo                                 \
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 LOCAL_MODULE:= libmediaplayerservice
 
 LOCAL_32_BIT_ONLY := true
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
index 8ee7c0b..147d35f 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>
@@ -88,7 +89,7 @@
 
     // first check cache
     Vector<uint8_t> uuidVector;
-    uuidVector.appendArray(uuid, sizeof(uuid));
+    uuidVector.appendArray(uuid, sizeof(uuid[0]) * 16);
     ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
     if (index >= 0) {
         if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
@@ -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..a7f6f8b 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -52,6 +52,7 @@
 KeyedVector<Vector<uint8_t>, String8> Drm::mUUIDToLibraryPathMap;
 KeyedVector<String8, wp<SharedLibrary> > Drm::mLibraryPathToOpenLibraryMap;
 Mutex Drm::mMapLock;
+Mutex Drm::mLock;
 
 static bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
     if (lhs.size() < rhs.size()) {
@@ -136,25 +137,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
@@ -177,7 +210,7 @@
 
     // first check cache
     Vector<uint8_t> uuidVector;
-    uuidVector.appendArray(uuid, sizeof(uuid));
+    uuidVector.appendArray(uuid, sizeof(uuid[0]) * 16);
     ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
     if (index >= 0) {
         if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
@@ -325,7 +358,18 @@
     status_t err = mPlugin->openSession(sessionId);
     if (err == ERROR_DRM_RESOURCE_BUSY) {
         bool retry = false;
+        mLock.unlock();
+        // reclaimSession may call back to closeSession, since mLock is shared between Drm
+        // instances, we should unlock here to avoid deadlock.
         retry = DrmSessionManager::Instance()->reclaimSession(getCallingPid());
+        mLock.lock();
+        if (mInitCheck != OK) {
+            return mInitCheck;
+        }
+
+        if (mPlugin == NULL) {
+            return -EINVAL;
+        }
         if (retry) {
             err = mPlugin->openSession(sessionId);
         }
@@ -744,7 +788,7 @@
     return mPlugin->signRSA(sessionId, algorithm, message, wrappedKey, signature);
 }
 
-void Drm::binderDied(const wp<IBinder> &the_late_who)
+void Drm::binderDied(const wp<IBinder> &the_late_who __unused)
 {
     mEventLock.lock();
     mListener.clear();
@@ -756,4 +800,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..056723c 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -26,8 +26,8 @@
 
 namespace android {
 
-struct DrmFactory;
-struct DrmPlugin;
+class DrmFactory;
+class DrmPlugin;
 struct DrmSessionClientInterface;
 
 struct Drm : public BnDrm,
@@ -133,10 +133,17 @@
                            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:
-    mutable Mutex mLock;
+    static Mutex mLock;
 
     status_t mInitCheck;
 
@@ -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/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 48884b9..ca33aed 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -131,6 +131,11 @@
     GET_PLAYER_TYPE_IMPL(client, source);
 }
 
+player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
+                                              const sp<DataSource> &source) {
+    GET_PLAYER_TYPE_IMPL(client, source);
+}
+
 #undef GET_PLAYER_TYPE_IMPL
 
 sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
@@ -273,6 +278,13 @@
         return 1.0;
     }
 
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+                               const sp<DataSource>& /*source*/,
+                               float /*curScore*/) {
+        // Only NuPlayer supports setting a DataSource source directly.
+        return 1.0;
+    }
+
     virtual sp<MediaPlayerBase> createPlayer() {
         ALOGV(" create NuPlayer");
         return new NuPlayerDriver;
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.h b/media/libmediaplayerservice/MediaPlayerFactory.h
index 55ff918..7f9b3b5 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.h
+++ b/media/libmediaplayerservice/MediaPlayerFactory.h
@@ -43,6 +43,10 @@
                                    const sp<IStreamSource> &/*source*/,
                                    float /*curScore*/) { return 0.0; }
 
+        virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+                                   const sp<DataSource> &/*source*/,
+                                   float /*curScore*/) { return 0.0; }
+
         virtual sp<MediaPlayerBase> createPlayer() = 0;
     };
 
@@ -57,6 +61,8 @@
                                      int64_t length);
     static player_type getPlayerType(const sp<IMediaPlayer>& client,
                                      const sp<IStreamSource> &source);
+    static player_type getPlayerType(const sp<IMediaPlayer>& client,
+                                     const sp<DataSource> &source);
 
     static sp<MediaPlayerBase> createPlayer(player_type playerType,
                                             void* cookie,
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index f113e21..4fc6fa3 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -307,10 +307,10 @@
     ALOGV("MediaPlayerService destroyed");
 }
 
-sp<IMediaRecorder> MediaPlayerService::createMediaRecorder()
+sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 &opPackageName)
 {
     pid_t pid = IPCThreadState::self()->getCallingPid();
-    sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);
+    sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
     wp<MediaRecorderClient> w = recorder;
     Mutex::Autolock lock(mLock);
     mMediaRecorderClients.add(w);
@@ -381,12 +381,13 @@
 }
 
 sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
+        const String16 &opPackageName,
         const sp<IRemoteDisplayClient>& client, const String8& iface) {
     if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
         return NULL;
     }
 
-    return new RemoteDisplay(client, iface.string());
+    return new RemoteDisplay(opPackageName, client, iface.string());
 }
 
 status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& args) const
@@ -413,7 +414,7 @@
     return NO_ERROR;
 }
 
-status_t MediaPlayerService::Client::dump(int fd, const Vector<String16>& args) const
+status_t MediaPlayerService::Client::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
@@ -746,7 +747,7 @@
         return UNKNOWN_ERROR;
     }
 
-    ALOGV("st_dev  = %llu", sb.st_dev);
+    ALOGV("st_dev  = %llu", static_cast<uint64_t>(sb.st_dev));
     ALOGV("st_mode = %u", sb.st_mode);
     ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
     ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
@@ -754,7 +755,6 @@
 
     if (offset >= sb.st_size) {
         ALOGE("offset error");
-        ::close(fd);
         return UNKNOWN_ERROR;
     }
     if (offset + length > sb.st_size) {
@@ -790,6 +790,19 @@
     return mStatus;
 }
 
+status_t MediaPlayerService::Client::setDataSource(
+        const sp<IDataSource> &source) {
+    sp<DataSource> dataSource = DataSource::CreateFromIDataSource(source);
+    player_type playerType = MediaPlayerFactory::getPlayerType(this, dataSource);
+    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
+    if (p == NULL) {
+        return NO_INIT;
+    }
+    // now set data source
+    setDataSource_post(p, p->setDataSource(dataSource));
+    return mStatus;
+}
+
 void MediaPlayerService::Client::disconnectNativeWindow() {
     if (mConnectedWindow != NULL) {
         status_t err = native_window_api_disconnect(mConnectedWindow.get(),
@@ -967,12 +980,52 @@
     return NO_ERROR;
 }
 
-status_t MediaPlayerService::Client::setPlaybackRate(float rate)
+status_t MediaPlayerService::Client::setPlaybackSettings(const AudioPlaybackRate& rate)
 {
-    ALOGV("[%d] setPlaybackRate(%f)", mConnId, rate);
+    ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
+            mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
     sp<MediaPlayerBase> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
-    return p->setPlaybackRate(rate);
+    return p->setPlaybackSettings(rate);
+}
+
+status_t MediaPlayerService::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
+{
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    status_t ret = p->getPlaybackSettings(rate);
+    if (ret == NO_ERROR) {
+        ALOGV("[%d] getPlaybackSettings(%f, %f, %d, %d)",
+                mConnId, rate->mSpeed, rate->mPitch, rate->mFallbackMode, rate->mStretchMode);
+    } else {
+        ALOGV("[%d] getPlaybackSettings returned %d", mConnId, ret);
+    }
+    return ret;
+}
+
+status_t MediaPlayerService::Client::setSyncSettings(
+        const AVSyncSettings& sync, float videoFpsHint)
+{
+    ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
+            mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    return p->setSyncSettings(sync, videoFpsHint);
+}
+
+status_t MediaPlayerService::Client::getSyncSettings(
+        AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
+{
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    status_t ret = p->getSyncSettings(sync, videoFps);
+    if (ret == NO_ERROR) {
+        ALOGV("[%d] getSyncSettings(%u, %u, %f, %f)",
+                mConnId, sync->mSource, sync->mAudioAdjustMode, sync->mTolerance, *videoFps);
+    } else {
+        ALOGV("[%d] getSyncSettings returned %d", mConnId, ret);
+    }
+    return ret;
 }
 
 status_t MediaPlayerService::Client::getCurrentPosition(int *msec)
@@ -1297,7 +1350,7 @@
     mStreamType = AUDIO_STREAM_MUSIC;
     mLeftVolume = 1.0;
     mRightVolume = 1.0;
-    mPlaybackRatePermille = 1000;
+    mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
     mSampleRateHz = 0;
     mMsecsPerFrame = 0;
     mAuxEffectId = 0;
@@ -1448,8 +1501,6 @@
     }
     ALOGV("open(%u, %d, 0x%x, 0x%x, %d, %d 0x%x)", sampleRate, channelCount, channelMask,
                 format, bufferCount, mSessionId, flags);
-    uint32_t afSampleRate;
-    size_t afFrameCount;
     size_t frameCount;
 
     // offloading is only supported in callback mode for now.
@@ -1622,7 +1673,7 @@
 
     mSampleRateHz = sampleRate;
     mFlags = flags;
-    mMsecsPerFrame = mPlaybackRatePermille / (float) sampleRate;
+    mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
     uint32_t pos;
     if (t->getPosition(&pos) == OK) {
         mBytesWritten = uint64_t(pos) * t->frameSize();
@@ -1631,7 +1682,7 @@
 
     status_t res = NO_ERROR;
     if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) {
-        res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000);
+        res = t->setPlaybackRate(mPlaybackRate);
         if (res == NO_ERROR) {
             t->setAuxEffectSendLevel(mSendLevel);
             res = t->attachAuxEffect(mAuxEffectId);
@@ -1727,22 +1778,38 @@
     }
 }
 
-status_t MediaPlayerService::AudioOutput::setPlaybackRatePermille(int32_t ratePermille)
+status_t MediaPlayerService::AudioOutput::setPlaybackRate(const AudioPlaybackRate &rate)
 {
-    ALOGV("setPlaybackRatePermille(%d)", ratePermille);
-    status_t res = NO_ERROR;
-    if (mTrack != 0) {
-        res = mTrack->setSampleRate(ratePermille * mSampleRateHz / 1000);
-    } else {
-        res = NO_INIT;
+    ALOGV("setPlaybackRate(%f %f %d %d)",
+                rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+    if (mTrack == 0) {
+        // remember rate so that we can set it when the track is opened
+        mPlaybackRate = rate;
+        return OK;
     }
-    mPlaybackRatePermille = ratePermille;
+    status_t res = mTrack->setPlaybackRate(rate);
+    if (res != NO_ERROR) {
+        return res;
+    }
+    // rate.mSpeed is always greater than 0 if setPlaybackRate succeeded
+    CHECK_GT(rate.mSpeed, 0.f);
+    mPlaybackRate = rate;
     if (mSampleRateHz != 0) {
-        mMsecsPerFrame = mPlaybackRatePermille / (float) mSampleRateHz;
+        mMsecsPerFrame = 1E3f / (rate.mSpeed * mSampleRateHz);
     }
     return res;
 }
 
+status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
+{
+    ALOGV("setPlaybackRate");
+    if (mTrack == 0) {
+        return NO_INIT;
+    }
+    *rate = mTrack->getPlaybackRate();
+    return NO_ERROR;
+}
+
 status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
 {
     ALOGV("setAuxEffectSendLevel(%f)", level);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 4ce4b81..5103841 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -34,7 +34,10 @@
 
 namespace android {
 
+struct AudioPlaybackRate;
 class AudioTrack;
+struct AVSyncSettings;
+class IDataSource;
 class IMediaRecorder;
 class IMediaMetadataRetriever;
 class IOMX;
@@ -108,7 +111,9 @@
                 void            setAudioAttributes(const audio_attributes_t * attributes);
 
                 void            setVolume(float left, float right);
-        virtual status_t        setPlaybackRatePermille(int32_t ratePermille);
+        virtual status_t        setPlaybackRate(const AudioPlaybackRate& rate);
+        virtual status_t        getPlaybackRate(AudioPlaybackRate* rate /* nonnull */);
+
                 status_t        setAuxEffectSendLevel(float level);
                 status_t        attachAuxEffect(int effectId);
         virtual status_t        dump(int fd, const Vector<String16>& args) const;
@@ -138,7 +143,7 @@
         const audio_attributes_t *mAttributes;
         float                   mLeftVolume;
         float                   mRightVolume;
-        int32_t                 mPlaybackRatePermille;
+        AudioPlaybackRate       mPlaybackRate;
         uint32_t                mSampleRateHz; // sample rate of the content, as set in open()
         float                   mMsecsPerFrame;
         int                     mSessionId;
@@ -187,7 +192,7 @@
     static  void                instantiate();
 
     // IMediaPlayerService interface
-    virtual sp<IMediaRecorder>  createMediaRecorder();
+    virtual sp<IMediaRecorder>  createMediaRecorder(const String16 &opPackageName);
     void    removeMediaRecorderClient(wp<MediaRecorderClient> client);
     virtual sp<IMediaMetadataRetriever> createMetadataRetriever();
 
@@ -199,8 +204,8 @@
     virtual sp<IDrm>            makeDrm();
     virtual sp<IHDCP>           makeHDCP(bool createEncryptionModule);
 
-    virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
-            const String8& iface);
+    virtual sp<IRemoteDisplay> listenForRemoteDisplay(const String16 &opPackageName,
+            const sp<IRemoteDisplayClient>& client, const String8& iface);
     virtual status_t            dump(int fd, const Vector<String16>& args);
 
             void                removeClient(wp<Client> client);
@@ -261,7 +266,11 @@
         virtual status_t        stop();
         virtual status_t        pause();
         virtual status_t        isPlaying(bool* state);
-        virtual status_t        setPlaybackRate(float rate);
+        virtual status_t        setPlaybackSettings(const AudioPlaybackRate& rate);
+        virtual status_t        getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
+        virtual status_t        setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);
+        virtual status_t        getSyncSettings(AVSyncSettings* rate /* nonnull */,
+                                                float* videoFps /* nonnull */);
         virtual status_t        seekTo(int msec);
         virtual status_t        getCurrentPosition(int* msec);
         virtual status_t        getDuration(int* msec);
@@ -292,6 +301,8 @@
         virtual status_t        setDataSource(int fd, int64_t offset, int64_t length);
 
         virtual status_t        setDataSource(const sp<IStreamSource> &source);
+        virtual status_t        setDataSource(const sp<IDataSource> &source);
+
 
         sp<MediaPlayerBase>     setDataSource_pre(player_type playerType);
         void                    setDataSource_post(const sp<MediaPlayerBase>& p,
@@ -301,7 +312,7 @@
                                        int ext1, int ext2, const Parcel *obj);
 
                 pid_t           pid() const { return mPid; }
-        virtual status_t        dump(int fd, const Vector<String16>& args) const;
+        virtual status_t        dump(int fd, const Vector<String16>& args);
 
                 int             getAudioSessionId() { return mAudioSessionId; }
 
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 4d4de9b..f761dec 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -55,6 +55,16 @@
     return ok;
 }
 
+status_t MediaRecorderClient::setInputSurface(const sp<IGraphicBufferConsumer>& surface)
+{
+    ALOGV("setInputSurface");
+    Mutex::Autolock lock(mLock);
+    if (mRecorder == NULL) {
+        ALOGE("recorder is not initialized");
+        return NO_INIT;
+    }
+    return mRecorder->setInputSurface(surface);
+}
 
 sp<IGraphicBufferProducer> MediaRecorderClient::querySurfaceMediaSource()
 {
@@ -290,11 +300,12 @@
     return NO_ERROR;
 }
 
-MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid)
+MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid,
+        const String16& opPackageName)
 {
     ALOGV("Client constructor");
     mPid = pid;
-    mRecorder = new StagefrightRecorder;
+    mRecorder = new StagefrightRecorder(opPackageName);
     mMediaPlayerService = service;
 }
 
@@ -325,7 +336,7 @@
     return mRecorder->setClientName(clientName);
 }
 
-status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) const {
+status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) {
     if (mRecorder != NULL) {
         return mRecorder->dump(fd, args);
     }
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index a444b6c..05130d4 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -54,7 +54,8 @@
     virtual     status_t   init();
     virtual     status_t   close();
     virtual     status_t   release();
-    virtual     status_t   dump(int fd, const Vector<String16>& args) const;
+    virtual     status_t   dump(int fd, const Vector<String16>& args);
+    virtual     status_t   setInputSurface(const sp<IGraphicBufferConsumer>& surface);
     virtual     sp<IGraphicBufferProducer> querySurfaceMediaSource();
 
 private:
@@ -62,7 +63,8 @@
 
                            MediaRecorderClient(
                                    const sp<MediaPlayerService>& service,
-                                                               pid_t pid);
+                                                               pid_t pid,
+                                                               const String16& opPackageName);
     virtual                ~MediaRecorderClient();
 
     pid_t                  mPid;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 715cc0c..bec6203 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -34,6 +34,7 @@
 #include <media/IMediaHTTPService.h>
 #include <media/MediaMetadataRetrieverInterface.h>
 #include <media/MediaPlayerInterface.h>
+#include <media/stagefright/DataSource.h>
 #include <private/media/VideoFrame.h>
 #include "MetadataRetrieverClient.h"
 #include "StagefrightMetadataRetriever.h"
@@ -56,7 +57,7 @@
     disconnect();
 }
 
-status_t MetadataRetrieverClient::dump(int fd, const Vector<String16>& /*args*/) const
+status_t MetadataRetrieverClient::dump(int fd, const Vector<String16>& /*args*/)
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
@@ -140,7 +141,7 @@
         ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
         return BAD_VALUE;
     }
-    ALOGV("st_dev  = %llu", sb.st_dev);
+    ALOGV("st_dev  = %llu", static_cast<uint64_t>(sb.st_dev));
     ALOGV("st_mode = %u", sb.st_mode);
     ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
     ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
@@ -148,7 +149,6 @@
 
     if (offset >= sb.st_size) {
         ALOGE("offset (%lld) bigger than file size (%llu)", offset, sb.st_size);
-        ::close(fd);
         return BAD_VALUE;
     }
     if (offset + length > sb.st_size) {
@@ -164,15 +164,30 @@
     ALOGV("player type = %d", playerType);
     sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
     if (p == NULL) {
-        ::close(fd);
         return NO_INIT;
     }
     status_t status = p->setDataSource(fd, offset, length);
     if (status == NO_ERROR) mRetriever = p;
-    ::close(fd);
     return status;
 }
 
+status_t MetadataRetrieverClient::setDataSource(
+        const sp<IDataSource>& source)
+{
+    ALOGV("setDataSource(IDataSource)");
+    Mutex::Autolock lock(mLock);
+
+    sp<DataSource> dataSource = DataSource::CreateFromIDataSource(source);
+    player_type playerType =
+        MediaPlayerFactory::getPlayerType(NULL /* client */, dataSource);
+    ALOGV("player type = %d", playerType);
+    sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
+    if (p == NULL) return NO_INIT;
+    status_t ret = p->setDataSource(dataSource);
+    if (ret == NO_ERROR) mRetriever = p;
+    return ret;
+}
+
 sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option)
 {
     ALOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 9d3fbe9..e71a29e 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -49,11 +49,12 @@
             const KeyedVector<String8, String8> *headers);
 
     virtual status_t                setDataSource(int fd, int64_t offset, int64_t length);
+    virtual status_t                setDataSource(const sp<IDataSource>& source);
     virtual sp<IMemory>             getFrameAtTime(int64_t timeUs, int option);
     virtual sp<IMemory>             extractAlbumArt();
     virtual const char*             extractMetadata(int keyCode);
 
-    virtual status_t                dump(int fd, const Vector<String16>& args) const;
+    virtual status_t                dump(int fd, const Vector<String16>& args);
 
 private:
     friend class MediaPlayerService;
diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp
index eb959b4..0eb4b5d 100644
--- a/media/libmediaplayerservice/RemoteDisplay.cpp
+++ b/media/libmediaplayerservice/RemoteDisplay.cpp
@@ -26,13 +26,14 @@
 namespace android {
 
 RemoteDisplay::RemoteDisplay(
+        const String16 &opPackageName,
         const sp<IRemoteDisplayClient> &client,
         const char *iface)
     : mLooper(new ALooper),
       mNetSession(new ANetworkSession) {
     mLooper->setName("wfd_looper");
 
-    mSource = new WifiDisplaySource(mNetSession, client);
+    mSource = new WifiDisplaySource(opPackageName, mNetSession, client);
     mLooper->registerHandler(mSource);
 
     mNetSession->start();
diff --git a/media/libmediaplayerservice/RemoteDisplay.h b/media/libmediaplayerservice/RemoteDisplay.h
index 82a0116..d4573e9 100644
--- a/media/libmediaplayerservice/RemoteDisplay.h
+++ b/media/libmediaplayerservice/RemoteDisplay.h
@@ -28,11 +28,12 @@
 
 struct ALooper;
 struct ANetworkSession;
-struct IRemoteDisplayClient;
+class IRemoteDisplayClient;
 struct WifiDisplaySource;
 
 struct RemoteDisplay : public BnRemoteDisplay {
     RemoteDisplay(
+            const String16 &opPackageName,
             const sp<IRemoteDisplayClient> &client,
             const char *iface);
 
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index b37aee3..8fc4b29 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -64,7 +64,7 @@
 // the method returns, if you want to keep it, dup it!
 status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
     ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
-    return mPlayer->setDataSource(dup(fd), offset, length);
+    return mPlayer->setDataSource(fd, offset, length);
 }
 
 status_t StagefrightPlayer::setDataSource(const sp<IStreamSource> &source) {
@@ -188,6 +188,14 @@
     return mPlayer->getParameter(key, reply);
 }
 
+status_t StagefrightPlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+    return mPlayer->setPlaybackSettings(rate);
+}
+
+status_t StagefrightPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+    return mPlayer->getPlaybackSettings(rate);
+}
+
 status_t StagefrightPlayer::getMetadata(
         const media::Metadata::Filter& /* ids */, Parcel *records) {
     using media::Metadata;
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index e6c30ff..96013df 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -60,6 +60,8 @@
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
     virtual status_t setParameter(int key, const Parcel &request);
     virtual status_t getParameter(int key, Parcel *reply);
+    virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+    virtual status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
 
     virtual status_t getMetadata(
             const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 55763f0..e16a4b5 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -69,8 +69,9 @@
 }
 
 
-StagefrightRecorder::StagefrightRecorder()
-    : mWriter(NULL),
+StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName)
+    : MediaRecorderBase(opPackageName),
+      mWriter(NULL),
       mOutputFd(-1),
       mAudioSource(AUDIO_SOURCE_CNT),
       mVideoSource(VIDEO_SOURCE_LIST_END),
@@ -242,6 +243,13 @@
     return OK;
 }
 
+status_t StagefrightRecorder::setInputSurface(
+        const sp<IGraphicBufferConsumer>& surface) {
+    mPersistentSurface = surface;
+
+    return OK;
+}
+
 status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) {
     ALOGV("setOutputFile: %d, %lld, %lld", fd, offset, length);
     // These don't make any sense, do they?
@@ -905,6 +913,7 @@
     sp<AudioSource> audioSource =
         new AudioSource(
                 mAudioSource,
+                mOpPackageName,
                 mSampleRate,
                 mAudioChannels);
 
@@ -916,7 +925,6 @@
     }
 
     sp<AMessage> format = new AMessage;
-    const char *mime;
     switch (mAudioEncoder) {
         case AUDIO_ENCODER_AMR_NB:
         case AUDIO_ENCODER_DEFAULT:
@@ -954,6 +962,7 @@
     if (mAudioTimeScale > 0) {
         format->setInt32("time-scale", mAudioTimeScale);
     }
+    format->setInt32("priority", 0 /* realtime */);
 
     sp<MediaSource> audioEncoder =
             MediaCodecSource::Create(mLooper, format, audioSource);
@@ -1544,6 +1553,11 @@
         format->setInt32("level", mVideoEncoderLevel);
     }
 
+    format->setInt32("priority", 0 /* realtime */);
+    if (mCaptureTimeLapse) {
+        format->setFloat("operating-rate", mCaptureFps);
+    }
+
     uint32_t flags = 0;
     if (mIsMetaDataStoredInVideoBuffers) {
         flags |= MediaCodecSource::FLAG_USE_METADATA_INPUT;
@@ -1553,8 +1567,8 @@
         flags |= MediaCodecSource::FLAG_USE_SURFACE_INPUT;
     }
 
-    sp<MediaCodecSource> encoder =
-            MediaCodecSource::Create(mLooper, format, cameraSource, flags);
+    sp<MediaCodecSource> encoder = MediaCodecSource::Create(
+            mLooper, format, cameraSource, mPersistentSurface, flags);
     if (encoder == NULL) {
         ALOGE("Failed to create video encoder");
         // When the encoder fails to be created, we need
@@ -1738,6 +1752,7 @@
     }
 
     mGraphicBufferProducer.clear();
+    mPersistentSurface.clear();
 
     if (mOutputFd >= 0) {
         ::close(mOutputFd);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index f34c229..7473f42 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -35,12 +35,13 @@
 class MetaData;
 struct AudioSource;
 class MediaProfiles;
+class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
 class SurfaceMediaSource;
-class ALooper;
+struct ALooper;
 
 struct StagefrightRecorder : public MediaRecorderBase {
-    StagefrightRecorder();
+    StagefrightRecorder(const String16 &opPackageName);
     virtual ~StagefrightRecorder();
 
     virtual status_t init();
@@ -53,6 +54,7 @@
     virtual status_t setVideoFrameRate(int frames_per_second);
     virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy);
     virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface);
+    virtual status_t setInputSurface(const sp<IGraphicBufferConsumer>& surface);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
     virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
@@ -72,6 +74,7 @@
     sp<ICamera> mCamera;
     sp<ICameraRecordingProxy> mCameraProxy;
     sp<IGraphicBufferProducer> mPreviewSurface;
+    sp<IGraphicBufferConsumer> mPersistentSurface;
     sp<IMediaRecorderClient> mListener;
     String16 mClientName;
     uid_t mClientUid;
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.h b/media/libmediaplayerservice/VideoFrameScheduler.h
index 84b27b4..b1765c9 100644
--- a/media/libmediaplayerservice/VideoFrameScheduler.h
+++ b/media/libmediaplayerservice/VideoFrameScheduler.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-struct ISurfaceComposer;
+class ISurfaceComposer;
 
 struct VideoFrameScheduler : public RefBase {
     VideoFrameScheduler();
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index 6609874..20193c3 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -16,6 +16,7 @@
         StreamingSource.cpp             \
 
 LOCAL_C_INCLUDES := \
+	$(TOP)/frameworks/av/media/libstagefright                     \
 	$(TOP)/frameworks/av/media/libstagefright/httplive            \
 	$(TOP)/frameworks/av/media/libstagefright/include             \
 	$(TOP)/frameworks/av/media/libstagefright/mpeg2ts             \
@@ -24,6 +25,9 @@
 	$(TOP)/frameworks/av/media/libmediaplayerservice              \
 	$(TOP)/frameworks/native/include/media/openmax
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 LOCAL_MODULE:= libstagefright_nuplayer
 
 LOCAL_MODULE_TAGS := eng
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 5a31b74..5e7b644 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -124,6 +124,12 @@
     return OK;
 }
 
+status_t NuPlayer::GenericSource::setDataSource(const sp<DataSource>& source) {
+    resetDataSource();
+    mDataSource = source;
+    return OK;
+}
+
 sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const {
     return mFileMeta;
 }
@@ -377,20 +383,20 @@
             notifyPreparedAndCleanup(UNKNOWN_ERROR);
             return;
         }
-
-        if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
-            mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
-        }
-
-        // For widevine or other cached streaming cases, we need to wait for
-        // enough buffering before reporting prepared.
-        // Note that even when URL doesn't start with widevine://, mIsWidevine
-        // could still be set to true later, if the streaming or file source
-        // is sniffed to be widevine. We don't want to buffer for file source
-        // in that case, so must check the flag now.
-        mIsStreaming = (mIsWidevine || mCachedSource != NULL);
     }
 
+    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+        mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
+    }
+
+    // For widevine or other cached streaming cases, we need to wait for
+    // enough buffering before reporting prepared.
+    // Note that even when URL doesn't start with widevine://, mIsWidevine
+    // could still be set to true later, if the streaming or file source
+    // is sniffed to be widevine. We don't want to buffer for file source
+    // in that case, so must check the flag now.
+    mIsStreaming = (mIsWidevine || mCachedSource != NULL);
+
     // init extractor from data source
     status_t err = initFromDataSource();
 
@@ -697,7 +703,7 @@
             stopBufferingIfNecessary();
         }
     } else if (cachedDataRemaining >= 0) {
-        ALOGV("onPollBuffering: cachedDataRemaining %d bytes",
+        ALOGV("onPollBuffering: cachedDataRemaining %zd bytes",
                 cachedDataRemaining);
 
         if (cachedDataRemaining < kLowWaterMarkBytes) {
@@ -784,7 +790,7 @@
           }
           readBuffer(trackType, timeUs, &actualTimeUs, formatChange);
           readBuffer(counterpartType, -1, NULL, formatChange);
-          ALOGV("timeUs %lld actualTimeUs %lld", timeUs, actualTimeUs);
+          ALOGV("timeUs %lld actualTimeUs %lld", (long long)timeUs, (long long)actualTimeUs);
 
           break;
       }
@@ -1059,6 +1065,7 @@
 
     const char *mime;
     CHECK(meta->findCString(kKeyMIMEType, &mime));
+    format->setString("mime", mime);
 
     int32_t trackType;
     if (!strncasecmp(mime, "video/", 6)) {
@@ -1079,8 +1086,6 @@
     format->setString("language", lang);
 
     if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-        format->setString("mime", mime);
-
         int32_t isAutoselect = 1, isDefault = 0, isForced = 0;
         meta->findInt32(kKeyTrackIsAutoselect, &isAutoselect);
         meta->findInt32(kKeyTrackIsDefault, &isDefault);
@@ -1394,6 +1399,14 @@
         meta->setInt32("trackIndex", mSubtitleTrack.mIndex);
     }
 
+    uint32_t dataType; // unused
+    const void *seiData;
+    size_t seiLength;
+    if (mb->meta_data()->findData(kKeySEI, &dataType, &seiData, &seiLength)) {
+        sp<ABuffer> sei = ABuffer::CreateAsCopy(seiData, seiLength);;
+        meta->setBuffer("sei", sei);
+    }
+
     if (actualTimeUs) {
         *actualTimeUs = timeUs;
     }
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 862ee5f..7fab051 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -31,12 +31,13 @@
 class DrmManagerClient;
 struct AnotherPacketSource;
 struct ARTSPController;
-struct DataSource;
+class DataSource;
+class IDataSource;
 struct IMediaHTTPService;
 struct MediaSource;
 class MediaBuffer;
 struct NuCachedSource2;
-struct WVMExtractor;
+class WVMExtractor;
 
 struct NuPlayer::GenericSource : public NuPlayer::Source {
     GenericSource(const sp<AMessage> &notify, bool uidValid, uid_t uid);
@@ -48,6 +49,8 @@
 
     status_t setDataSource(int fd, int64_t offset, int64_t length);
 
+    status_t setDataSource(const sp<DataSource>& dataSource);
+
     virtual void prepareAsync();
 
     virtual void start();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 0476c9b..39b8d09 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -22,7 +22,6 @@
 
 #include "AnotherPacketSource.h"
 #include "LiveDataSource.h"
-#include "LiveSession.h"
 
 #include <media/IMediaHTTPService.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -30,6 +29,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaDefs.h>
 
 namespace android {
 
@@ -44,7 +44,10 @@
       mFlags(0),
       mFinalResult(OK),
       mOffset(0),
-      mFetchSubtitleDataGeneration(0) {
+      mFetchSubtitleDataGeneration(0),
+      mFetchMetaDataGeneration(0),
+      mHasMetadata(false),
+      mMetadataSelected(false) {
     if (headers) {
         mExtraHeaders = *headers;
 
@@ -142,19 +145,49 @@
 ssize_t NuPlayer::HTTPLiveSource::getSelectedTrack(media_track_type type) const {
     if (mLiveSession == NULL) {
         return -1;
+    } else if (type == MEDIA_TRACK_TYPE_METADATA) {
+        // MEDIA_TRACK_TYPE_METADATA is always last track
+        // mMetadataSelected can only be true when mHasMetadata is true
+        return mMetadataSelected ? (mLiveSession->getTrackCount() - 1) : -1;
     } else {
         return mLiveSession->getSelectedTrack(type);
     }
 }
 
 status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select, int64_t /*timeUs*/) {
-    status_t err = mLiveSession->selectTrack(trackIndex, select);
+    if (mLiveSession == NULL) {
+        return INVALID_OPERATION;
+    }
+
+    status_t err = INVALID_OPERATION;
+    bool postFetchMsg = false, isSub = false;
+    if (trackIndex != mLiveSession->getTrackCount() - 1) {
+        err = mLiveSession->selectTrack(trackIndex, select);
+        postFetchMsg = select;
+        isSub = true;
+    } else {
+        // metadata track
+        if (mHasMetadata) {
+            if (mMetadataSelected && !select) {
+                err = OK;
+            } else if (!mMetadataSelected && select) {
+                postFetchMsg = true;
+                err = OK;
+            } else {
+                err = BAD_VALUE; // behave as LiveSession::selectTrack
+            }
+
+            mMetadataSelected = select;
+        }
+    }
 
     if (err == OK) {
-        mFetchSubtitleDataGeneration++;
-        if (select) {
-            sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
-            msg->setInt32("generation", mFetchSubtitleDataGeneration);
+        int32_t &generation = isSub ? mFetchSubtitleDataGeneration : mFetchMetaDataGeneration;
+        generation++;
+        if (postFetchMsg) {
+            int32_t what = isSub ? kWhatFetchSubtitleData : kWhatFetchMetaData;
+            sp<AMessage> msg = new AMessage(what, this);
+            msg->setInt32("generation", generation);
             msg->post();
         }
     }
@@ -169,6 +202,49 @@
     return mLiveSession->seekTo(seekTimeUs);
 }
 
+void NuPlayer::HTTPLiveSource::pollForRawData(
+        const sp<AMessage> &msg, int32_t currentGeneration,
+        LiveSession::StreamType fetchType, int32_t pushWhat) {
+
+    int32_t generation;
+    CHECK(msg->findInt32("generation", &generation));
+
+    if (generation != currentGeneration) {
+        return;
+    }
+
+    sp<ABuffer> buffer;
+    while (mLiveSession->dequeueAccessUnit(fetchType, &buffer) == OK) {
+
+        sp<AMessage> notify = dupNotify();
+        notify->setInt32("what", pushWhat);
+        notify->setBuffer("buffer", buffer);
+
+        int64_t timeUs, baseUs, delayUs;
+        CHECK(buffer->meta()->findInt64("baseUs", &baseUs));
+        CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+        delayUs = baseUs + timeUs - ALooper::GetNowUs();
+
+        if (fetchType == LiveSession::STREAMTYPE_SUBTITLES) {
+            notify->post();
+            msg->post(delayUs > 0ll ? delayUs : 0ll);
+            return;
+        } else if (fetchType == LiveSession::STREAMTYPE_METADATA) {
+            if (delayUs < -1000000ll) { // 1 second
+                continue;
+            }
+            notify->post();
+            // push all currently available metadata buffers in each invocation of pollForRawData
+            // continue;
+        } else {
+            TRESPASS();
+        }
+    }
+
+    // try again in 1 second
+    msg->post(1000000ll);
+}
+
 void NuPlayer::HTTPLiveSource::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatSessionNotify:
@@ -179,33 +255,24 @@
 
         case kWhatFetchSubtitleData:
         {
-            int32_t generation;
-            CHECK(msg->findInt32("generation", &generation));
+            pollForRawData(
+                    msg, mFetchSubtitleDataGeneration,
+                    /* fetch */ LiveSession::STREAMTYPE_SUBTITLES,
+                    /* push */ kWhatSubtitleData);
 
-            if (generation != mFetchSubtitleDataGeneration) {
-                // stale
+            break;
+        }
+
+        case kWhatFetchMetaData:
+        {
+            if (!mMetadataSelected) {
                 break;
             }
 
-            sp<ABuffer> buffer;
-            if (mLiveSession->dequeueAccessUnit(
-                    LiveSession::STREAMTYPE_SUBTITLES, &buffer) == OK) {
-                sp<AMessage> notify = dupNotify();
-                notify->setInt32("what", kWhatSubtitleData);
-                notify->setBuffer("buffer", buffer);
-                notify->post();
-
-                int64_t timeUs, baseUs, durationUs, delayUs;
-                CHECK(buffer->meta()->findInt64("baseUs", &baseUs));
-                CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-                CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
-                delayUs = baseUs + timeUs - ALooper::GetNowUs();
-
-                msg->post(delayUs > 0ll ? delayUs : 0ll);
-            } else {
-                // try again in 1 second
-                msg->post(1000000ll);
-            }
+            pollForRawData(
+                    msg, mFetchMetaDataGeneration,
+                    /* fetch */ LiveSession::STREAMTYPE_METADATA,
+                    /* push */ kWhatTimedMetaData);
 
             break;
         }
@@ -309,6 +376,19 @@
             break;
         }
 
+        case LiveSession::kWhatMetadataDetected:
+        {
+            if (!mHasMetadata) {
+                mHasMetadata = true;
+
+                sp<AMessage> notify = dupNotify();
+                // notification without buffer triggers MEDIA_INFO_METADATA_UPDATE
+                notify->setInt32("what", kWhatTimedMetaData);
+                notify->post();
+            }
+            break;
+        }
+
         case LiveSession::kWhatError:
         {
             break;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index bbb8981..9e0ec2f 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -21,6 +21,8 @@
 #include "NuPlayer.h"
 #include "NuPlayerSource.h"
 
+#include "LiveSession.h"
+
 namespace android {
 
 struct LiveSession;
@@ -60,6 +62,7 @@
     enum {
         kWhatSessionNotify,
         kWhatFetchSubtitleData,
+        kWhatFetchMetaData,
     };
 
     sp<IMediaHTTPService> mHTTPService;
@@ -71,8 +74,14 @@
     sp<ALooper> mLiveLooper;
     sp<LiveSession> mLiveSession;
     int32_t mFetchSubtitleDataGeneration;
+    int32_t mFetchMetaDataGeneration;
+    bool mHasMetadata;
+    bool mMetadataSelected;
 
     void onSessionNotify(const sp<AMessage> &msg);
+    void pollForRawData(
+            const sp<AMessage> &msg, int32_t currentGeneration,
+            LiveSession::StreamType fetchType, int32_t pushWhat);
 
     DISALLOW_EVIL_CONSTRUCTORS(HTTPLiveSource);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 02d9f32..1fb4365 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -37,6 +37,9 @@
 
 #include <cutils/properties.h>
 
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
+
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -45,7 +48,9 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
 
 #include "avc_utils.h"
 
@@ -96,16 +101,16 @@
 };
 
 struct NuPlayer::SetSurfaceAction : public Action {
-    SetSurfaceAction(const sp<NativeWindowWrapper> &wrapper)
-        : mWrapper(wrapper) {
+    SetSurfaceAction(const sp<Surface> &surface)
+        : mSurface(surface) {
     }
 
     virtual void execute(NuPlayer *player) {
-        player->performSetSurface(mWrapper);
+        player->performSetSurface(mSurface);
     }
 
 private:
-    sp<NativeWindowWrapper> mWrapper;
+    sp<Surface> mSurface;
 
     DISALLOW_EVIL_CONSTRUCTORS(SetSurfaceAction);
 };
@@ -180,10 +185,12 @@
       mFlushingVideo(NONE),
       mResumePending(false),
       mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
-      mPlaybackRate(1.0),
+      mPlaybackSettings(AUDIO_PLAYBACK_RATE_DEFAULT),
+      mVideoFpsHint(-1.f),
       mStarted(false),
       mPaused(false),
-      mPausedByClient(false) {
+      mPausedByClient(false),
+      mPausedForBuffering(false) {
     clearFlushComplete();
 }
 
@@ -285,21 +292,34 @@
     msg->post();
 }
 
+void NuPlayer::setDataSourceAsync(const sp<DataSource> &dataSource) {
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
+    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
+
+    sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID);
+    status_t err = source->setDataSource(dataSource);
+
+    if (err != OK) {
+        ALOGE("Failed to set data source!");
+        source = NULL;
+    }
+
+    msg->setObject("source", source);
+    msg->post();
+}
+
 void NuPlayer::prepareAsync() {
     (new AMessage(kWhatPrepare, this))->post();
 }
 
 void NuPlayer::setVideoSurfaceTextureAsync(
         const sp<IGraphicBufferProducer> &bufferProducer) {
-    sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, this);
+    sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
 
     if (bufferProducer == NULL) {
-        msg->setObject("native-window", NULL);
+        msg->setObject("surface", NULL);
     } else {
-        msg->setObject(
-                "native-window",
-                new NativeWindowWrapper(
-                    new Surface(bufferProducer, true /* controlledByApp */)));
+        msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
     }
 
     msg->post();
@@ -315,10 +335,61 @@
     (new AMessage(kWhatStart, this))->post();
 }
 
-void NuPlayer::setPlaybackRate(float rate) {
-    sp<AMessage> msg = new AMessage(kWhatSetRate, this);
-    msg->setFloat("rate", rate);
-    msg->post();
+status_t NuPlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+    // do some cursory validation of the settings here. audio modes are
+    // only validated when set on the audiosink.
+     if ((rate.mSpeed != 0.f && rate.mSpeed < AUDIO_TIMESTRETCH_SPEED_MIN)
+            || rate.mSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
+            || rate.mPitch < AUDIO_TIMESTRETCH_SPEED_MIN
+            || rate.mPitch > AUDIO_TIMESTRETCH_SPEED_MAX) {
+        return BAD_VALUE;
+    }
+    sp<AMessage> msg = new AMessage(kWhatConfigPlayback, this);
+    writeToAMessage(msg, rate);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+    return err;
+}
+
+status_t NuPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+    sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+        if (err == OK) {
+            readFromAMessage(response, rate);
+        }
+    }
+    return err;
+}
+
+status_t NuPlayer::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+    sp<AMessage> msg = new AMessage(kWhatConfigSync, this);
+    writeToAMessage(msg, sync, videoFpsHint);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+    return err;
+}
+
+status_t NuPlayer::getSyncSettings(
+        AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */) {
+    sp<AMessage> msg = new AMessage(kWhatGetSyncSettings, this);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+        if (err == OK) {
+            readFromAMessage(response, sync, videoFps);
+        }
+    }
+    return err;
 }
 
 void NuPlayer::pause() {
@@ -352,23 +423,35 @@
     int32_t trackType;
     CHECK(format->findInt32("type", &trackType));
 
+    AString mime;
+    if (!format->findString("mime", &mime)) {
+        // Java MediaPlayer only uses mimetype for subtitle and timedtext tracks.
+        // If we can't find the mimetype here it means that we wouldn't be needing
+        // the mimetype on the Java end. We still write a placeholder mime to keep the
+        // (de)serialization logic simple.
+        if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
+            mime = "audio/";
+        } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
+            mime = "video/";
+        } else {
+            TRESPASS();
+        }
+    }
+
     AString lang;
     CHECK(format->findString("language", &lang));
 
     reply->writeInt32(2); // write something non-zero
     reply->writeInt32(trackType);
+    reply->writeString16(String16(mime.c_str()));
     reply->writeString16(String16(lang.c_str()));
 
     if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-        AString mime;
-        CHECK(format->findString("mime", &mime));
-
         int32_t isAuto, isDefault, isForced;
         CHECK(format->findInt32("auto", &isAuto));
         CHECK(format->findInt32("default", &isDefault));
         CHECK(format->findInt32("forced", &isForced));
 
-        reply->writeString16(String16(mime.c_str()));
         reply->writeInt32(isAuto);
         reply->writeInt32(isDefault);
         reply->writeInt32(isForced);
@@ -539,15 +622,15 @@
             break;
         }
 
-        case kWhatSetVideoNativeWindow:
+        case kWhatSetVideoSurface:
         {
-            ALOGV("kWhatSetVideoNativeWindow");
+            ALOGV("kWhatSetVideoSurface");
 
             sp<RefBase> obj;
-            CHECK(msg->findObject("native-window", &obj));
-
+            CHECK(msg->findObject("surface", &obj));
+            sp<Surface> surface = static_cast<Surface *>(obj.get());
             if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL) {
-                performSetSurface(static_cast<NativeWindowWrapper *>(obj.get()));
+                performSetSurface(surface);
                 break;
             }
 
@@ -555,9 +638,7 @@
                     new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
                                            FLUSH_CMD_SHUTDOWN /* video */));
 
-            mDeferredActions.push_back(
-                    new SetSurfaceAction(
-                        static_cast<NativeWindowWrapper *>(obj.get())));
+            mDeferredActions.push_back(new SetSurfaceAction(surface));
 
             if (obj != NULL) {
                 if (mStarted) {
@@ -603,7 +684,10 @@
         {
             ALOGV("kWhatStart");
             if (mStarted) {
-                onResume();
+                // do not resume yet if the source is still buffering
+                if (!mPausedForBuffering) {
+                    onResume();
+                }
             } else {
                 onStart();
             }
@@ -611,13 +695,114 @@
             break;
         }
 
-        case kWhatSetRate:
+        case kWhatConfigPlayback:
         {
-            ALOGV("kWhatSetRate");
-            CHECK(msg->findFloat("rate", &mPlaybackRate));
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            AudioPlaybackRate rate /* sanitized */;
+            readFromAMessage(msg, &rate);
+            status_t err = OK;
             if (mRenderer != NULL) {
-                mRenderer->setPlaybackRate(mPlaybackRate);
+                err = mRenderer->setPlaybackSettings(rate);
             }
+            if (err == OK) {
+                if (rate.mSpeed == 0.f) {
+                    onPause();
+                    // save all other settings (using non-paused speed)
+                    // so we can restore them on start
+                    AudioPlaybackRate newRate = rate;
+                    newRate.mSpeed = mPlaybackSettings.mSpeed;
+                    mPlaybackSettings = newRate;
+                } else { /* rate.mSpeed != 0.f */
+                    onResume();
+                    mPlaybackSettings = rate;
+                }
+            }
+
+            if (mVideoDecoder != NULL) {
+                float rate = getFrameRate();
+                if (rate > 0) {
+                    sp<AMessage> params = new AMessage();
+                    params->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
+                    mVideoDecoder->setParameters(params);
+                }
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatGetPlaybackSettings:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            AudioPlaybackRate rate = mPlaybackSettings;
+            status_t err = OK;
+            if (mRenderer != NULL) {
+                err = mRenderer->getPlaybackSettings(&rate);
+            }
+            if (err == OK) {
+                // get playback settings used by renderer, as it may be
+                // slightly off due to audiosink not taking small changes.
+                mPlaybackSettings = rate;
+                if (mPaused) {
+                    rate.mSpeed = 0.f;
+                }
+            }
+            sp<AMessage> response = new AMessage;
+            if (err == OK) {
+                writeToAMessage(response, rate);
+            }
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatConfigSync:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            ALOGV("kWhatConfigSync");
+            AVSyncSettings sync;
+            float videoFpsHint;
+            readFromAMessage(msg, &sync, &videoFpsHint);
+            status_t err = OK;
+            if (mRenderer != NULL) {
+                err = mRenderer->setSyncSettings(sync, videoFpsHint);
+            }
+            if (err == OK) {
+                mSyncSettings = sync;
+                mVideoFpsHint = videoFpsHint;
+            }
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatGetSyncSettings:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            AVSyncSettings sync = mSyncSettings;
+            float videoFps = mVideoFpsHint;
+            status_t err = OK;
+            if (mRenderer != NULL) {
+                err = mRenderer->getSyncSettings(&sync, &videoFps);
+                if (err == OK) {
+                    mSyncSettings = sync;
+                    mVideoFpsHint = videoFps;
+                }
+            }
+            sp<AMessage> response = new AMessage;
+            if (err == OK) {
+                writeToAMessage(response, sync, videoFps);
+            }
+            response->setInt32("err", err);
+            response->postReply(replyID);
             break;
         }
 
@@ -640,7 +825,7 @@
 
             // initialize video before audio because successful initialization of
             // video may change deep buffer mode of audio.
-            if (mNativeWindow != NULL) {
+            if (mSurface != NULL) {
                 instantiateDecoder(false, &mVideoDecoder);
             }
 
@@ -688,7 +873,7 @@
             }
 
             if ((mAudioDecoder == NULL && mAudioSink != NULL)
-                    || (mVideoDecoder == NULL && mNativeWindow != NULL)) {
+                    || (mVideoDecoder == NULL && mSurface != NULL)) {
                 msg->post(100000ll);
                 mScanSourcesPending = true;
             }
@@ -955,7 +1140,7 @@
             CHECK(msg->findInt32("needNotify", &needNotify));
 
             ALOGV("kWhatSeek seekTimeUs=%lld us, needNotify=%d",
-                    seekTimeUs, needNotify);
+                    (long long)seekTimeUs, needNotify);
 
             mDeferredActions.push_back(
                     new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
@@ -1034,7 +1219,7 @@
 
     // TRICKY: We rely on mRenderer being null, so that decoder does not start requesting
     // data on instantiation.
-    if (mNativeWindow != NULL) {
+    if (mSurface != NULL) {
         err = instantiateDecoder(false, &mVideoDecoder);
         if (err != OK) {
             return err;
@@ -1087,14 +1272,16 @@
     mRendererLooper->setName("NuPlayerRenderer");
     mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
     mRendererLooper->registerHandler(mRenderer);
-    if (mPlaybackRate != 1.0) {
-        mRenderer->setPlaybackRate(mPlaybackRate);
+
+    status_t err = mRenderer->setPlaybackSettings(mPlaybackSettings);
+    if (err != OK) {
+        mSource->stop();
+        notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+        return;
     }
 
-    sp<MetaData> meta = getFileMeta();
-    int32_t rate;
-    if (meta != NULL
-            && meta->findInt32(kKeyFrameRate, &rate) && rate > 0) {
+    float rate = getFrameRate();
+    if (rate > 0) {
         mRenderer->setVideoFrameRate(rate);
     }
 
@@ -1233,6 +1420,8 @@
         return -EWOULDBLOCK;
     }
 
+    format->setInt32("priority", 0 /* realtime */);
+
     if (!audio) {
         AString mime;
         CHECK(format->findString("mime", &mime));
@@ -1249,6 +1438,11 @@
         if (mSourceFlags & Source::FLAG_PROTECTED) {
             format->setInt32("protected", true);
         }
+
+        float rate = getFrameRate();
+        if (rate > 0) {
+            format->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
+        }
     }
 
     if (audio) {
@@ -1269,10 +1463,10 @@
         notify->setInt32("generation", mVideoDecoderGeneration);
 
         *decoder = new Decoder(
-                notify, mSource, mRenderer, mNativeWindow, mCCDecoder);
+                notify, mSource, mRenderer, mSurface, mCCDecoder);
 
         // enable FRC if high-quality AV sync is requested, even if not
-        // queuing to native window, as this will even improve textureview
+        // directly queuing to display, as this will even improve textureview
         // playback.
         {
             char value[PROPERTY_VALUE_MAX];
@@ -1320,8 +1514,6 @@
     }
 
     int32_t displayWidth, displayHeight;
-    int32_t cropLeft, cropTop, cropRight, cropBottom;
-
     if (outputFormat != NULL) {
         int32_t width, height;
         CHECK(outputFormat->findInt32("width", &width));
@@ -1403,7 +1595,11 @@
 
     // Make sure we don't continue to scan sources until we finish flushing.
     ++mScanSourcesGeneration;
-    mScanSourcesPending = false;
+    if (mScanSourcesPending) {
+        mDeferredActions.push_back(
+                new SimpleAction(&NuPlayer::performScanSources));
+        mScanSourcesPending = false;
+    }
 
     decoder->signalFlush();
 
@@ -1442,9 +1638,8 @@
 
 status_t NuPlayer::setVideoScalingMode(int32_t mode) {
     mVideoScalingMode = mode;
-    if (mNativeWindow != NULL) {
-        status_t ret = native_window_set_scaling_mode(
-                mNativeWindow->getNativeWindow().get(), mVideoScalingMode);
+    if (mSurface != NULL) {
+        status_t ret = native_window_set_scaling_mode(mSurface.get(), mVideoScalingMode);
         if (ret != OK) {
             ALOGE("Failed to set scaling mode (%d): %s",
                 -ret, strerror(-ret));
@@ -1519,6 +1714,28 @@
     return mSource->getFileFormatMeta();
 }
 
+float NuPlayer::getFrameRate() {
+    sp<MetaData> meta = mSource->getFormatMeta(false /* audio */);
+    if (meta == NULL) {
+        return 0;
+    }
+    int32_t rate;
+    if (!meta->findInt32(kKeyFrameRate, &rate)) {
+        // fall back to try file meta
+        sp<MetaData> fileMeta = getFileMeta();
+        if (fileMeta == NULL) {
+            ALOGW("source has video meta but not file meta");
+            return -1;
+        }
+        int32_t fileMetaRate;
+        if (!fileMeta->findInt32(kKeyFrameRate, &fileMetaRate)) {
+            return -1;
+        }
+        return fileMetaRate;
+    }
+    return rate;
+}
+
 void NuPlayer::schedulePollDuration() {
     sp<AMessage> msg = new AMessage(kWhatPollDuration, this);
     msg->setInt32("generation", mPollDurationGeneration);
@@ -1554,7 +1771,7 @@
 
 void NuPlayer::performSeek(int64_t seekTimeUs, bool needNotify) {
     ALOGV("performSeek seekTimeUs=%lld us (%.2f secs), needNotify(%d)",
-          seekTimeUs,
+          (long long)seekTimeUs,
           seekTimeUs / 1E6,
           needNotify);
 
@@ -1638,10 +1855,10 @@
     }
 }
 
-void NuPlayer::performSetSurface(const sp<NativeWindowWrapper> &wrapper) {
+void NuPlayer::performSetSurface(const sp<Surface> &surface) {
     ALOGV("performSetSurface");
 
-    mNativeWindow = wrapper;
+    mSurface = surface;
 
     // XXX - ignore error from setVideoScalingMode for now
     setVideoScalingMode(mVideoScalingMode);
@@ -1794,9 +2011,10 @@
         case Source::kWhatPauseOnBufferingStart:
         {
             // ignore if not playing
-            if (mStarted && !mPausedByClient) {
+            if (mStarted) {
                 ALOGI("buffer low, pausing...");
 
+                mPausedForBuffering = true;
                 onPause();
             }
             // fall-thru
@@ -1811,10 +2029,15 @@
         case Source::kWhatResumeOnBufferingEnd:
         {
             // ignore if not playing
-            if (mStarted && !mPausedByClient) {
+            if (mStarted) {
                 ALOGI("buffer ready, resuming...");
 
-                onResume();
+                mPausedForBuffering = false;
+
+                // do not resume yet if client didn't unpause
+                if (!mPausedByClient) {
+                    onResume();
+                }
             }
             // fall-thru
         }
@@ -1843,6 +2066,17 @@
             break;
         }
 
+        case Source::kWhatTimedMetaData:
+        {
+            sp<ABuffer> buffer;
+            if (!msg->findBuffer("buffer", &buffer)) {
+                notifyListener(MEDIA_INFO, MEDIA_INFO_METADATA_UPDATE, 0);
+            } else {
+                sendTimedMetaData(buffer);
+            }
+            break;
+        }
+
         case Source::kWhatTimedTextData:
         {
             int32_t generation;
@@ -1951,6 +2185,19 @@
     notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in);
 }
 
+void NuPlayer::sendTimedMetaData(const sp<ABuffer> &buffer) {
+    int64_t timeUs;
+    CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+    Parcel in;
+    in.writeInt64(timeUs);
+    in.writeInt32(buffer->size());
+    in.writeInt32(buffer->size());
+    in.write(buffer->data(), buffer->size());
+
+    notifyListener(MEDIA_META_DATA, 0, 0, &in);
+}
+
 void NuPlayer::sendTimedTextData(const sp<ABuffer> &buffer) {
     const void *data;
     size_t size = 0;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 2bc20d7..df9debc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -18,14 +18,17 @@
 
 #define NU_PLAYER_H_
 
+#include <media/AudioResamplerPublic.h>
 #include <media/MediaPlayerInterface.h>
 #include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/NativeWindowWrapper.h>
 
 namespace android {
 
 struct ABuffer;
 struct AMessage;
+struct AudioPlaybackRate;
+struct AVSyncSettings;
+class IDataSource;
 class MetaData;
 struct NuPlayerDriver;
 
@@ -45,13 +48,19 @@
 
     void setDataSourceAsync(int fd, int64_t offset, int64_t length);
 
+    void setDataSourceAsync(const sp<DataSource> &source);
+
     void prepareAsync();
 
     void setVideoSurfaceTextureAsync(
             const sp<IGraphicBufferProducer> &bufferProducer);
 
     void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
-    void setPlaybackRate(float rate);
+    status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+    status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+    status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
+    status_t getSyncSettings(AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
+
     void start();
 
     void pause();
@@ -71,6 +80,7 @@
     void getStats(int64_t *mNumFramesTotal, int64_t *mNumFramesDropped);
 
     sp<MetaData> getFileMeta();
+    float getFrameRate();
 
 protected:
     virtual ~NuPlayer();
@@ -102,10 +112,13 @@
     enum {
         kWhatSetDataSource              = '=DaS',
         kWhatPrepare                    = 'prep',
-        kWhatSetVideoNativeWindow       = '=NaW',
+        kWhatSetVideoSurface            = '=VSu',
         kWhatSetAudioSink               = '=AuS',
         kWhatMoreDataQueued             = 'more',
-        kWhatSetRate                    = 'setR',
+        kWhatConfigPlayback             = 'cfPB',
+        kWhatConfigSync                 = 'cfSy',
+        kWhatGetPlaybackSettings        = 'gPbS',
+        kWhatGetSyncSettings            = 'gSyS',
         kWhatStart                      = 'strt',
         kWhatScanSources                = 'scan',
         kWhatVideoNotify                = 'vidN',
@@ -128,7 +141,7 @@
     uid_t mUID;
     sp<Source> mSource;
     uint32_t mSourceFlags;
-    sp<NativeWindowWrapper> mNativeWindow;
+    sp<Surface> mSurface;
     sp<MediaPlayerBase::AudioSink> mAudioSink;
     sp<DecoderBase> mVideoDecoder;
     bool mOffloadAudio;
@@ -177,7 +190,9 @@
 
     int32_t mVideoScalingMode;
 
-    float mPlaybackRate;
+    AudioPlaybackRate mPlaybackSettings;
+    AVSyncSettings mSyncSettings;
+    float mVideoFpsHint;
     bool mStarted;
 
     // Actual pause state, either as requested by client or due to buffering.
@@ -188,6 +203,9 @@
     // still become true, when we pause internally due to buffering.
     bool mPausedByClient;
 
+    // Pause state as requested by source (internally) due to buffering
+    bool mPausedForBuffering;
+
     inline const sp<DecoderBase> &getDecoder(bool audio) {
         return audio ? mAudioDecoder : mVideoDecoder;
     }
@@ -236,7 +254,7 @@
     void performDecoderFlush(FlushCommand audio, FlushCommand video);
     void performReset();
     void performScanSources();
-    void performSetSurface(const sp<NativeWindowWrapper> &wrapper);
+    void performSetSurface(const sp<Surface> &wrapper);
     void performResumeDecoders(bool needNotify);
 
     void onSourceNotify(const sp<AMessage> &msg);
@@ -246,6 +264,7 @@
             bool audio, bool video, const sp<AMessage> &reply);
 
     void sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex);
+    void sendTimedMetaData(const sp<ABuffer> &buffer);
     void sendTimedTextData(const sp<ABuffer> &buffer);
 
     void writeTrackInfo(Parcel* reply, const sp<AMessage> format) const;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index cf3e8ad..ac3c6b6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -51,6 +51,7 @@
     return cc->mData1 < 0x10 && cc->mData2 < 0x10;
 }
 
+static void dumpBytePair(const sp<ABuffer> &ccBuf) __attribute__ ((unused));
 static void dumpBytePair(const sp<ABuffer> &ccBuf) {
     size_t offset = 0;
     AString out;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 04ac699..376c93a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -33,6 +33,8 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 
+#include <gui/Surface.h>
+
 #include "avc_utils.h"
 #include "ATSParser.h"
 
@@ -42,10 +44,10 @@
         const sp<AMessage> &notify,
         const sp<Source> &source,
         const sp<Renderer> &renderer,
-        const sp<NativeWindowWrapper> &nativeWindow,
+        const sp<Surface> &surface,
         const sp<CCDecoder> &ccDecoder)
     : DecoderBase(notify),
-      mNativeWindow(nativeWindow),
+      mSurface(surface),
       mSource(source),
       mRenderer(renderer),
       mCCDecoder(ccDecoder),
@@ -82,25 +84,71 @@
     switch (msg->what()) {
         case kWhatCodecNotify:
         {
-            if (!isStaleReply(msg)) {
-                int32_t numInput, numOutput;
+            int32_t cbID;
+            CHECK(msg->findInt32("callbackID", &cbID));
 
-                if (!msg->findInt32("input-buffers", &numInput)) {
-                    numInput = INT32_MAX;
-                }
+            ALOGV("[%s] kWhatCodecNotify: cbID = %d, paused = %d",
+                    mIsAudio ? "audio" : "video", cbID, mPaused);
 
-                if (!msg->findInt32("output-buffers", &numOutput)) {
-                    numOutput = INT32_MAX;
-                }
-
-                if (!mPaused) {
-                    while (numInput-- > 0 && handleAnInputBuffer()) {}
-                }
-
-                while (numOutput-- > 0 && handleAnOutputBuffer()) {}
+            if (mPaused) {
+                break;
             }
 
-            requestCodecNotification();
+            switch (cbID) {
+                case MediaCodec::CB_INPUT_AVAILABLE:
+                {
+                    int32_t index;
+                    CHECK(msg->findInt32("index", &index));
+
+                    handleAnInputBuffer(index);
+                    break;
+                }
+
+                case MediaCodec::CB_OUTPUT_AVAILABLE:
+                {
+                    int32_t index;
+                    size_t offset;
+                    size_t size;
+                    int64_t timeUs;
+                    int32_t flags;
+
+                    CHECK(msg->findInt32("index", &index));
+                    CHECK(msg->findSize("offset", &offset));
+                    CHECK(msg->findSize("size", &size));
+                    CHECK(msg->findInt64("timeUs", &timeUs));
+                    CHECK(msg->findInt32("flags", &flags));
+
+                    handleAnOutputBuffer(index, offset, size, timeUs, flags);
+                    break;
+                }
+
+                case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
+                {
+                    sp<AMessage> format;
+                    CHECK(msg->findMessage("format", &format));
+
+                    handleOutputFormatChange(format);
+                    break;
+                }
+
+                case MediaCodec::CB_ERROR:
+                {
+                    status_t err;
+                    CHECK(msg->findInt32("err", &err));
+                    ALOGE("Decoder (%s) reported error : 0x%x",
+                            mIsAudio ? "audio" : "video", err);
+
+                    handleError(err);
+                    break;
+                }
+
+                default:
+                {
+                    TRESPASS();
+                    break;
+                }
+            }
+
             break;
         }
 
@@ -132,14 +180,9 @@
     mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
     mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
 
-    sp<Surface> surface = NULL;
-    if (mNativeWindow != NULL) {
-        surface = mNativeWindow->getSurfaceTextureClient();
-    }
-
     mComponentName = mime;
     mComponentName.append(" decoder");
-    ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get());
+    ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get());
 
     mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */);
     int32_t secure = 0;
@@ -164,17 +207,17 @@
     mCodec->getName(&mComponentName);
 
     status_t err;
-    if (mNativeWindow != NULL) {
+    if (mSurface != NULL) {
         // disconnect from surface as MediaCodec will reconnect
         err = native_window_api_disconnect(
-                surface.get(), NATIVE_WINDOW_API_MEDIA);
+                mSurface.get(), NATIVE_WINDOW_API_MEDIA);
         // We treat this as a warning, as this is a preparatory step.
         // Codec will try to connect to the surface, which is where
         // any error signaling will occur.
         ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err);
     }
     err = mCodec->configure(
-            format, surface, NULL /* crypto */, 0 /* flags */);
+            format, mSurface, NULL /* crypto */, 0 /* flags */);
     if (err != OK) {
         ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
         mCodec->release();
@@ -188,6 +231,9 @@
     CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
     CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
 
+    sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
+    mCodec->setCallback(reply);
+
     err = mCodec->start();
     if (err != OK) {
         ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
@@ -197,36 +243,32 @@
         return;
     }
 
-    // the following should work after start
-    CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
     releaseAndResetMediaBuffers();
-    CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
-    ALOGV("[%s] got %zu input and %zu output buffers",
-            mComponentName.c_str(),
-            mInputBuffers.size(),
-            mOutputBuffers.size());
 
-    if (mRenderer != NULL) {
-        requestCodecNotification();
-    }
     mPaused = false;
     mResumePending = false;
 }
 
+void NuPlayer::Decoder::onSetParameters(const sp<AMessage> &params) {
+    if (mCodec == NULL) {
+        ALOGW("onSetParameters called before codec is created.");
+        return;
+    }
+    mCodec->setParameters(params);
+}
+
 void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
     bool hadNoRenderer = (mRenderer == NULL);
     mRenderer = renderer;
     if (hadNoRenderer && mRenderer != NULL) {
-        requestCodecNotification();
+        // this means that the widevine legacy source is ready
+        onRequestInputBuffers();
     }
 }
 
 void NuPlayer::Decoder::onGetInputBuffers(
         Vector<sp<ABuffer> > *dstBuffers) {
-    dstBuffers->clear();
-    for (size_t i = 0; i < mInputBuffers.size(); i++) {
-        dstBuffers->push(mInputBuffers[i]);
-    }
+    CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers));
 }
 
 void NuPlayer::Decoder::onResume(bool notifyComplete) {
@@ -235,6 +277,7 @@
     if (notifyComplete) {
         mResumePending = true;
     }
+    mCodec->start();
 }
 
 void NuPlayer::Decoder::doFlush(bool notifyComplete) {
@@ -261,8 +304,10 @@
         // we attempt to release the buffers even if flush fails.
     }
     releaseAndResetMediaBuffers();
+    mPaused = true;
 }
 
+
 void NuPlayer::Decoder::onFlush() {
     doFlush(true);
 
@@ -276,7 +321,6 @@
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatFlushCompleted);
     notify->post();
-    mPaused = true;
 }
 
 void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -290,12 +334,10 @@
         mCodec = NULL;
         ++mBufferGeneration;
 
-        if (mNativeWindow != NULL) {
+        if (mSurface != NULL) {
             // reconnect to surface as MediaCodec disconnected from it
             status_t error =
-                    native_window_api_connect(
-                            mNativeWindow->getNativeWindow().get(),
-                            NATIVE_WINDOW_API_MEDIA);
+                    native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_MEDIA);
             ALOGW_IF(error != NO_ERROR,
                     "[%s] failed to connect to native window, error=%d",
                     mComponentName.c_str(), error);
@@ -319,9 +361,14 @@
     }
 }
 
-void NuPlayer::Decoder::doRequestBuffers() {
-    if (isDiscontinuityPending()) {
-        return;
+/*
+ * returns true if we should request more data
+ */
+bool NuPlayer::Decoder::doRequestBuffers() {
+    // mRenderer is only NULL if we have a legacy widevine source that
+    // is not yet ready. In this case we must not fetch input.
+    if (isDiscontinuityPending() || mRenderer == NULL) {
+        return false;
     }
     status_t err = OK;
     while (err == OK && !mDequeuedInputBuffers.empty()) {
@@ -341,40 +388,54 @@
         }
     }
 
-    if (err == -EWOULDBLOCK
-            && mSource->feedMoreTSData() == OK) {
-        scheduleRequestBuffers();
-    }
+    return err == -EWOULDBLOCK
+            && mSource->feedMoreTSData() == OK;
 }
 
-bool NuPlayer::Decoder::handleAnInputBuffer() {
+void NuPlayer::Decoder::handleError(int32_t err)
+{
+    // We cannot immediately release the codec due to buffers still outstanding
+    // in the renderer.  We signal to the player the error so it can shutdown/release the
+    // decoder after flushing and increment the generation to discard unnecessary messages.
+
+    ++mBufferGeneration;
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatError);
+    notify->setInt32("err", err);
+    notify->post();
+}
+
+bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
     if (isDiscontinuityPending()) {
         return false;
     }
-    size_t bufferIx = -1;
-    status_t res = mCodec->dequeueInputBuffer(&bufferIx);
-    ALOGV("[%s] dequeued input: %d",
-            mComponentName.c_str(), res == OK ? (int)bufferIx : res);
-    if (res != OK) {
-        if (res != -EAGAIN) {
-            ALOGE("Failed to dequeue input buffer for %s (err=%d)",
-                    mComponentName.c_str(), res);
-            handleError(res);
+
+    sp<ABuffer> buffer;
+    mCodec->getInputBuffer(index, &buffer);
+
+    if (index >= mInputBuffers.size()) {
+        for (size_t i = mInputBuffers.size(); i <= index; ++i) {
+            mInputBuffers.add();
+            mMediaBuffers.add();
+            mInputBufferIsDequeued.add();
+            mMediaBuffers.editItemAt(i) = NULL;
+            mInputBufferIsDequeued.editItemAt(i) = false;
         }
-        return false;
     }
+    mInputBuffers.editItemAt(index) = buffer;
 
-    CHECK_LT(bufferIx, mInputBuffers.size());
+    //CHECK_LT(bufferIx, mInputBuffers.size());
 
-    if (mMediaBuffers[bufferIx] != NULL) {
-        mMediaBuffers[bufferIx]->release();
-        mMediaBuffers.editItemAt(bufferIx) = NULL;
+    if (mMediaBuffers[index] != NULL) {
+        mMediaBuffers[index]->release();
+        mMediaBuffers.editItemAt(index) = NULL;
     }
-    mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+    mInputBufferIsDequeued.editItemAt(index) = true;
 
     if (!mCSDsToSubmit.isEmpty()) {
         sp<AMessage> msg = new AMessage();
-        msg->setSize("buffer-ix", bufferIx);
+        msg->setSize("buffer-ix", index);
 
         sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
         ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
@@ -392,94 +453,34 @@
         mPendingInputMessages.erase(mPendingInputMessages.begin());
     }
 
-    if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
+    if (!mInputBufferIsDequeued.editItemAt(index)) {
         return true;
     }
 
-    mDequeuedInputBuffers.push_back(bufferIx);
+    mDequeuedInputBuffers.push_back(index);
 
     onRequestInputBuffers();
     return true;
 }
 
-bool NuPlayer::Decoder::handleAnOutputBuffer() {
-    size_t bufferIx = -1;
-    size_t offset;
-    size_t size;
-    int64_t timeUs;
-    uint32_t flags;
-    status_t res = mCodec->dequeueOutputBuffer(
-            &bufferIx, &offset, &size, &timeUs, &flags);
+bool NuPlayer::Decoder::handleAnOutputBuffer(
+        size_t index,
+        size_t offset,
+        size_t size,
+        int64_t timeUs,
+        int32_t flags) {
+//    CHECK_LT(bufferIx, mOutputBuffers.size());
+    sp<ABuffer> buffer;
+    mCodec->getOutputBuffer(index, &buffer);
 
-    if (res != OK) {
-        ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res);
-    } else {
-        ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")",
-                mComponentName.c_str(), (int)bufferIx, timeUs, flags);
+    if (index >= mOutputBuffers.size()) {
+        for (size_t i = mOutputBuffers.size(); i <= index; ++i) {
+            mOutputBuffers.add();
+        }
     }
 
-    if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
-        res = mCodec->getOutputBuffers(&mOutputBuffers);
-        if (res != OK) {
-            ALOGE("Failed to get output buffers for %s after INFO event (err=%d)",
-                    mComponentName.c_str(), res);
-            handleError(res);
-            return false;
-        }
-        // NuPlayer ignores this
-        return true;
-    } else if (res == INFO_FORMAT_CHANGED) {
-        sp<AMessage> format = new AMessage();
-        res = mCodec->getOutputFormat(&format);
-        if (res != OK) {
-            ALOGE("Failed to get output format for %s after INFO event (err=%d)",
-                    mComponentName.c_str(), res);
-            handleError(res);
-            return false;
-        }
+    mOutputBuffers.editItemAt(index) = buffer;
 
-        if (!mIsAudio) {
-            sp<AMessage> notify = mNotify->dup();
-            notify->setInt32("what", kWhatVideoSizeChanged);
-            notify->setMessage("format", format);
-            notify->post();
-        } else if (mRenderer != NULL) {
-            uint32_t flags;
-            int64_t durationUs;
-            bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
-            if (!hasVideo &&
-                    mSource->getDuration(&durationUs) == OK &&
-                    durationUs
-                        > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
-                flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
-            } else {
-                flags = AUDIO_OUTPUT_FLAG_NONE;
-            }
-
-            res = mRenderer->openAudioSink(
-                    format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */);
-            if (res != OK) {
-                ALOGE("Failed to open AudioSink on format change for %s (err=%d)",
-                        mComponentName.c_str(), res);
-                handleError(res);
-                return false;
-            }
-        }
-        return true;
-    } else if (res == INFO_DISCONTINUITY) {
-        // nothing to do
-        return true;
-    } else if (res != OK) {
-        if (res != -EAGAIN) {
-            ALOGE("Failed to dequeue output buffer for %s (err=%d)",
-                    mComponentName.c_str(), res);
-            handleError(res);
-        }
-        return false;
-    }
-
-    CHECK_LT(bufferIx, mOutputBuffers.size());
-    sp<ABuffer> buffer = mOutputBuffers[bufferIx];
     buffer->setRange(offset, size);
     buffer->meta()->clear();
     buffer->meta()->setInt64("timeUs", timeUs);
@@ -488,7 +489,7 @@
     // we do not expect CODECCONFIG or SYNCFRAME for decoder
 
     sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
-    reply->setSize("buffer-ix", bufferIx);
+    reply->setSize("buffer-ix", index);
     reply->setInt32("generation", mBufferGeneration);
 
     if (eos) {
@@ -522,6 +523,29 @@
     return true;
 }
 
+void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
+    if (!mIsAudio) {
+        sp<AMessage> notify = mNotify->dup();
+        notify->setInt32("what", kWhatVideoSizeChanged);
+        notify->setMessage("format", format);
+        notify->post();
+    } else if (mRenderer != NULL) {
+        uint32_t flags;
+        int64_t durationUs;
+        bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
+        if (!hasVideo &&
+                mSource->getDuration(&durationUs) == OK &&
+                durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
+            flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+        } else {
+            flags = AUDIO_OUTPUT_FLAG_NONE;
+        }
+
+        mRenderer->openAudioSink(
+                format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */);
+    }
+}
+
 void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
     for (size_t i = 0; i < mMediaBuffers.size(); i++) {
         if (mMediaBuffers[i] != NULL) {
@@ -637,7 +661,7 @@
 #if 0
     int64_t mediaTimeUs;
     CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
-    ALOGV("[%s] feeding input buffer at media time %" PRId64,
+    ALOGV("[%s] feeding input buffer at media time %.3f",
          mIsAudio ? "audio" : "video",
          mediaTimeUs / 1E6);
 #endif
@@ -825,11 +849,9 @@
         mPaused = true;
     } else if (mTimeChangePending) {
         if (flushOnTimeChange) {
-            doFlush(false /*notifyComplete*/);
+            doFlush(false /* notifyComplete */);
+            signalResume(false /* notifyComplete */);
         }
-
-        // restart fetching input
-        scheduleRequestBuffers();
     }
 
     // Notify NuPlayer to either shutdown decoder, or rescan sources
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 4aab2c6..070d51a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -27,7 +27,7 @@
     Decoder(const sp<AMessage> &notify,
             const sp<Source> &source,
             const sp<Renderer> &renderer = NULL,
-            const sp<NativeWindowWrapper> &nativeWindow = NULL,
+            const sp<Surface> &surface = NULL,
             const sp<CCDecoder> &ccDecoder = NULL);
 
     virtual void getStats(
@@ -40,12 +40,13 @@
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
     virtual void onConfigure(const sp<AMessage> &format);
+    virtual void onSetParameters(const sp<AMessage> &params);
     virtual void onSetRenderer(const sp<Renderer> &renderer);
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
     virtual void onResume(bool notifyComplete);
     virtual void onFlush();
     virtual void onShutdown(bool notifyComplete);
-    virtual void doRequestBuffers();
+    virtual bool doRequestBuffers();
 
 private:
     enum {
@@ -53,7 +54,7 @@
         kWhatRenderBuffer        = 'rndr',
     };
 
-    sp<NativeWindowWrapper> mNativeWindow;
+    sp<Surface> mSurface;
 
     sp<Source> mSource;
     sp<Renderer> mRenderer;
@@ -87,8 +88,15 @@
     bool mResumePending;
     AString mComponentName;
 
-    bool handleAnInputBuffer();
-    bool handleAnOutputBuffer();
+    void handleError(int32_t err);
+    bool handleAnInputBuffer(size_t index);
+    bool handleAnOutputBuffer(
+            size_t index,
+            size_t offset,
+            size_t size,
+            int64_t timeUs,
+            int32_t flags);
+    void handleOutputFormatChange(const sp<AMessage> &format);
 
     void releaseAndResetMediaBuffers();
     void requestCodecNotification();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 4636f0a..9d509bf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -70,6 +70,12 @@
     mDecoderLooper->registerHandler(this);
 }
 
+void NuPlayer::DecoderBase::setParameters(const sp<AMessage> &params) {
+    sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
+    msg->setMessage("params", params);
+    msg->post();
+}
+
 void NuPlayer::DecoderBase::setRenderer(const sp<Renderer> &renderer) {
     sp<AMessage> msg = new AMessage(kWhatSetRenderer, this);
     msg->setObject("renderer", renderer);
@@ -103,16 +109,13 @@
         return;
     }
 
-    doRequestBuffers();
-}
+    // doRequestBuffers() return true if we should request more data
+    if (doRequestBuffers()) {
+        mRequestInputBuffersPending = true;
 
-void NuPlayer::DecoderBase::scheduleRequestBuffers() {
-    if (mRequestInputBuffersPending) {
-        return;
+        sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this);
+        msg->post(10 * 1000ll);
     }
-    mRequestInputBuffersPending = true;
-    sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this);
-    msg->post(10 * 1000ll);
 }
 
 void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) {
@@ -126,6 +129,14 @@
             break;
         }
 
+        case kWhatSetParameters:
+        {
+            sp<AMessage> params;
+            CHECK(msg->findMessage("params", &params));
+            onSetParameters(params);
+            break;
+        }
+
         case kWhatSetRenderer:
         {
             sp<RefBase> obj;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 97e9269..b52e7f7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -26,13 +26,14 @@
 
 struct ABuffer;
 struct MediaCodec;
-struct MediaBuffer;
+class MediaBuffer;
 
 struct NuPlayer::DecoderBase : public AHandler {
     DecoderBase(const sp<AMessage> &notify);
 
     void configure(const sp<AMessage> &format);
     void init();
+    void setParameters(const sp<AMessage> &params);
 
     void setRenderer(const sp<Renderer> &renderer);
 
@@ -62,6 +63,7 @@
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
     virtual void onConfigure(const sp<AMessage> &format) = 0;
+    virtual void onSetParameters(const sp<AMessage> &params) = 0;
     virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0;
     virtual void onResume(bool notifyComplete) = 0;
@@ -69,8 +71,7 @@
     virtual void onShutdown(bool notifyComplete) = 0;
 
     void onRequestInputBuffers();
-    void scheduleRequestBuffers();
-    virtual void doRequestBuffers() = 0;
+    virtual bool doRequestBuffers() = 0;
     virtual void handleError(int32_t err);
 
     sp<AMessage> mNotify;
@@ -79,6 +80,7 @@
 private:
     enum {
         kWhatConfigure           = 'conf',
+        kWhatSetParameters       = 'setP',
         kWhatSetRenderer         = 'setR',
         kWhatGetInputBuffers     = 'gInB',
         kWhatRequestInputBuffers = 'reqB',
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 563de5e..d7b070e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -88,6 +88,10 @@
     }
 }
 
+void NuPlayer::DecoderPassThrough::onSetParameters(const sp<AMessage> &/*params*/) {
+    ALOGW("onSetParameters() called unexpectedly");
+}
+
 void NuPlayer::DecoderPassThrough::onSetRenderer(
         const sp<Renderer> &renderer) {
     // renderer can't be changed during offloading
@@ -113,7 +117,10 @@
     return mCachedBytes >= kMaxCachedBytes || mReachedEOS || mPaused;
 }
 
-void NuPlayer::DecoderPassThrough::doRequestBuffers() {
+/*
+ * returns true if we should request more data
+ */
+bool NuPlayer::DecoderPassThrough::doRequestBuffers() {
     status_t err = OK;
     while (!isDoneFetching()) {
         sp<AMessage> msg = new AMessage();
@@ -126,10 +133,8 @@
         onInputBufferFetched(msg);
     }
 
-    if (err == -EWOULDBLOCK
-            && mSource->feedMoreTSData() == OK) {
-        scheduleRequestBuffers();
-    }
+    return err == -EWOULDBLOCK
+            && mSource->feedMoreTSData() == OK;
 }
 
 status_t NuPlayer::DecoderPassThrough::dequeueAccessUnit(sp<ABuffer> *accessUnit) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 173cfbd..2f6df2c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -40,12 +40,13 @@
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
     virtual void onConfigure(const sp<AMessage> &format);
+    virtual void onSetParameters(const sp<AMessage> &params);
     virtual void onSetRenderer(const sp<Renderer> &renderer);
     virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
     virtual void onResume(bool notifyComplete);
     virtual void onFlush();
     virtual void onShutdown(bool notifyComplete);
-    virtual void doRequestBuffers();
+    virtual bool doRequestBuffers();
 
 private:
     enum {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 1fa9cef..231f2e1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -135,6 +135,25 @@
     return mAsyncResult;
 }
 
+status_t NuPlayerDriver::setDataSource(const sp<DataSource> &source) {
+    ALOGV("setDataSource(%p) callback source", this);
+    Mutex::Autolock autoLock(mLock);
+
+    if (mState != STATE_IDLE) {
+        return INVALID_OPERATION;
+    }
+
+    mState = STATE_SET_DATASOURCE_PENDING;
+
+    mPlayer->setDataSourceAsync(source);
+
+    while (mState == STATE_SET_DATASOURCE_PENDING) {
+        mCondition.wait(mLock);
+    }
+
+    return mAsyncResult;
+}
+
 status_t NuPlayerDriver::setVideoSurfaceTexture(
         const sp<IGraphicBufferProducer> &bufferProducer) {
     ALOGV("setVideoSurfaceTexture(%p)", this);
@@ -341,9 +360,32 @@
     return mState == STATE_RUNNING && !mAtEOS;
 }
 
-status_t NuPlayerDriver::setPlaybackRate(float rate) {
-    mPlayer->setPlaybackRate(rate);
-    return OK;
+status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
+    Mutex::Autolock autoLock(mLock);
+    status_t err = mPlayer->setPlaybackSettings(rate);
+    if (err == OK) {
+        if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
+            mState = STATE_PAUSED;
+            // try to update position
+            (void)mPlayer->getCurrentPosition(&mPositionUs);
+            notifyListener_l(MEDIA_PAUSED);
+        } else if (rate.mSpeed != 0.f && mState == STATE_PAUSED) {
+            mState = STATE_RUNNING;
+        }
+    }
+    return err;
+}
+
+status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) {
+    return mPlayer->getPlaybackSettings(rate);
+}
+
+status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+    return mPlayer->setSyncSettings(sync, videoFpsHint);
+}
+
+status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
+    return mPlayer->getSyncSettings(sync, videoFps);
 }
 
 status_t NuPlayerDriver::seekTo(int msec) {
@@ -369,6 +411,9 @@
         {
             mAtEOS = false;
             mSeekInProgress = true;
+            if (mState == STATE_PAUSED) {
+               mStartupSeekTimeUs = seekTimeUs;
+            }
             // seeks can take a while, so we essentially paused
             notifyListener_l(MEDIA_PAUSED);
             mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index e53abcd..9da7fc1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -39,6 +39,8 @@
 
     virtual status_t setDataSource(const sp<IStreamSource> &source);
 
+    virtual status_t setDataSource(const sp<DataSource>& dataSource);
+
     virtual status_t setVideoSurfaceTexture(
             const sp<IGraphicBufferProducer> &bufferProducer);
     virtual status_t prepare();
@@ -47,7 +49,10 @@
     virtual status_t stop();
     virtual status_t pause();
     virtual bool isPlaying();
-    virtual status_t setPlaybackRate(float rate);
+    virtual status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+    virtual status_t getPlaybackSettings(AudioPlaybackRate *rate);
+    virtual status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
+    virtual status_t getSyncSettings(AVSyncSettings *sync, float *videoFps);
     virtual status_t seekTo(int msec);
     virtual status_t getCurrentPosition(int *msec);
     virtual status_t getDuration(int *msec);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index a2ec51c..007a335 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -66,7 +66,7 @@
       mVideoQueueGeneration(0),
       mAudioDrainGeneration(0),
       mVideoDrainGeneration(0),
-      mPlaybackRate(1.0),
+      mPlaybackSettings(AUDIO_PLAYBACK_RATE_DEFAULT),
       mAudioFirstAnchorTimeMediaUs(-1),
       mAnchorTimeMediaUs(-1),
       mAnchorNumFramesWritten(-1),
@@ -89,6 +89,8 @@
       mLastAudioBufferDrained(0),
       mWakeLock(new AWakeLock()) {
     mMediaClock = new MediaClock;
+    mPlaybackRate = mPlaybackSettings.mSpeed;
+    mMediaClock->setPlaybackRate(mPlaybackRate);
 }
 
 NuPlayer::Renderer::~Renderer() {
@@ -121,10 +123,111 @@
     msg->post();
 }
 
-void NuPlayer::Renderer::setPlaybackRate(float rate) {
-    sp<AMessage> msg = new AMessage(kWhatSetRate, this);
-    msg->setFloat("rate", rate);
-    msg->post();
+status_t NuPlayer::Renderer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+    sp<AMessage> msg = new AMessage(kWhatConfigPlayback, this);
+    writeToAMessage(msg, rate);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+    return err;
+}
+
+status_t NuPlayer::Renderer::onConfigPlayback(const AudioPlaybackRate &rate /* sanitized */) {
+    if (rate.mSpeed == 0.f) {
+        onPause();
+        // don't call audiosink's setPlaybackRate if pausing, as pitch does not
+        // have to correspond to the any non-0 speed (e.g old speed). Keep
+        // settings nonetheless, using the old speed, in case audiosink changes.
+        AudioPlaybackRate newRate = rate;
+        newRate.mSpeed = mPlaybackSettings.mSpeed;
+        mPlaybackSettings = newRate;
+        return OK;
+    }
+
+    if (mAudioSink != NULL && mAudioSink->ready()) {
+        status_t err = mAudioSink->setPlaybackRate(rate);
+        if (err != OK) {
+            return err;
+        }
+    }
+    mPlaybackSettings = rate;
+    mPlaybackRate = rate.mSpeed;
+    mMediaClock->setPlaybackRate(mPlaybackRate);
+    return OK;
+}
+
+status_t NuPlayer::Renderer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+    sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+        if (err == OK) {
+            readFromAMessage(response, rate);
+        }
+    }
+    return err;
+}
+
+status_t NuPlayer::Renderer::onGetPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+    if (mAudioSink != NULL && mAudioSink->ready()) {
+        status_t err = mAudioSink->getPlaybackRate(rate);
+        if (err == OK) {
+            if (!isAudioPlaybackRateEqual(*rate, mPlaybackSettings)) {
+                ALOGW("correcting mismatch in internal/external playback rate");
+            }
+            // get playback settings used by audiosink, as it may be
+            // slightly off due to audiosink not taking small changes.
+            mPlaybackSettings = *rate;
+            if (mPaused) {
+                rate->mSpeed = 0.f;
+            }
+        }
+        return err;
+    }
+    *rate = mPlaybackSettings;
+    return OK;
+}
+
+status_t NuPlayer::Renderer::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+    sp<AMessage> msg = new AMessage(kWhatConfigSync, this);
+    writeToAMessage(msg, sync, videoFpsHint);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+    return err;
+}
+
+status_t NuPlayer::Renderer::onConfigSync(const AVSyncSettings &sync, float videoFpsHint __unused) {
+    if (sync.mSource != AVSYNC_SOURCE_DEFAULT) {
+        return BAD_VALUE;
+    }
+    // TODO: support sync sources
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayer::Renderer::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
+    sp<AMessage> msg = new AMessage(kWhatGetSyncSettings, this);
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+        if (err == OK) {
+            readFromAMessage(response, sync, videoFps);
+        }
+    }
+    return err;
+}
+
+status_t NuPlayer::Renderer::onGetSyncSettings(
+        AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */) {
+    *sync = mSyncSettings;
+    *videoFps = -1.f;
+    return OK;
 }
 
 void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) {
@@ -312,6 +415,9 @@
                 int64_t delayUs =
                     mAudioSink->msecsPerFrame()
                         * numFramesPendingPlayout * 1000ll;
+                if (mPlaybackRate > 1.0f) {
+                    delayUs /= mPlaybackRate;
+                }
 
                 // Let's give it more data after about half that time
                 // has elapsed.
@@ -362,13 +468,63 @@
             break;
         }
 
-        case kWhatSetRate:
+        case kWhatConfigPlayback:
         {
-            CHECK(msg->findFloat("rate", &mPlaybackRate));
-            int32_t ratePermille = (int32_t)(0.5f + 1000 * mPlaybackRate);
-            mPlaybackRate = ratePermille / 1000.0f;
-            mMediaClock->setPlaybackRate(mPlaybackRate);
-            mAudioSink->setPlaybackRatePermille(ratePermille);
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            AudioPlaybackRate rate;
+            readFromAMessage(msg, &rate);
+            status_t err = onConfigPlayback(rate);
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatGetPlaybackSettings:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            AudioPlaybackRate rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+            status_t err = onGetPlaybackSettings(&rate);
+            sp<AMessage> response = new AMessage;
+            if (err == OK) {
+                writeToAMessage(response, rate);
+            }
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatConfigSync:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            AVSyncSettings sync;
+            float videoFpsHint;
+            readFromAMessage(msg, &sync, &videoFpsHint);
+            status_t err = onConfigSync(sync, videoFpsHint);
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatGetSyncSettings:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            ALOGV("kWhatGetSyncSettings");
+            AVSyncSettings sync;
+            float videoFps = -1.f;
+            status_t err = onGetSyncSettings(&sync, &videoFps);
+            sp<AMessage> response = new AMessage;
+            if (err == OK) {
+                writeToAMessage(response, sync, videoFps);
+            }
+            response->setInt32("err", err);
+            response->postReply(replyID);
             break;
         }
 
@@ -618,7 +774,8 @@
             return false;
         }
 
-        if (entry->mOffset == 0) {
+        // ignore 0-sized buffer which could be EOS marker with no data
+        if (entry->mOffset == 0 && entry->mBuffer->size() > 0) {
             int64_t mediaTimeUs;
             CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
             ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
@@ -853,7 +1010,7 @@
 
         if (tooLate) {
             ALOGV("video late by %lld us (%.2f secs)",
-                 mVideoLateByUs, mVideoLateByUs / 1E6);
+                 (long long)mVideoLateByUs, mVideoLateByUs / 1E6);
         } else {
             int64_t mediaUs = 0;
             mMediaClock->getMediaTime(realTimeUs, &mediaUs);
@@ -1083,6 +1240,16 @@
             mAudioSink->pause();
             mAudioSink->flush();
             mAudioSink->start();
+        } else {
+            mAudioSink->pause();
+            mAudioSink->flush();
+            // Call stop() to signal to the AudioSink to completely fill the
+            // internal buffer before resuming playback.
+            mAudioSink->stop();
+            if (!mPaused) {
+                mAudioSink->start();
+            }
+            mNumFramesWritten = 0;
         }
     } else {
         flushQueue(&mVideoQueue);
@@ -1172,10 +1339,9 @@
 
 void NuPlayer::Renderer::onPause() {
     if (mPaused) {
-        ALOGW("Renderer::onPause() called while already paused!");
         return;
     }
-    int64_t currentPositionUs;
+
     {
         Mutex::Autolock autoLock(mLock);
         ++mAudioDrainGeneration;
@@ -1193,7 +1359,7 @@
         startAudioOffloadPauseTimeout();
     }
 
-    ALOGV("now paused audio queue has %d entries, video has %d entries",
+    ALOGV("now paused audio queue has %zu entries, video has %zu entries",
           mAudioQueue.size(), mVideoQueue.size());
 }
 
@@ -1210,6 +1376,12 @@
     {
         Mutex::Autolock autoLock(mLock);
         mPaused = false;
+
+        // configure audiosink as we did not do it when pausing
+        if (mAudioSink != NULL && mAudioSink->ready()) {
+            mAudioSink->setPlaybackRate(mPlaybackSettings);
+        }
+
         mMediaClock->setPlaybackRate(mPlaybackRate);
 
         if (!mAudioQueue.empty()) {
@@ -1286,7 +1458,7 @@
         CHECK_EQ(res, (status_t)OK);
         numFramesPlayedAt = nowUs;
         numFramesPlayedAt += 1000LL * mAudioSink->latency() / 2; /* XXX */
-        //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt);
+        //ALOGD("getPosition: %u %lld", numFramesPlayed, (long long)numFramesPlayedAt);
     }
 
     //CHECK_EQ(numFramesPlayed & (1 << 31), 0);  // can't be negative until 12.4 hrs, test
@@ -1429,10 +1601,10 @@
                     &offloadInfo);
 
             if (err == OK) {
-                if (mPlaybackRate != 1.0) {
-                    mAudioSink->setPlaybackRatePermille(
-                            (int32_t)(mPlaybackRate * 1000 + 0.5f));
-                }
+                err = mAudioSink->setPlaybackRate(mPlaybackSettings);
+            }
+
+            if (err == OK) {
                 // If the playback is offloaded to h/w, we pass
                 // the HAL some metadata information.
                 // We don't want to do this for PCM because it
@@ -1482,16 +1654,15 @@
                     NULL,
                     NULL,
                     (audio_output_flags_t)pcmFlags);
+        if (err == OK) {
+            err = mAudioSink->setPlaybackRate(mPlaybackSettings);
+        }
         if (err != OK) {
             ALOGW("openAudioSink: non offloaded open failed status: %d", err);
             mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
             return err;
         }
         mCurrentPcmInfo = info;
-        if (mPlaybackRate != 1.0) {
-            mAudioSink->setPlaybackRatePermille(
-                    (int32_t)(mPlaybackRate * 1000 + 0.5f));
-        }
         mAudioSink->start();
     }
     if (audioSinkChanged) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 38843d5..928b71b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -18,6 +18,9 @@
 
 #define NUPLAYER_RENDERER_H_
 
+#include <media/AudioResamplerPublic.h>
+#include <media/AVSyncSettings.h>
+
 #include "NuPlayer.h"
 
 namespace android {
@@ -48,7 +51,10 @@
 
     void queueEOS(bool audio, status_t finalResult);
 
-    void setPlaybackRate(float rate);
+    status_t setPlaybackSettings(const AudioPlaybackRate &rate /* sanitized */);
+    status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+    status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
+    status_t getSyncSettings(AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
 
     void flush(bool audio, bool notifyComplete);
 
@@ -102,7 +108,10 @@
         kWhatPostDrainVideoQueue = 'pDVQ',
         kWhatQueueBuffer         = 'queB',
         kWhatQueueEOS            = 'qEOS',
-        kWhatSetRate             = 'setR',
+        kWhatConfigPlayback      = 'cfPB',
+        kWhatConfigSync          = 'cfSy',
+        kWhatGetPlaybackSettings = 'gPbS',
+        kWhatGetSyncSettings     = 'gSyS',
         kWhatFlush               = 'flus',
         kWhatPause               = 'paus',
         kWhatResume              = 'resm',
@@ -141,7 +150,12 @@
     int32_t mVideoDrainGeneration;
 
     sp<MediaClock> mMediaClock;
-    float mPlaybackRate;
+    float mPlaybackRate; // audio track rate
+
+    AudioPlaybackRate mPlaybackSettings;
+    AVSyncSettings mSyncSettings;
+    float mVideoFpsHint;
+
     int64_t mAudioFirstAnchorTimeMediaUs;
     int64_t mAnchorTimeMediaUs;
     int64_t mAnchorNumFramesWritten;
@@ -217,6 +231,11 @@
     void onAudioSinkChanged();
     void onDisableOffloadAudio();
     void onEnableOffloadAudio();
+    status_t onConfigPlayback(const AudioPlaybackRate &rate /* sanitized */);
+    status_t onGetPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
+    status_t onConfigSync(const AVSyncSettings &sync, float videoFpsHint);
+    status_t onGetSyncSettings(AVSyncSettings *sync /* nonnull */, float *videoFps /* nonnull */);
+
     void onPause();
     void onResume();
     void onSetVideoFrameRate(float fps);
@@ -252,6 +271,6 @@
     DISALLOW_EVIL_CONSTRUCTORS(Renderer);
 };
 
-}  // namespace android
+} // namespace android
 
 #endif  // NUPLAYER_RENDERER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index d9f14a2..ef1ba13 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -28,7 +28,7 @@
 namespace android {
 
 struct ABuffer;
-struct MediaBuffer;
+class MediaBuffer;
 
 struct NuPlayer::Source : public AHandler {
     enum Flags {
@@ -53,6 +53,7 @@
         kWhatCacheStats,
         kWhatSubtitleData,
         kWhatTimedTextData,
+        kWhatTimedMetaData,
         kWhatQueueDecoderShutdown,
         kWhatDrmNoLicense,
         kWhatInstantiateSecureDecoders,
diff --git a/media/libmediaplayerservice/tests/Android.mk b/media/libmediaplayerservice/tests/Android.mk
index 7bc78ff..8cbf782 100644
--- a/media/libmediaplayerservice/tests/Android.mk
+++ b/media/libmediaplayerservice/tests/Android.mk
@@ -18,6 +18,9 @@
 	frameworks/av/include \
 	frameworks/av/media/libmediaplayerservice \
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 LOCAL_32_BIT_ONLY := true
 
 include $(BUILD_NATIVE_TEST)
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index d3e760b..de350a1 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -98,17 +98,17 @@
         mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2);
         mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3);
         const PidSessionInfosMap& map = sessionMap();
-        EXPECT_EQ(2, map.size());
+        EXPECT_EQ(2u, map.size());
         ssize_t index1 = map.indexOfKey(kTestPid1);
         ASSERT_GE(index1, 0);
         const SessionInfos& infos1 = map[index1];
-        EXPECT_EQ(1, infos1.size());
+        EXPECT_EQ(1u, infos1.size());
         ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0);
 
         ssize_t index2 = map.indexOfKey(kTestPid2);
         ASSERT_GE(index2, 0);
         const SessionInfos& infos2 = map[index2];
-        EXPECT_EQ(2, infos2.size());
+        EXPECT_EQ(2u, infos2.size());
         ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1);
         ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2);
     }
@@ -185,11 +185,11 @@
     mDrmSessionManager->removeSession(mSessionId2);
 
     const PidSessionInfosMap& map = sessionMap();
-    EXPECT_EQ(2, map.size());
+    EXPECT_EQ(2u, map.size());
     const SessionInfos& infos1 = map.valueFor(kTestPid1);
     const SessionInfos& infos2 = map.valueFor(kTestPid2);
-    EXPECT_EQ(1, infos1.size());
-    EXPECT_EQ(1, infos2.size());
+    EXPECT_EQ(1u, infos1.size());
+    EXPECT_EQ(1u, infos2.size());
     // mSessionId2 has been removed.
     ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2);
 }
@@ -207,7 +207,7 @@
 
     const PidSessionInfosMap& map = sessionMap();
     const SessionInfos& infos2 = map.valueFor(kTestPid2);
-    EXPECT_EQ(1, infos2.size());
+    EXPECT_EQ(1u, infos2.size());
     // mTestDrm2 has been removed.
     ExpectEqSessionInfo(infos2[0], drm, sessionId, 3);
 }
@@ -220,7 +220,7 @@
     EXPECT_FALSE(mDrmSessionManager->reclaimSession(50));
 
     EXPECT_TRUE(mDrmSessionManager->reclaimSession(10));
-    EXPECT_EQ(1, mTestDrm1->reclaimedSessions().size());
+    EXPECT_EQ(1u, mTestDrm1->reclaimedSessions().size());
     EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
 
     mDrmSessionManager->removeSession(mSessionId1);
@@ -233,7 +233,7 @@
     mDrmSessionManager->addSession(15, drm, sessionId);
 
     EXPECT_TRUE(mDrmSessionManager->reclaimSession(18));
-    EXPECT_EQ(1, mTestDrm2->reclaimedSessions().size());
+    EXPECT_EQ(1u, mTestDrm2->reclaimedSessions().size());
     // mSessionId2 is reclaimed.
     EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));
 }
diff --git a/media/libnbaio/SourceAudioBufferProvider.cpp b/media/libnbaio/SourceAudioBufferProvider.cpp
index e21ef48..04c42c9 100644
--- a/media/libnbaio/SourceAudioBufferProvider.cpp
+++ b/media/libnbaio/SourceAudioBufferProvider.cpp
@@ -61,20 +61,30 @@
     // do we need to reallocate?
     if (buffer->frameCount > mSize) {
         free(mAllocated);
-        mAllocated = malloc(buffer->frameCount * mFrameSize);
+        // Android convention is to _not_ check the return value of malloc and friends.
+        // But in this case the calloc() can also fail due to integer overflow,
+        // so we check and recover.
+        mAllocated = calloc(buffer->frameCount, mFrameSize);
+        if (mAllocated == NULL) {
+            mSize = 0;
+            goto fail;
+        }
         mSize = buffer->frameCount;
     }
-    // read from source
-    ssize_t actual = mSource->read(mAllocated, buffer->frameCount, pts);
-    if (actual > 0) {
-        ALOG_ASSERT((size_t) actual <= buffer->frameCount);
-        mOffset = 0;
-        mRemaining = actual;
-        buffer->raw = mAllocated;
-        buffer->frameCount = actual;
-        mGetCount = actual;
-        return OK;
+    {
+        // read from source
+        ssize_t actual = mSource->read(mAllocated, buffer->frameCount, pts);
+        if (actual > 0) {
+            ALOG_ASSERT((size_t) actual <= buffer->frameCount);
+            mOffset = 0;
+            mRemaining = actual;
+            buffer->raw = mAllocated;
+            buffer->frameCount = actual;
+            mGetCount = actual;
+            return OK;
+        }
     }
+fail:
     buffer->raw = NULL;
     buffer->frameCount = 0;
     mGetCount = 0;
diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp
index 196f6ee..45e8a30 100644
--- a/media/libstagefright/AACExtractor.cpp
+++ b/media/libstagefright/AACExtractor.cpp
@@ -360,7 +360,7 @@
         pos += len;
 
         ALOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)",
-             pos, pos);
+                (long long)pos, (long long)pos);
     }
 
     uint8_t header[2];
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 97f3e20..7cba450 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -24,6 +24,8 @@
 #include <inttypes.h>
 #include <utils/Trace.h>
 
+#include <gui/Surface.h>
+
 #include <media/stagefright/ACodec.h>
 
 #include <binder/MemoryDealer.h>
@@ -37,16 +39,17 @@
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
-
+#include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/SurfaceUtils.h>
 #include <media/hardware/HardwareAPI.h>
 
 #include <OMX_AudioExt.h>
 #include <OMX_VideoExt.h>
 #include <OMX_Component.h>
 #include <OMX_IndexExt.h>
+#include <OMX_AsString.h>
 
 #include "include/avc_utils.h"
 
@@ -152,7 +155,7 @@
             }
 
             default:
-                TRESPASS();
+                ALOGE("Unrecognized message type: %d", omx_msg.type);
                 break;
         }
 
@@ -259,9 +262,12 @@
 
     bool onConfigureComponent(const sp<AMessage> &msg);
     void onCreateInputSurface(const sp<AMessage> &msg);
+    void onSetInputSurface(const sp<AMessage> &msg);
     void onStart();
     void onShutdown(bool keepComponentAllocated);
 
+    status_t setupInputSurface();
+
     DISALLOW_EVIL_CONSTRUCTORS(LoadedState);
 };
 
@@ -405,6 +411,7 @@
     : mQuirks(0),
       mNode(0),
       mSentFormat(false),
+      mIsVideo(false),
       mIsEncoder(false),
       mUseMetadataOnEncoderOutput(false),
       mShutdownInProgress(false),
@@ -416,6 +423,7 @@
       mChannelMask(0),
       mDequeueCounter(0),
       mStoreMetaDataInOutputBuffers(false),
+      mLegacyAdaptiveExperiment(false),
       mMetaDataBuffersToSubmit(0),
       mRepeatFrameDelayUs(-1ll),
       mMaxPtsGapUs(-1ll),
@@ -474,10 +482,30 @@
     msg->post();
 }
 
+status_t ACodec::setSurface(const sp<Surface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
+    msg->setObject("surface", surface);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+
+    if (err == OK) {
+        (void)response->findInt32("err", &err);
+    }
+    return err;
+}
+
 void ACodec::initiateCreateInputSurface() {
     (new AMessage(kWhatCreateInputSurface, this))->post();
 }
 
+void ACodec::initiateSetInputSurface(
+        const sp<PersistentSurface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
+    msg->setObject("input-surface", surface);
+    msg->post();
+}
+
 void ACodec::signalEndOfInputStream() {
     (new AMessage(kWhatSignalEndOfInputStream, this))->post();
 }
@@ -521,6 +549,123 @@
     }
 }
 
+status_t ACodec::handleSetSurface(const sp<Surface> &surface) {
+    // allow keeping unset surface
+    if (surface == NULL) {
+        if (mNativeWindow != NULL) {
+            ALOGW("cannot unset a surface");
+            return INVALID_OPERATION;
+        }
+        return OK;
+    }
+
+    // allow keeping unset surface
+    if (mNativeWindow == NULL) {
+        ALOGW("component was not configured with a surface");
+        return INVALID_OPERATION;
+    }
+
+    ANativeWindow *nativeWindow = surface.get();
+    // if we have not yet started the codec, we can simply set the native window
+    if (mBuffers[kPortIndexInput].size() == 0) {
+        mNativeWindow = surface;
+        return OK;
+    }
+
+    // we do not support changing a tunneled surface after start
+    if (mTunneled) {
+        ALOGW("cannot change tunneled surface");
+        return INVALID_OPERATION;
+    }
+
+    status_t err = setupNativeWindowSizeFormatAndUsage(nativeWindow);
+    if (err != OK) {
+        return err;
+    }
+
+    // get min undequeued count. We cannot switch to a surface that has a higher
+    // undequeued count than we allocated.
+    int minUndequeuedBuffers = 0;
+    err = nativeWindow->query(
+            nativeWindow, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+            &minUndequeuedBuffers);
+    if (err != 0) {
+        ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+                strerror(-err), -err);
+        return err;
+    }
+    if (minUndequeuedBuffers > (int)mNumUndequeuedBuffers) {
+        ALOGE("new surface holds onto more buffers (%d) than planned for (%zu)",
+                minUndequeuedBuffers, mNumUndequeuedBuffers);
+        return BAD_VALUE;
+    }
+
+    // we cannot change the number of output buffers while OMX is running
+    // set up surface to the same count
+    Vector<BufferInfo> &buffers = mBuffers[kPortIndexOutput];
+    ALOGV("setting up surface for %zu buffers", buffers.size());
+
+    err = native_window_set_buffer_count(nativeWindow, buffers.size());
+    if (err != 0) {
+        ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
+                -err);
+        return err;
+    }
+
+    // need to enable allocation when attaching
+    surface->getIGraphicBufferProducer()->allowAllocation(true);
+
+    // for meta data mode, we move dequeud buffers to the new surface.
+    // for non-meta mode, we must move all registered buffers
+    for (size_t i = 0; i < buffers.size(); ++i) {
+        const BufferInfo &info = buffers[i];
+        // skip undequeued buffers for meta data mode
+        if (mStoreMetaDataInOutputBuffers
+                && !mLegacyAdaptiveExperiment
+                && info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+            ALOGV("skipping buffer %p", info.mGraphicBuffer->getNativeBuffer());
+            continue;
+        }
+        ALOGV("attaching buffer %p", info.mGraphicBuffer->getNativeBuffer());
+
+        err = surface->attachBuffer(info.mGraphicBuffer->getNativeBuffer());
+        if (err != OK) {
+            ALOGE("failed to attach buffer %p to the new surface: %s (%d)",
+                    info.mGraphicBuffer->getNativeBuffer(),
+                    strerror(-err), -err);
+            return err;
+        }
+    }
+
+    // cancel undequeued buffers to new surface
+    if (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment) {
+        for (size_t i = 0; i < buffers.size(); ++i) {
+            const BufferInfo &info = buffers[i];
+            if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                ALOGV("canceling buffer %p", info.mGraphicBuffer->getNativeBuffer());
+                err = nativeWindow->cancelBuffer(
+                        nativeWindow, info.mGraphicBuffer->getNativeBuffer(), -1);
+                if (err != OK) {
+                    ALOGE("failed to cancel buffer %p to the new surface: %s (%d)",
+                            info.mGraphicBuffer->getNativeBuffer(),
+                            strerror(-err), -err);
+                    return err;
+                }
+            }
+        }
+        // disallow further allocation
+        (void)surface->getIGraphicBufferProducer()->allowAllocation(false);
+    }
+
+    // push blank buffers to previous window if requested
+    if (mFlags & kFlagPushBlankBuffersToNativeWindowOnShutdown) {
+        pushBlankBuffersToNativeWindow(mNativeWindow.get());
+    }
+
+    mNativeWindow = nativeWindow;
+    return OK;
+}
+
 status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
     CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
 
@@ -553,7 +698,9 @@
 
             for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
                 sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
-                CHECK(mem.get() != NULL);
+                if (mem.get() == NULL) {
+                    return NO_MEMORY;
+                }
 
                 BufferInfo info;
                 info.mStatus = BufferInfo::OWNED_BY_US;
@@ -615,9 +762,7 @@
     return OK;
 }
 
-status_t ACodec::configureOutputBuffersFromNativeWindow(
-        OMX_U32 *bufferCount, OMX_U32 *bufferSize,
-        OMX_U32 *minUndequeuedBuffers) {
+status_t ACodec::setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */) {
     OMX_PARAM_PORTDEFINITIONTYPE def;
     InitOMXParams(&def);
     def.nPortIndex = kPortIndexOutput;
@@ -629,40 +774,6 @@
         return err;
     }
 
-    err = native_window_set_buffers_geometry(
-            mNativeWindow.get(),
-            def.format.video.nFrameWidth,
-            def.format.video.nFrameHeight,
-            def.format.video.eColorFormat);
-
-    if (err != 0) {
-        ALOGE("native_window_set_buffers_geometry failed: %s (%d)",
-                strerror(-err), -err);
-        return err;
-    }
-
-    if (mRotationDegrees != 0) {
-        uint32_t transform = 0;
-        switch (mRotationDegrees) {
-            case 0: transform = 0; break;
-            case 90: transform = HAL_TRANSFORM_ROT_90; break;
-            case 180: transform = HAL_TRANSFORM_ROT_180; break;
-            case 270: transform = HAL_TRANSFORM_ROT_270; break;
-            default: transform = 0; break;
-        }
-
-        if (transform > 0) {
-            err = native_window_set_buffers_transform(
-                    mNativeWindow.get(), transform);
-            if (err != 0) {
-                ALOGE("native_window_set_buffers_transform failed: %s (%d)",
-                        strerror(-err), -err);
-                return err;
-            }
-        }
-    }
-
-    // Set up the native window.
     OMX_U32 usage = 0;
     err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
     if (err != 0) {
@@ -676,43 +787,32 @@
         usage |= GRALLOC_USAGE_PROTECTED;
     }
 
-    // Make sure to check whether either Stagefright or the video decoder
-    // requested protected buffers.
-    if (usage & GRALLOC_USAGE_PROTECTED) {
-        // Verify that the ANativeWindow sends images directly to
-        // SurfaceFlinger.
-        int queuesToNativeWindow = 0;
-        err = mNativeWindow->query(
-                mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
-                &queuesToNativeWindow);
-        if (err != 0) {
-            ALOGE("error authenticating native window: %d", err);
-            return err;
-        }
-        if (queuesToNativeWindow != 1) {
-            ALOGE("native window could not be authenticated");
-            return PERMISSION_DENIED;
-        }
+    usage |= GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP;
+
+    ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage);
+    return setNativeWindowSizeFormatAndUsage(
+            nativeWindow,
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            def.format.video.eColorFormat,
+            mRotationDegrees,
+            usage);
+}
+
+status_t ACodec::configureOutputBuffersFromNativeWindow(
+        OMX_U32 *bufferCount, OMX_U32 *bufferSize,
+        OMX_U32 *minUndequeuedBuffers) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    if (err == OK) {
+        err = setupNativeWindowSizeFormatAndUsage(mNativeWindow.get());
     }
-
-    int consumerUsage = 0;
-    err = mNativeWindow->query(
-            mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS,
-            &consumerUsage);
-    if (err != 0) {
-        ALOGW("failed to get consumer usage bits. ignoring");
-        err = 0;
-    }
-
-    ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec) + %#x(Consumer) = %#x",
-            omxUsage, usage, consumerUsage, usage | consumerUsage);
-    usage |= consumerUsage;
-    err = native_window_set_usage(
-            mNativeWindow.get(),
-            usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
-
-    if (err != 0) {
-        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+    if (err != OK) {
         return err;
     }
 
@@ -796,6 +896,11 @@
         return err;
     mNumUndequeuedBuffers = minUndequeuedBuffers;
 
+    if (!mStoreMetaDataInOutputBuffers) {
+        static_cast<Surface*>(mNativeWindow.get())
+                ->getIGraphicBufferProducer()->allowAllocation(true);
+    }
+
     ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
          "output port",
          mComponentName.c_str(), bufferCount, bufferSize);
@@ -854,6 +959,11 @@
         }
     }
 
+    if (!mStoreMetaDataInOutputBuffers) {
+        static_cast<Surface*>(mNativeWindow.get())
+                ->getIGraphicBufferProducer()->allowAllocation(false);
+    }
+
     return err;
 }
 
@@ -880,7 +990,9 @@
 
         sp<IMemory> mem = mDealer[kPortIndexOutput]->allocate(
                 sizeof(struct VideoDecoderOutputMetaData));
-        CHECK(mem.get() != NULL);
+        if (mem.get() == NULL) {
+            return NO_MEMORY;
+        }
         info.mData = new ABuffer(mem->pointer(), mem->size());
 
         // we use useBuffer for metadata regardless of quirks
@@ -893,6 +1005,45 @@
              mComponentName.c_str(), info.mBufferID, mem->pointer());
     }
 
+    if (mLegacyAdaptiveExperiment) {
+        // preallocate and preregister buffers
+        static_cast<Surface *>(mNativeWindow.get())
+                ->getIGraphicBufferProducer()->allowAllocation(true);
+
+        ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
+             "output port",
+             mComponentName.c_str(), bufferCount, bufferSize);
+
+        // Dequeue buffers then cancel them all
+        for (OMX_U32 i = 0; i < bufferCount; i++) {
+            BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+
+            ANativeWindowBuffer *buf;
+            err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);
+            if (err != 0) {
+                ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
+                break;
+            }
+
+            sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
+            mOMX->updateGraphicBufferInMeta(
+                    mNode, kPortIndexOutput, graphicBuffer, info->mBufferID);
+            info->mStatus = BufferInfo::OWNED_BY_US;
+            info->mGraphicBuffer = graphicBuffer;
+        }
+
+        for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) {
+            BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+            status_t error = cancelBufferToNativeWindow(info);
+            if (err == OK) {
+                err = error;
+            }
+        }
+
+        static_cast<Surface*>(mNativeWindow.get())
+                ->getIGraphicBufferProducer()->allowAllocation(false);
+    }
+
     mMetaDataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
     return err;
 }
@@ -910,11 +1061,12 @@
           mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get());
 
     --mMetaDataBuffersToSubmit;
-    CHECK_EQ(mOMX->fillBuffer(mNode, info->mBufferID),
-             (status_t)OK);
+    status_t err = mOMX->fillBuffer(mNode, info->mBufferID);
+    if (err == OK) {
+        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+    }
 
-    info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
-    return OK;
+    return err;
 }
 
 status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
@@ -944,26 +1096,57 @@
         return NULL;
     }
 
-    if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) {
-        ALOGE("dequeueBuffer failed.");
-        return NULL;
-    }
+    do {
+        if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) {
+            ALOGE("dequeueBuffer failed.");
+            return NULL;
+        }
 
+        bool stale = false;
+        for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+            BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+
+            if (info->mGraphicBuffer != NULL &&
+                info->mGraphicBuffer->handle == buf->handle) {
+                // Since consumers can attach buffers to BufferQueues, it is possible
+                // that a known yet stale buffer can return from a surface that we
+                // once used.  We can simply ignore this as we have already dequeued
+                // this buffer properly.  NOTE: this does not eliminate all cases,
+                // e.g. it is possible that we have queued the valid buffer to the
+                // NW, and a stale copy of the same buffer gets dequeued - which will
+                // be treated as the valid buffer by ACodec.
+                if (info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+                    ALOGI("dequeued stale buffer %p. discarding", buf);
+                    stale = true;
+                    break;
+                }
+                ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer());
+                info->mStatus = BufferInfo::OWNED_BY_US;
+
+                return info;
+            }
+        }
+
+        // It is also possible to receive a previously unregistered buffer
+        // in non-meta mode. These should be treated as stale buffers. The
+        // same is possible in meta mode, in which case, it will be treated
+        // as a normal buffer, which is not desirable.
+        // TODO: fix this.
+        if (!stale && (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment)) {
+            ALOGI("dequeued unrecognized (stale) buffer %p. discarding", buf);
+            stale = true;
+        }
+        if (stale) {
+            // TODO: detach stale buffer, but there is no API yet to do it.
+            buf = NULL;
+        }
+    } while (buf == NULL);
+
+    // get oldest undequeued buffer
     BufferInfo *oldest = NULL;
     for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
         BufferInfo *info =
             &mBuffers[kPortIndexOutput].editItemAt(i);
-
-        if (info->mGraphicBuffer != NULL &&
-            info->mGraphicBuffer->handle == buf->handle) {
-            CHECK_EQ((int)info->mStatus,
-                     (int)BufferInfo::OWNED_BY_NATIVE_WINDOW);
-
-            info->mStatus = BufferInfo::OWNED_BY_US;
-
-            return info;
-        }
-
         if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW &&
             (oldest == NULL ||
              // avoid potential issues from counter rolling over
@@ -990,7 +1173,7 @@
         CHECK_EQ(metaData->eType, kMetadataBufferTypeGrallocSource);
 
         ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
-                oldest - &mBuffers[kPortIndexOutput][0],
+                (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
                 mDequeueCounter - oldest->mDequeuedAt,
                 metaData->pHandle,
                 oldest->mGraphicBuffer->handle, oldest->mData->base());
@@ -1004,16 +1187,21 @@
 }
 
 status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
+    status_t err = OK;
     for (size_t i = mBuffers[portIndex].size(); i-- > 0;) {
-        CHECK_EQ((status_t)OK, freeBuffer(portIndex, i));
+        status_t err2 = freeBuffer(portIndex, i);
+        if (err == OK) {
+            err = err2;
+        }
     }
 
     mDealer[portIndex].clear();
 
-    return OK;
+    return err;
 }
 
 status_t ACodec::freeOutputBuffersNotOwnedByComponent() {
+    status_t err = OK;
     for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
         BufferInfo *info =
             &mBuffers[kPortIndexOutput].editItemAt(i);
@@ -1022,11 +1210,14 @@
         // or being drained.
         if (info->mStatus != BufferInfo::OWNED_BY_COMPONENT &&
             info->mStatus != BufferInfo::OWNED_BY_DOWNSTREAM) {
-            CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
+            status_t err2 = freeBuffer(kPortIndexOutput, i);
+            if (err == OK) {
+                err = err2;
+            }
         }
     }
 
-    return OK;
+    return err;
 }
 
 status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
@@ -1040,13 +1231,11 @@
         cancelBufferToNativeWindow(info);
     }
 
-    CHECK_EQ(mOMX->freeBuffer(
-                mNode, portIndex, info->mBufferID),
-             (status_t)OK);
-
+    status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBufferID);
+    // remove buffer even if mOMX->freeBuffer fails
     mBuffers[portIndex].removeAt(i);
 
-    return OK;
+    return err;
 }
 
 ACodec::BufferInfo *ACodec::findBufferByID(
@@ -1063,8 +1252,7 @@
         }
     }
 
-    TRESPASS();
-
+    ALOGE("Could not find buffer with ID %u", bufferID);
     return NULL;
 }
 
@@ -1177,6 +1365,7 @@
 
     mIsEncoder = encoder;
 
+
     status_t err = setComponentRole(encoder /* isEncoder */, mime);
 
     if (err != OK) {
@@ -1235,6 +1424,7 @@
     // sps/pps to idr frames, since in metadata mode the bitstream is in an
     // opaque handle, to which we don't have access.
     int32_t video = !strncasecmp(mime, "video/", 6);
+    mIsVideo = video;
     if (encoder && video) {
         OMX_BOOL enable = (OMX_BOOL) (prependSPSPPS
             && msg->findInt32("store-metadata-in-buffers-output", &storeMeta)
@@ -1280,6 +1470,7 @@
     bool haveNativeWindow = msg->findObject("native-window", &obj)
             && obj != NULL && video && !encoder;
     mStoreMetaDataInOutputBuffers = false;
+    mLegacyAdaptiveExperiment = false;
     if (video && !encoder) {
         inputFormat->setInt32("adaptive-playback", false);
 
@@ -1294,9 +1485,8 @@
         }
     }
     if (haveNativeWindow) {
-        sp<NativeWindowWrapper> windowWrapper(
-                static_cast<NativeWindowWrapper *>(obj.get()));
-        sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow();
+        sp<ANativeWindow> nativeWindow =
+            static_cast<ANativeWindow *>(static_cast<Surface *>(obj.get()));
 
         // START of temporary support for automatic FRC - THIS WILL BE REMOVED
         int32_t autoFrc;
@@ -1418,6 +1608,9 @@
                 ALOGV("[%s] storeMetaDataInBuffers succeeded",
                         mComponentName.c_str());
                 mStoreMetaDataInOutputBuffers = true;
+                mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled(
+                        "legacy-adaptive", !msg->contains("no-experiments"));
+
                 inputFormat->setInt32("adaptive-playback", true);
             }
 
@@ -1455,28 +1648,31 @@
         }
 
         if (haveNativeWindow) {
-            sp<NativeWindowWrapper> nativeWindow(
-                    static_cast<NativeWindowWrapper *>(obj.get()));
-            CHECK(nativeWindow != NULL);
-            mNativeWindow = nativeWindow->getNativeWindow();
-
-            native_window_set_scaling_mode(
-                    mNativeWindow.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+            mNativeWindow = static_cast<Surface *>(obj.get());
         }
 
         // initialize native window now to get actual output format
         // TODO: this is needed for some encoders even though they don't use native window
-        CHECK_EQ((status_t)OK, initNativeWindow());
+        err = initNativeWindow();
+        if (err != OK) {
+            return err;
+        }
 
         // fallback for devices that do not handle flex-YUV for native buffers
         if (haveNativeWindow) {
             int32_t requestedColorFormat = OMX_COLOR_FormatUnused;
             if (msg->findInt32("color-format", &requestedColorFormat) &&
                     requestedColorFormat == OMX_COLOR_FormatYUV420Flexible) {
-                CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK);
+                status_t err = getPortFormat(kPortIndexOutput, outputFormat);
+                if (err != OK) {
+                    return err;
+                }
                 int32_t colorFormat = OMX_COLOR_FormatUnused;
                 OMX_U32 flexibleEquivalent = OMX_COLOR_FormatUnused;
-                CHECK(outputFormat->findInt32("color-format", &colorFormat));
+                if (!outputFormat->findInt32("color-format", &colorFormat)) {
+                    ALOGE("ouptut port did not have a color format (wrong domain?)");
+                    return BAD_VALUE;
+                }
                 ALOGD("[%s] Requested output format %#x and got %#x.",
                         mComponentName.c_str(), requestedColorFormat, colorFormat);
                 if (!isFlexibleColorFormat(
@@ -1598,7 +1794,7 @@
             err = setupG711Codec(encoder, sampleRate, numChannels);
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
-        int32_t numChannels, sampleRate, compressionLevel = -1;
+        int32_t numChannels = 0, sampleRate = 0, compressionLevel = -1;
         if (encoder &&
                 (!msg->findInt32("channel-count", &numChannels)
                         || !msg->findInt32("sample-rate", &sampleRate))) {
@@ -1685,13 +1881,26 @@
         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);
-    CHECK_EQ(getPortFormat(kPortIndexOutput, outputFormat), (status_t)OK);
-    mInputFormat = inputFormat;
-    mOutputFormat = outputFormat;
-
+    err = getPortFormat(kPortIndexInput, inputFormat);
+    if (err == OK) {
+        err = getPortFormat(kPortIndexOutput, outputFormat);
+        if (err == OK) {
+            mInputFormat = inputFormat;
+            mOutputFormat = outputFormat;
+        }
+    }
     return err;
 }
 
@@ -1711,6 +1920,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);
@@ -1743,7 +1980,10 @@
         return err;
     }
 
-    CHECK(def.nBufferSize >= size);
+    if (def.nBufferSize < size) {
+        ALOGE("failed to set min buffer size to %zu (is still %u)", size, def.nBufferSize);
+        return FAILED_TRANSACTION;
+    }
 
     return OK;
 }
@@ -2071,7 +2311,9 @@
 }
 
 status_t ACodec::setupG711Codec(bool encoder, int32_t sampleRate, int32_t numChannels) {
-    CHECK(!encoder);  // XXX TODO
+    if (encoder) {
+        return INVALID_OPERATION;
+    }
 
     return setupRawAudioFormat(
             kPortIndexInput, sampleRate, numChannels);
@@ -3180,8 +3422,9 @@
 
     status_t err = mOMX->getParameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
-    CHECK_EQ(err, (status_t)OK);
+    if (err != OK) {
+        return err;
+    }
 
     if (portIndex == kPortIndexInput) {
         // XXX Need a (much) better heuristic to compute input buffer sizes.
@@ -3191,7 +3434,10 @@
         }
     }
 
-    CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
+    if (def.eDomain != OMX_PortDomainVideo) {
+        ALOGE("expected video port, got %s(%d)", asString(def.eDomain), def.eDomain);
+        return FAILED_TRANSACTION;
+    }
 
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
@@ -3468,17 +3714,20 @@
 }
 
 status_t ACodec::getPortFormat(OMX_U32 portIndex, sp<AMessage> &notify) {
-    // TODO: catch errors an return them instead of using CHECK
+    const char *niceIndex = portIndex == kPortIndexInput ? "input" : "output";
     OMX_PARAM_PORTDEFINITIONTYPE def;
     InitOMXParams(&def);
     def.nPortIndex = portIndex;
 
-    CHECK_EQ(mOMX->getParameter(
-                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)),
-             (status_t)OK);
+    status_t err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    if (err != OK) {
+        return err;
+    }
 
-    CHECK_EQ((int)def.eDir,
-            (int)(portIndex == kPortIndexOutput ? OMX_DirOutput : OMX_DirInput));
+    if (def.eDir != (portIndex == kPortIndexOutput ? OMX_DirOutput : OMX_DirInput)) {
+        ALOGE("unexpected dir: %s(%d) on %s port", asString(def.eDir), def.eDir, niceIndex);
+        return BAD_VALUE;
+    }
 
     switch (def.eDomain) {
         case OMX_PortDomainVideo:
@@ -3541,12 +3790,16 @@
                         rect.nHeight = videoDef->nFrameHeight;
                     }
 
-                    CHECK_GE(rect.nLeft, 0);
-                    CHECK_GE(rect.nTop, 0);
-                    CHECK_GE(rect.nWidth, 0u);
-                    CHECK_GE(rect.nHeight, 0u);
-                    CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
-                    CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
+                    if (rect.nLeft < 0 ||
+                        rect.nTop < 0 ||
+                        rect.nLeft + rect.nWidth > videoDef->nFrameWidth ||
+                        rect.nTop + rect.nHeight > videoDef->nFrameHeight) {
+                        ALOGE("Wrong cropped rect (%d, %d) - (%u, %u) vs. frame (%u, %u)",
+                                rect.nLeft, rect.nTop,
+                                rect.nLeft + rect.nWidth, rect.nTop + rect.nHeight,
+                                videoDef->nFrameWidth, videoDef->nFrameHeight);
+                        return BAD_VALUE;
+                    }
 
                     notify->setRect(
                             "crop",
@@ -3603,7 +3856,13 @@
 
                 default:
                 {
-                    CHECK(mIsEncoder ^ (portIndex == kPortIndexInput));
+                    if (mIsEncoder ^ (portIndex == kPortIndexOutput)) {
+                        // should be CodingUnused
+                        ALOGE("Raw port video compression format is %s(%d)",
+                                asString(videoDef->eCompressionFormat),
+                                videoDef->eCompressionFormat);
+                        return BAD_VALUE;
+                    }
                     AString mime;
                     if (GetMimeTypeForVideoCoding(
                         videoDef->eCompressionFormat, &mime) != OK) {
@@ -3634,20 +3893,25 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioPcm,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                            mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
-                    CHECK_GT(params.nChannels, 0);
-                    CHECK(params.nChannels == 1 || params.bInterleaved);
-                    CHECK_EQ(params.nBitPerSample, 16u);
-
-                    CHECK_EQ((int)params.eNumData,
-                             (int)OMX_NumericalDataSigned);
-
-                    CHECK_EQ((int)params.ePCMMode,
-                             (int)OMX_AUDIO_PCMModeLinear);
+                    if (params.nChannels <= 0
+                            || (params.nChannels != 1 && !params.bInterleaved)
+                            || params.nBitPerSample != 16u
+                            || params.eNumData != OMX_NumericalDataSigned
+                            || params.ePCMMode != OMX_AUDIO_PCMModeLinear) {
+                        ALOGE("unsupported PCM port: %u channels%s, %u-bit, %s(%d), %s(%d) mode ",
+                                params.nChannels,
+                                params.bInterleaved ? " interleaved" : "",
+                                params.nBitPerSample,
+                                asString(params.eNumData), params.eNumData,
+                                asString(params.ePCMMode), params.ePCMMode);
+                        return FAILED_TRANSACTION;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3665,10 +3929,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioAac,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                            mNode, OMX_IndexParamAudioAac, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3682,10 +3947,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioAmr,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                            mNode, OMX_IndexParamAudioAmr, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setInt32("channel-count", 1);
                     if (params.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0) {
@@ -3708,10 +3974,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioFlac,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                            mNode, OMX_IndexParamAudioFlac, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_FLAC);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3725,10 +3992,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioMp3,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                            mNode, OMX_IndexParamAudioMp3, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MPEG);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3742,10 +4010,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioVorbis,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                            mNode, OMX_IndexParamAudioVorbis, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_VORBIS);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3759,11 +4028,12 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ((status_t)OK, mOMX->getParameter(
-                            mNode,
-                            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
-                            &params,
-                            sizeof(params)));
+                    err = mOMX->getParameter(
+                            mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3,
+                            &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC3);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3777,11 +4047,12 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ((status_t)OK, mOMX->getParameter(
-                            mNode,
-                            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3,
-                            &params,
-                            sizeof(params)));
+                    err = mOMX->getParameter(
+                            mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidEac3,
+                            &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_EAC3);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3795,11 +4066,12 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ((status_t)OK, mOMX->getParameter(
-                            mNode,
-                            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus,
-                            &params,
-                            sizeof(params)));
+                    err = mOMX->getParameter(
+                            mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus,
+                            &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_OPUS);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3813,11 +4085,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ((status_t)OK, mOMX->getParameter(
-                            mNode,
-                            (OMX_INDEXTYPE)OMX_IndexParamAudioPcm,
-                            &params,
-                            sizeof(params)));
+                    err = mOMX->getParameter(
+                            mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioPcm, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     const char *mime = NULL;
                     if (params.ePCMMode == OMX_AUDIO_PCMModeMULaw) {
@@ -3839,10 +4111,11 @@
                     InitOMXParams(&params);
                     params.nPortIndex = portIndex;
 
-                    CHECK_EQ(mOMX->getParameter(
-                                mNode, OMX_IndexParamAudioPcm,
-                                &params, sizeof(params)),
-                             (status_t)OK);
+                    err = mOMX->getParameter(
+                                mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+                    if (err != OK) {
+                        return err;
+                    }
 
                     notify->setString("mime", MEDIA_MIMETYPE_AUDIO_MSGSM);
                     notify->setInt32("channel-count", params.nChannels);
@@ -3851,14 +4124,16 @@
                 }
 
                 default:
-                    ALOGE("UNKNOWN AUDIO CODING: %d\n", audioDef->eEncoding);
-                    TRESPASS();
+                    ALOGE("Unsupported audio coding: %s(%d)\n",
+                            asString(audioDef->eEncoding), audioDef->eEncoding);
+                    return BAD_TYPE;
             }
             break;
         }
 
         default:
-            TRESPASS();
+            ALOGE("Unsupported domain: %s(%d)", asString(def.eDomain), def.eDomain);
+            return BAD_TYPE;
     }
 
     return OK;
@@ -3868,7 +4143,10 @@
     sp<AMessage> notify = mBaseOutputFormat->dup();
     notify->setInt32("what", kWhatOutputFormatChanged);
 
-    CHECK_EQ(getPortFormat(kPortIndexOutput, notify), (status_t)OK);
+    if (getPortFormat(kPortIndexOutput, notify) != OK) {
+        ALOGE("[%s] Failed to get port format to send format change", mComponentName.c_str());
+        return;
+    }
 
     AString mime;
     CHECK(notify->findString("mime", &mime));
@@ -3888,9 +4166,7 @@
         if (mSkipCutBuffer != NULL) {
             size_t prevbufsize = mSkipCutBuffer->size();
             if (prevbufsize != 0) {
-                ALOGW("Replacing SkipCutBuffer holding %d "
-                      "bytes",
-                      prevbufsize);
+                ALOGW("Replacing SkipCutBuffer holding %zu bytes", prevbufsize);
             }
         }
         mSkipCutBuffer = new SkipCutBuffer(
@@ -3921,150 +4197,6 @@
     notify->post();
 }
 
-status_t ACodec::pushBlankBuffersToNativeWindow() {
-    status_t err = NO_ERROR;
-    ANativeWindowBuffer* anb = NULL;
-    int numBufs = 0;
-    int minUndequeuedBufs = 0;
-
-    // We need to reconnect to the ANativeWindow as a CPU client to ensure that
-    // no frames get dropped by SurfaceFlinger assuming that these are video
-    // frames.
-    err = native_window_api_disconnect(mNativeWindow.get(),
-            NATIVE_WINDOW_API_MEDIA);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
-                strerror(-err), -err);
-        return err;
-    }
-
-    err = native_window_api_connect(mNativeWindow.get(),
-            NATIVE_WINDOW_API_CPU);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
-                strerror(-err), -err);
-        return err;
-    }
-
-    err = native_window_set_buffers_geometry(mNativeWindow.get(), 1, 1,
-            HAL_PIXEL_FORMAT_RGBX_8888);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    err = native_window_set_scaling_mode(mNativeWindow.get(),
-                NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank_frames: set_scaling_mode failed: %s (%d)",
-              strerror(-err), -err);
-        goto error;
-    }
-
-    err = native_window_set_usage(mNativeWindow.get(),
-            GRALLOC_USAGE_SW_WRITE_OFTEN);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: set_usage failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    err = mNativeWindow->query(mNativeWindow.get(),
-            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
-                "failed: %s (%d)", strerror(-err), -err);
-        goto error;
-    }
-
-    numBufs = minUndequeuedBufs + 1;
-    err = native_window_set_buffer_count(mNativeWindow.get(), numBufs);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    // We  push numBufs + 1 buffers to ensure that we've drawn into the same
-    // buffer twice.  This should guarantee that the buffer has been displayed
-    // on the screen and then been replaced, so an previous video frames are
-    // guaranteed NOT to be currently displayed.
-    for (int i = 0; i < numBufs + 1; i++) {
-        err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
-
-        // Fill the buffer with the a 1x1 checkerboard pattern ;)
-        uint32_t* img = NULL;
-        err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: lock failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        *img = 0;
-
-        err = buf->unlock();
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: unlock failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        err = mNativeWindow->queueBuffer(mNativeWindow.get(),
-                buf->getNativeBuffer(), -1);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        anb = NULL;
-    }
-
-error:
-
-    if (err != NO_ERROR) {
-        // Clean up after an error.
-        if (anb != NULL) {
-            mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1);
-        }
-
-        native_window_api_disconnect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_CPU);
-        native_window_api_connect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_MEDIA);
-
-        return err;
-    } else {
-        // Clean up after success.
-        err = native_window_api_disconnect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_CPU);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
-                    strerror(-err), -err);
-            return err;
-        }
-
-        err = native_window_api_connect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_MEDIA);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
-                    strerror(-err), -err);
-            return err;
-        }
-
-        return NO_ERROR;
-    }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 ACodec::PortDescription::PortDescription() {
@@ -4137,7 +4269,26 @@
             return onOMXMessage(msg);
         }
 
+        case ACodec::kWhatSetSurface:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<RefBase> obj;
+            CHECK(msg->findObject("surface", &obj));
+
+            status_t err =
+                ADebug::isExperimentEnabled("legacy-setsurface") ? BAD_VALUE :
+                        mCodec->handleSetSurface(static_cast<Surface *>(obj.get()));
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+            response->postReply(replyID);
+            break;
+        }
+
         case ACodec::kWhatCreateInputSurface:
+        case ACodec::kWhatSetInputSurface:
         case ACodec::kWhatSignalEndOfInputStream:
         {
             // This may result in an app illegal state exception.
@@ -4180,7 +4331,7 @@
 
     // there is a possibility that this is an outstanding message for a
     // codec that we have already destroyed
-    if (mCodec->mNode == NULL) {
+    if (mCodec->mNode == 0) {
         ALOGI("ignoring message as already freed component: %s",
                 msg->debugString().c_str());
         return true;
@@ -4188,7 +4339,10 @@
 
     IOMX::node_id nodeID;
     CHECK(msg->findInt32("node", (int32_t*)&nodeID));
-    CHECK_EQ(nodeID, mCodec->mNode);
+    if (nodeID != mCodec->mNode) {
+        ALOGE("Unexpected message for nodeID: %u, should have been %u", nodeID, mCodec->mNode);
+        return false;
+    }
 
     switch (type) {
         case omx_message::EVENT:
@@ -4244,21 +4398,21 @@
         }
 
         default:
-            TRESPASS();
-            break;
+            ALOGE("Unexpected message type: %d", type);
+            return false;
     }
 }
 
 bool ACodec::BaseState::onOMXEvent(
         OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
     if (event != OMX_EventError) {
-        ALOGV("[%s] EVENT(%d, 0x%08lx, 0x%08lx)",
+        ALOGV("[%s] EVENT(%d, 0x%08x, 0x%08x)",
              mCodec->mComponentName.c_str(), event, data1, data2);
 
         return false;
     }
 
-    ALOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1);
+    ALOGE("[%s] ERROR(0x%08x)", mCodec->mComponentName.c_str(), data1);
 
     // verify OMX component sends back an error we expect.
     OMX_ERRORTYPE omxError = (OMX_ERRORTYPE)data1;
@@ -4272,7 +4426,7 @@
 }
 
 bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
-    ALOGV("[%s] onOMXEmptyBufferDone %p",
+    ALOGV("[%s] onOMXEmptyBufferDone %u",
          mCodec->mComponentName.c_str(), bufferID);
 
     BufferInfo *info =
@@ -4298,12 +4452,10 @@
             postFillThisBuffer(info);
             break;
 
+        case FREE_BUFFERS:
         default:
-        {
-            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
-            TRESPASS();  // Not currently used
-            break;
-        }
+            ALOGE("SHOULD NOT REACH HERE: cannot free empty output buffers");
+            return false;
     }
 
     return true;
@@ -4398,7 +4550,7 @@
                 }
 
                 if (buffer != info->mData) {
-                    ALOGV("[%s] Needs to copy input data for buffer %p. (%p != %p)",
+                    ALOGV("[%s] Needs to copy input data for buffer %u. (%p != %p)",
                          mCodec->mComponentName.c_str(),
                          bufferID,
                          buffer.get(), info->mData.get());
@@ -4408,18 +4560,18 @@
                 }
 
                 if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
-                    ALOGV("[%s] calling emptyBuffer %p w/ codec specific data",
+                    ALOGV("[%s] calling emptyBuffer %u w/ codec specific data",
                          mCodec->mComponentName.c_str(), bufferID);
                 } else if (flags & OMX_BUFFERFLAG_EOS) {
-                    ALOGV("[%s] calling emptyBuffer %p w/ EOS",
+                    ALOGV("[%s] calling emptyBuffer %u w/ EOS",
                          mCodec->mComponentName.c_str(), bufferID);
                 } else {
 #if TRACK_BUFFER_TIMING
-                    ALOGI("[%s] calling emptyBuffer %p w/ time %lld us",
-                         mCodec->mComponentName.c_str(), bufferID, timeUs);
+                    ALOGI("[%s] calling emptyBuffer %u w/ time %lld us",
+                         mCodec->mComponentName.c_str(), bufferID, (long long)timeUs);
 #else
-                    ALOGV("[%s] calling emptyBuffer %p w/ time %lld us",
-                         mCodec->mComponentName.c_str(), bufferID, timeUs);
+                    ALOGV("[%s] calling emptyBuffer %u w/ time %lld us",
+                         mCodec->mComponentName.c_str(), bufferID, (long long)timeUs);
 #endif
                 }
 
@@ -4473,7 +4625,7 @@
                          mCodec->mComponentName.c_str());
                 }
 
-                ALOGV("[%s] calling emptyBuffer %p signalling EOS",
+                ALOGV("[%s] calling emptyBuffer %u signalling EOS",
                      mCodec->mComponentName.c_str(), bufferID);
 
                 CHECK_EQ(mCodec->mOMX->emptyBuffer(
@@ -4493,8 +4645,11 @@
             break;
         }
 
+        case FREE_BUFFERS:
+            break;
+
         default:
-            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+            ALOGE("invalid port mode: %d", mode);
             break;
     }
 }
@@ -4638,14 +4793,14 @@
             break;
         }
 
-        default:
-        {
-            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
-
+        case FREE_BUFFERS:
             CHECK_EQ((status_t)OK,
                      mCodec->freeBuffer(kPortIndexOutput, index));
             break;
-        }
+
+        default:
+            ALOGE("Invalid port mode: %d", mode);
+            return false;
     }
 
     return true;
@@ -4660,10 +4815,9 @@
     CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
 
     android_native_rect_t crop;
-    if (msg->findRect("crop",
-            &crop.left, &crop.top, &crop.right, &crop.bottom)) {
-        CHECK_EQ(0, native_window_set_crop(
-                mCodec->mNativeWindow.get(), &crop));
+    if (msg->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)) {
+        status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
+        ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
     }
 
     int32_t render;
@@ -4748,14 +4902,16 @@
             break;
         }
 
-        default:
+        case FREE_BUFFERS:
         {
-            CHECK_EQ((int)mode, (int)FREE_BUFFERS);
-
             CHECK_EQ((status_t)OK,
                      mCodec->freeBuffer(kPortIndexOutput, index));
             break;
         }
+
+        default:
+            ALOGE("Invalid port mode: %d", mode);
+            return;
     }
 }
 
@@ -4774,7 +4930,7 @@
     }
 
     mCodec->mNativeWindow.clear();
-    mCodec->mNode = NULL;
+    mCodec->mNode = 0;
     mCodec->mOMX.clear();
     mCodec->mQuirks = 0;
     mCodec->mFlags = 0;
@@ -4852,10 +5008,13 @@
 bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
     ALOGV("onAllocateComponent");
 
-    CHECK(mCodec->mNode == NULL);
+    CHECK(mCodec->mNode == 0);
 
     OMXClient client;
-    CHECK_EQ(client.connect(), (status_t)OK);
+    if (client.connect() != OK) {
+        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
+        return false;
+    }
 
     sp<IOMX> omx = client.interface();
 
@@ -4900,8 +5059,9 @@
     }
 
     sp<CodecObserver> observer = new CodecObserver;
-    IOMX::node_id node = NULL;
+    IOMX::node_id node = 0;
 
+    status_t err = OMX_ErrorComponentNotFound;
     for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
             ++matchIndex) {
         componentName = matchingCodecs.itemAt(matchIndex).mName.string();
@@ -4910,7 +5070,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) {
@@ -4919,18 +5079,18 @@
             ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
         }
 
-        node = NULL;
+        node = 0;
     }
 
-    if (node == NULL) {
+    if (node == 0) {
         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;
     }
 
@@ -5029,6 +5189,13 @@
             break;
         }
 
+        case ACodec::kWhatSetInputSurface:
+        {
+            onSetInputSurface(msg);
+            handled = true;
+            break;
+        }
+
         case ACodec::kWhatStart:
         {
             onStart();
@@ -5070,7 +5237,7 @@
         const sp<AMessage> &msg) {
     ALOGV("onConfigureComponent");
 
-    CHECK(mCodec->mNode != NULL);
+    CHECK(mCodec->mNode != 0);
 
     AString mime;
     CHECK(msg->findString("mime", &mime));
@@ -5096,20 +5263,10 @@
     return true;
 }
 
-void ACodec::LoadedState::onCreateInputSurface(
-        const sp<AMessage> & /* msg */) {
-    ALOGV("onCreateInputSurface");
+status_t ACodec::LoadedState::setupInputSurface() {
+    status_t err = OK;
 
-    sp<AMessage> notify = mCodec->mNotify->dup();
-    notify->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
-
-    sp<IGraphicBufferProducer> bufferProducer;
-    status_t err;
-
-    err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput,
-            &bufferProducer);
-
-    if (err == OK && mCodec->mRepeatFrameDelayUs > 0ll) {
+    if (mCodec->mRepeatFrameDelayUs > 0ll) {
         err = mCodec->mOMX->setInternalOption(
                 mCodec->mNode,
                 kPortIndexInput,
@@ -5122,10 +5279,11 @@
                   "frames (err %d)",
                   mCodec->mComponentName.c_str(),
                   err);
+            return err;
         }
     }
 
-    if (err == OK && mCodec->mMaxPtsGapUs > 0ll) {
+    if (mCodec->mMaxPtsGapUs > 0ll) {
         err = mCodec->mOMX->setInternalOption(
                 mCodec->mNode,
                 kPortIndexInput,
@@ -5137,10 +5295,11 @@
             ALOGE("[%s] Unable to configure max timestamp gap (err %d)",
                     mCodec->mComponentName.c_str(),
                     err);
+            return err;
         }
     }
 
-    if (err == OK && mCodec->mMaxFps > 0) {
+    if (mCodec->mMaxFps > 0) {
         err = mCodec->mOMX->setInternalOption(
                 mCodec->mNode,
                 kPortIndexInput,
@@ -5152,10 +5311,11 @@
             ALOGE("[%s] Unable to configure max fps (err %d)",
                     mCodec->mComponentName.c_str(),
                     err);
+            return err;
         }
     }
 
-    if (err == OK && mCodec->mTimePerCaptureUs > 0ll
+    if (mCodec->mTimePerCaptureUs > 0ll
             && mCodec->mTimePerFrameUs > 0ll) {
         int64_t timeLapse[2];
         timeLapse[0] = mCodec->mTimePerFrameUs;
@@ -5171,10 +5331,11 @@
             ALOGE("[%s] Unable to configure time lapse (err %d)",
                     mCodec->mComponentName.c_str(),
                     err);
+            return err;
         }
     }
 
-    if (err == OK && mCodec->mCreateInputBuffersSuspended) {
+    if (mCodec->mCreateInputBuffersSuspended) {
         bool suspend = true;
         err = mCodec->mOMX->setInternalOption(
                 mCodec->mNode,
@@ -5187,9 +5348,28 @@
             ALOGE("[%s] Unable to configure option to suspend (err %d)",
                   mCodec->mComponentName.c_str(),
                   err);
+            return err;
         }
     }
 
+    return OK;
+}
+
+void ACodec::LoadedState::onCreateInputSurface(
+        const sp<AMessage> & /* msg */) {
+    ALOGV("onCreateInputSurface");
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
+
+    sp<IGraphicBufferProducer> bufferProducer;
+    status_t err = mCodec->mOMX->createInputSurface(
+            mCodec->mNode, kPortIndexInput, &bufferProducer);
+
+    if (err == OK) {
+        err = setupInputSurface();
+    }
+
     if (err == OK) {
         notify->setObject("input-surface",
                 new BufferProducerWrapper(bufferProducer));
@@ -5204,6 +5384,35 @@
     notify->post();
 }
 
+void ACodec::LoadedState::onSetInputSurface(
+        const sp<AMessage> &msg) {
+    ALOGV("onSetInputSurface");
+
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", CodecBase::kWhatInputSurfaceAccepted);
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("input-surface", &obj));
+    sp<PersistentSurface> surface = static_cast<PersistentSurface *>(obj.get());
+
+    status_t err = mCodec->mOMX->setInputSurface(
+            mCodec->mNode, kPortIndexInput, surface->getBufferConsumer());
+
+    if (err == OK) {
+        err = setupInputSurface();
+    }
+
+    if (err != OK) {
+        // Can't use mCodec->signalError() here -- MediaCodec won't forward
+        // the error through because it's in the "configured" state.  We
+        // send a kWhatInputSurfaceAccepted with an error value instead.
+        ALOGE("[%s] onSetInputSurface returning error %d",
+                mCodec->mComponentName.c_str(), err);
+        notify->setInt32("err", err);
+    }
+    notify->post();
+}
+
 void ACodec::LoadedState::onStart() {
     ALOGV("onStart");
 
@@ -5410,7 +5619,7 @@
             CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
         }
 
-        ALOGV("[%s] calling fillBuffer %p",
+        ALOGV("[%s] calling fillBuffer %u",
              mCodec->mComponentName.c_str(), info->mBufferID);
 
         CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
@@ -5438,7 +5647,10 @@
     submitOutputBuffers();
 
     // Post all available input buffers
-    CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
+    if (mCodec->mBuffers[kPortIndexInput].size() == 0u) {
+        ALOGW("[%s] we don't have any input buffers to resume", mCodec->mComponentName.c_str());
+    }
+
     for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); i++) {
         BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
         if (info->mStatus == BufferInfo::OWNED_BY_US) {
@@ -5484,7 +5696,7 @@
         case kWhatFlush:
         {
             ALOGV("[%s] ExecutingState flushing now "
-                 "(codec owns %d/%d input, %d/%d output).",
+                 "(codec owns %zu/%zu input, %zu/%zu output).",
                     mCodec->mComponentName.c_str(),
                     mCodec->countBuffersOwnedByComponent(kPortIndexInput),
                     mCodec->mBuffers[kPortIndexInput].size(),
@@ -5632,6 +5844,15 @@
         }
     }
 
+    float rate;
+    if (params->findFloat("operating-rate", &rate) && rate > 0) {
+        status_t err = setOperatingRate(rate, mIsVideo);
+        if (err != OK) {
+            ALOGE("Failed to set parameter 'operating-rate' (err %d)", err);
+            return err;
+        }
+    }
+
     return OK;
 }
 
@@ -5666,7 +5887,7 @@
             } else if (data2 == OMX_IndexConfigCommonOutputCrop) {
                 mCodec->mSentFormat = false;
             } else {
-                ALOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
+                ALOGV("[%s] OMX_EventPortSettingsChanged 0x%08x",
                      mCodec->mComponentName.c_str(), data2);
             }
 
@@ -5772,7 +5993,10 @@
 
                 return true;
             } else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
-                CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+                if (data2 != (OMX_U32)kPortIndexOutput) {
+                    ALOGW("ignoring EventCmdComplete OMX_CommandPortEnable for port %u", data2);
+                    return false;
+                }
 
                 mCodec->mSentFormat = false;
 
@@ -5811,7 +6035,7 @@
         {
             // Don't send me a flush request if you previously wanted me
             // to shutdown.
-            TRESPASS();
+            ALOGE("Got flush request in IdleToLoadedState");
             break;
         }
 
@@ -5880,7 +6104,7 @@
             // them has made it to the display.  This allows the OMX
             // component teardown to zero out any protected buffers
             // without the risk of scanning out one of those buffers.
-            mCodec->pushBlankBuffersToNativeWindow();
+            pushBlankBuffersToNativeWindow(mCodec->mNativeWindow.get());
         }
 
         mCodec->changeState(mCodec->mIdleToLoadedState);
@@ -5923,7 +6147,7 @@
         {
             // Don't send me a flush request if you previously wanted me
             // to shutdown.
-            TRESPASS();
+            ALOGW("Ignoring flush request in ExecutingToIdleState");
             break;
         }
 
@@ -5996,8 +6220,8 @@
 
 bool ACodec::FlushingState::onOMXEvent(
         OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
-    ALOGV("[%s] FlushingState onOMXEvent(%d,%ld)",
-            mCodec->mComponentName.c_str(), event, data1);
+    ALOGV("[%s] FlushingState onOMXEvent(%u,%d)",
+            mCodec->mComponentName.c_str(), event, (OMX_S32)data1);
 
     switch (event) {
         case OMX_EventCmdComplete:
@@ -6005,19 +6229,28 @@
             CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush);
 
             if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
-                CHECK(!mFlushComplete[data2]);
+                if (mFlushComplete[data2]) {
+                    ALOGW("Flush already completed for %s port",
+                            data2 == kPortIndexInput ? "input" : "output");
+                    return true;
+                }
                 mFlushComplete[data2] = true;
 
                 if (mFlushComplete[kPortIndexInput]
                         && mFlushComplete[kPortIndexOutput]) {
                     changeStateIfWeOwnAllBuffers();
                 }
-            } else {
-                CHECK_EQ(data2, OMX_ALL);
-                CHECK(mFlushComplete[kPortIndexInput]);
-                CHECK(mFlushComplete[kPortIndexOutput]);
+            } else if (data2 == OMX_ALL) {
+                if (!mFlushComplete[kPortIndexInput] || !mFlushComplete[kPortIndexOutput]) {
+                    ALOGW("received flush complete event for OMX_ALL before ports have been"
+                            "flushed (%d/%d)",
+                            mFlushComplete[kPortIndexInput], mFlushComplete[kPortIndexOutput]);
+                    return false;
+                }
 
                 changeStateIfWeOwnAllBuffers();
+            } else {
+                ALOGW("data2 not OMX_ALL but %u in EventCmdComplete CommandFlush", data2);
             }
 
             return true;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index a2cbdaf..6010558 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -12,6 +12,7 @@
         AudioPlayer.cpp                   \
         AudioSource.cpp                   \
         AwesomePlayer.cpp                 \
+        CallbackDataSource.cpp            \
         CameraSource.cpp                  \
         CameraSourceTimeLapse.cpp         \
         ClockEstimator.cpp                \
@@ -34,6 +35,7 @@
         MediaClock.cpp                    \
         MediaCodec.cpp                    \
         MediaCodecList.cpp                \
+        MediaCodecListOverrides.cpp       \
         MediaCodecSource.cpp              \
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
@@ -55,6 +57,7 @@
         StagefrightMediaScanner.cpp       \
         StagefrightMetadataRetriever.cpp  \
         SurfaceMediaSource.cpp            \
+        SurfaceUtils.cpp                  \
         ThrottledSource.cpp               \
         TimeSource.cpp                    \
         TimedEventQueue.cpp               \
@@ -121,7 +124,8 @@
         libdl \
         libRScpp \
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS
+LOCAL_CLANG := true
 
 LOCAL_MODULE:= libstagefright
 
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index e24824b..dd9d393 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -408,11 +408,22 @@
     }
 }
 
-status_t AudioPlayer::setPlaybackRatePermille(int32_t ratePermille) {
+status_t AudioPlayer::setPlaybackRate(const AudioPlaybackRate &rate) {
     if (mAudioSink.get() != NULL) {
-        return mAudioSink->setPlaybackRatePermille(ratePermille);
+        return mAudioSink->setPlaybackRate(rate);
     } else if (mAudioTrack != 0){
-        return mAudioTrack->setSampleRate(ratePermille * mSampleRate / 1000);
+        return mAudioTrack->setPlaybackRate(rate);
+    } else {
+        return NO_INIT;
+    }
+}
+
+status_t AudioPlayer::getPlaybackRate(AudioPlaybackRate *rate /* nonnull */) {
+    if (mAudioSink.get() != NULL) {
+        return mAudioSink->getPlaybackRate(rate);
+    } else if (mAudioTrack != 0) {
+        *rate = mAudioTrack->getPlaybackRate();
+        return OK;
     } else {
         return NO_INIT;
     }
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 804f131..e5a6a9b 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -50,7 +50,8 @@
 }
 
 AudioSource::AudioSource(
-        audio_source_t inputSource, uint32_t sampleRate, uint32_t channelCount)
+        audio_source_t inputSource, const String16 &opPackageName, uint32_t sampleRate,
+        uint32_t channelCount)
     : mStarted(false),
       mSampleRate(sampleRate),
       mPrevSampleTimeUs(0),
@@ -78,6 +79,7 @@
         mRecord = new AudioRecord(
                     inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
                     audio_channel_in_mask_from_count(channelCount),
+                    opPackageName,
                     (size_t) (bufCount * frameCount),
                     AudioRecordCallbackFunction,
                     this,
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 87eef1e..c178091 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -241,6 +241,8 @@
 
     mClockEstimator = new WindowedLinearFitEstimator();
 
+    mPlaybackSettings = AUDIO_PLAYBACK_RATE_DEFAULT;
+
     reset();
 }
 
@@ -341,6 +343,7 @@
 
     reset_l();
 
+    fd = dup(fd);
     sp<DataSource> dataSource = new FileSource(fd, offset, length);
 
     status_t err = dataSource->initCheck();
@@ -360,7 +363,7 @@
     return setDataSource_l(dataSource);
 }
 
-status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source) {
+status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source __unused) {
     return INVALID_OPERATION;
 }
 
@@ -422,7 +425,7 @@
 
     mBitrate = totalBitRate;
 
-    ALOGV("mBitrate = %lld bits/sec", mBitrate);
+    ALOGV("mBitrate = %lld bits/sec", (long long)mBitrate);
 
     {
         Mutex::Autolock autoLock(mStatsLock);
@@ -1009,6 +1012,10 @@
                 return err;
             }
         }
+
+        if (mAudioPlayer != NULL) {
+            mAudioPlayer->setPlaybackRate(mPlaybackSettings);
+        }
     }
 
     if (mTimeSource == NULL && mAudioPlayer == NULL) {
@@ -1131,6 +1138,10 @@
     }
 
     if (err == OK) {
+        err = mAudioPlayer->setPlaybackRate(mPlaybackSettings);
+    }
+
+    if (err == OK) {
         modifyFlags(AUDIO_RUNNING, SET);
 
         mWatchForAudioEOS = true;
@@ -1722,7 +1733,7 @@
     // we are now resuming.  Signal new position to media time provider.
     // Cannot signal another SEEK_COMPLETE, as existing clients may not expect
     // multiple SEEK_COMPLETE responses to a single seek() request.
-    if (mSeekNotificationSent && abs(mSeekTimeUs - videoTimeUs) > 10000) {
+    if (mSeekNotificationSent && llabs((long long)(mSeekTimeUs - videoTimeUs)) > 10000) {
         // notify if we are resuming more than 10ms away from desired seek time
         notifyListener_l(MEDIA_SKIPPED);
     }
@@ -2358,7 +2369,7 @@
                         }
 
                         CHECK_GE(metaDataSize, 0ll);
-                        ALOGV("metaDataSize = %lld bytes", metaDataSize);
+                        ALOGV("metaDataSize = %lld bytes", (long long)metaDataSize);
                     }
 
                     usleep(200000);
@@ -2553,14 +2564,6 @@
         {
             return setCacheStatCollectFreq(request);
         }
-        case KEY_PARAMETER_PLAYBACK_RATE_PERMILLE:
-        {
-            if (mAudioPlayer != NULL) {
-                return mAudioPlayer->setPlaybackRatePermille(request.readInt32());
-            } else {
-                return NO_INIT;
-            }
-        }
         default:
         {
             return ERROR_UNSUPPORTED;
@@ -2597,6 +2600,58 @@
     }
 }
 
+status_t AwesomePlayer::setPlaybackSettings(const AudioPlaybackRate &rate) {
+    Mutex::Autolock autoLock(mLock);
+    // cursory sanity check for non-audio and paused cases
+    if ((rate.mSpeed != 0.f && rate.mSpeed < AUDIO_TIMESTRETCH_SPEED_MIN)
+        || rate.mSpeed > AUDIO_TIMESTRETCH_SPEED_MAX
+        || rate.mPitch < AUDIO_TIMESTRETCH_SPEED_MIN
+        || rate.mPitch > AUDIO_TIMESTRETCH_SPEED_MAX) {
+        return BAD_VALUE;
+    }
+
+    status_t err = OK;
+    if (rate.mSpeed == 0.f) {
+        if (mFlags & PLAYING) {
+            modifyFlags(CACHE_UNDERRUN, CLEAR); // same as pause
+            err = pause_l();
+        }
+        if (err == OK) {
+            // save settings (using old speed) in case player is resumed
+            AudioPlaybackRate newRate = rate;
+            newRate.mSpeed = mPlaybackSettings.mSpeed;
+            mPlaybackSettings = newRate;
+        }
+        return err;
+    }
+    if (mAudioPlayer != NULL) {
+        err = mAudioPlayer->setPlaybackRate(rate);
+    }
+    if (err == OK) {
+        mPlaybackSettings = rate;
+        if (!(mFlags & PLAYING)) {
+            play_l();
+        }
+    }
+    return err;
+}
+
+status_t AwesomePlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
+    if (mAudioPlayer != NULL) {
+        status_t err = mAudioPlayer->getPlaybackRate(rate);
+        if (err == OK) {
+            mPlaybackSettings = *rate;
+            Mutex::Autolock autoLock(mLock);
+            if (!(mFlags & PLAYING)) {
+                rate->mSpeed = 0.f;
+            }
+        }
+        return err;
+    }
+    *rate = mPlaybackSettings;
+    return OK;
+}
+
 status_t AwesomePlayer::getTrackInfo(Parcel *reply) const {
     Mutex::Autolock autoLock(mLock);
     size_t trackCount = mExtractor->countTracks();
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
new file mode 100644
index 0000000..41f0175
--- /dev/null
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CallbackDataSource"
+#include <utils/Log.h>
+
+#include "include/CallbackDataSource.h"
+
+#include <binder/IMemory.h>
+#include <media/IDataSource.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <algorithm>
+
+namespace android {
+
+CallbackDataSource::CallbackDataSource(
+    const sp<IDataSource>& binderDataSource)
+    : mIDataSource(binderDataSource) {
+    // Set up the buffer to read into.
+    mMemory = mIDataSource->getIMemory();
+}
+
+CallbackDataSource::~CallbackDataSource() {
+    ALOGV("~CallbackDataSource");
+    mIDataSource->close();
+}
+
+status_t CallbackDataSource::initCheck() const {
+    if (mMemory == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+ssize_t CallbackDataSource::readAt(off64_t offset, void* data, size_t size) {
+    if (mMemory == NULL) {
+        return -1;
+    }
+
+    // IDataSource can only read up to mMemory->size() bytes at a time, but this
+    // method should be able to read any number of bytes, so read in a loop.
+    size_t totalNumRead = 0;
+    size_t numLeft = size;
+    const size_t bufferSize = mMemory->size();
+
+    while (numLeft > 0) {
+        size_t numToRead = std::min(numLeft, bufferSize);
+        ssize_t numRead =
+            mIDataSource->readAt(offset + totalNumRead, numToRead);
+        // A negative return value represents an error. Pass it on.
+        if (numRead < 0) {
+            return numRead;
+        }
+        // A zero return value signals EOS. Return the bytes read so far.
+        if (numRead == 0) {
+            return totalNumRead;
+        }
+        if ((size_t)numRead > numToRead) {
+            return ERROR_OUT_OF_RANGE;
+        }
+        CHECK(numRead >= 0 && (size_t)numRead <= bufferSize);
+        memcpy(((uint8_t*)data) + totalNumRead, mMemory->pointer(), numRead);
+        numLeft -= numRead;
+        totalNumRead += numRead;
+    }
+
+    return totalNumRead;
+}
+
+status_t CallbackDataSource::getSize(off64_t *size) {
+    status_t err = mIDataSource->getSize(size);
+    if (err != OK) {
+        return err;
+    }
+    if (*size < 0) {
+        // IDataSource will set size to -1 to indicate unknown size, but
+        // DataSource returns ERROR_UNSUPPORTED for that.
+        return ERROR_UNSUPPORTED;
+    }
+    return OK;
+}
+
+TinyCacheSource::TinyCacheSource(const sp<DataSource>& source)
+    : mSource(source), mCachedOffset(0), mCachedSize(0) {
+}
+
+status_t TinyCacheSource::initCheck() const {
+    return mSource->initCheck();
+}
+
+ssize_t TinyCacheSource::readAt(off64_t offset, void* data, size_t size) {
+    if (size >= kCacheSize) {
+        return mSource->readAt(offset, data, size);
+    }
+
+    // Check if the cache satisfies the read.
+    if (offset >= mCachedOffset && offset + size <= mCachedOffset + mCachedSize) {
+        memcpy(data, &mCache[offset - mCachedOffset], size);
+        return size;
+    }
+
+    // Fill the cache and copy to the caller.
+    const ssize_t numRead = mSource->readAt(offset, mCache, kCacheSize);
+    if (numRead <= 0) {
+        return numRead;
+    }
+    if ((size_t)numRead > kCacheSize) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    mCachedSize = numRead;
+    mCachedOffset = offset;
+    CHECK(mCachedSize <= kCacheSize && mCachedOffset >= 0);
+    const size_t numToReturn = std::min(size, (size_t)numRead);
+    memcpy(data, mCache, numToReturn);
+
+    return numToReturn;
+}
+
+status_t TinyCacheSource::getSize(off64_t *size) {
+    return mSource->getSize(size);
+}
+
+uint32_t TinyCacheSource::flags() {
+    return mSource->flags();
+}
+
+} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index ad12bdd..1b788f3 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -851,11 +851,11 @@
 }
 
 void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
-        int32_t msgType, const sp<IMemory> &data) {
-    ALOGV("dataCallbackTimestamp: timestamp %" PRId64 " us", timestampUs);
+        int32_t msgType __unused, const sp<IMemory> &data) {
+    ALOGV("dataCallbackTimestamp: timestamp %lld us", (long long)timestampUs);
     Mutex::Autolock autoLock(mLock);
     if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
-        ALOGV("Drop frame at %" PRId64 "/%" PRId64 " us", timestampUs, mStartTimeUs);
+        ALOGV("Drop frame at %lld/%lld us", (long long)timestampUs, (long long)mStartTimeUs);
         releaseOneRecordingFrame(data);
         return;
     }
@@ -913,7 +913,7 @@
     mSource->dataCallbackTimestamp(timestamp / 1000, msgType, dataPtr);
 }
 
-void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who) {
+void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who __unused) {
     ALOGI("Camera recording proxy died");
 }
 
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index f7dcf35..75ef288 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,6 +19,7 @@
 #include "include/AMRExtractor.h"
 
 #include "include/AACExtractor.h"
+#include "include/CallbackDataSource.h"
 #include "include/DRMExtractor.h"
 #include "include/FLACExtractor.h"
 #include "include/HTTPBase.h"
@@ -281,6 +282,10 @@
     }
 }
 
+sp<DataSource> DataSource::CreateFromIDataSource(const sp<IDataSource> &source) {
+    return new TinyCacheSource(new CallbackDataSource(source));
+}
+
 String8 DataSource::getMIMEType() const {
     return String8("application/octet-stream");
 }
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/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
index fa7251c..8b972b7 100644
--- a/media/libstagefright/FLACExtractor.cpp
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -651,10 +651,10 @@
     if (doSeek) {
         // We implement the seek callback, so this works without explicit flush
         if (!FLAC__stream_decoder_seek_absolute(mDecoder, sample)) {
-            ALOGE("FLACParser::readBuffer seek to sample %llu failed", sample);
+            ALOGE("FLACParser::readBuffer seek to sample %lld failed", (long long)sample);
             return NULL;
         }
-        ALOGV("FLACParser::readBuffer seek to sample %llu succeeded", sample);
+        ALOGV("FLACParser::readBuffer seek to sample %lld succeeded", (long long)sample);
     } else {
         if (!FLAC__stream_decoder_process_single(mDecoder)) {
             ALOGE("FLACParser::readBuffer process_single failed");
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index f0db76b..565f156 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -111,7 +111,7 @@
    } else {
         off64_t result = lseek64(mFd, offset + mOffset, SEEK_SET);
         if (result == -1) {
-            ALOGE("seek to %lld failed", offset + mOffset);
+            ALOGE("seek to %lld failed", (long long)(offset + mOffset));
             return UNKNOWN_ERROR;
         }
 
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 77a652a..068a77f 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -34,10 +34,10 @@
     : mNumBandwidthHistoryItems(0),
       mTotalTransferTimeUs(0),
       mTotalTransferBytes(0),
+      mMaxBandwidthHistoryItems(100),
       mPrevBandwidthMeasureTimeUs(0),
       mPrevEstimatedBandWidthKbps(0),
-      mBandWidthCollectFreqMs(5000),
-      mMaxBandwidthHistoryItems(100) {
+      mBandWidthCollectFreqMs(5000) {
 }
 
 void HTTPBase::addBandwidthMeasurement(
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 4a63152..2e54e8c 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -82,7 +82,7 @@
             *inout_pos += len;
 
             ALOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)",
-                 *inout_pos, *inout_pos);
+                    (long long)*inout_pos, (long long)*inout_pos);
         }
 
         if (post_id3_pos != NULL) {
@@ -103,9 +103,9 @@
     uint8_t *tmp = buf;
 
     do {
-        if (pos >= *inout_pos + kMaxBytesChecked) {
+        if (pos >= (off64_t)(*inout_pos + kMaxBytesChecked)) {
             // Don't scan forever.
-            ALOGV("giving up at offset %lld", pos);
+            ALOGV("giving up at offset %lld", (long long)pos);
             break;
         }
 
@@ -155,7 +155,7 @@
             continue;
         }
 
-        ALOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header);
+        ALOGV("found possible 1st frame at %lld (header = 0x%08x)", (long long)pos, header);
 
         // We found what looks like a valid frame,
         // now find its successors.
@@ -186,7 +186,7 @@
                 break;
             }
 
-            ALOGV("found subsequent frame #%d at %lld", j + 2, test_pos);
+            ALOGV("found subsequent frame #%d at %lld", j + 2, (long long)test_pos);
 
             test_pos += test_frame_size;
         }
@@ -282,6 +282,41 @@
 
     mFirstFramePos = pos;
     mFixedHeader = header;
+    mMeta = new MetaData;
+    sp<XINGSeeker> seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
+
+    if (seeker == NULL) {
+        mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
+    } else {
+        mSeeker = seeker;
+        int encd = seeker->getEncoderDelay();
+        int encp = seeker->getEncoderPadding();
+        if (encd != 0 || encp != 0) {
+            mMeta->setInt32(kKeyEncoderDelay, encd);
+            mMeta->setInt32(kKeyEncoderPadding, encp);
+        }
+    }
+
+    if (mSeeker != NULL) {
+        // While it is safe to send the XING/VBRI frame to the decoder, this will
+        // result in an extra 1152 samples being output. In addition, the bitrate
+        // of the Xing header might not match the rest of the file, which could
+        // lead to problems when seeking. The real first frame to decode is after
+        // the XING/VBRI frame, so skip there.
+        size_t frame_size;
+        int sample_rate;
+        int num_channels;
+        int bitrate;
+        GetMPEGAudioFrameSize(
+                header, &frame_size, &sample_rate, &num_channels, &bitrate);
+        pos += frame_size;
+        if (!Resync(mDataSource, 0, &pos, &post_id3_pos, &header)) {
+            // mInitCheck will remain NO_INIT
+            return;
+        }
+        mFirstFramePos = pos;
+        mFixedHeader = header;
+    }
 
     size_t frame_size;
     int sample_rate;
@@ -292,8 +327,6 @@
 
     unsigned layer = 4 - ((header >> 17) & 3);
 
-    mMeta = new MetaData;
-
     switch (layer) {
         case 1:
             mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
@@ -312,27 +345,6 @@
     mMeta->setInt32(kKeyBitRate, bitrate * 1000);
     mMeta->setInt32(kKeyChannelCount, num_channels);
 
-    sp<XINGSeeker> seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
-
-    if (seeker == NULL) {
-        mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
-    } else {
-        mSeeker = seeker;
-        int encd = seeker->getEncoderDelay();
-        int encp = seeker->getEncoderPadding();
-        if (encd != 0 || encp != 0) {
-            mMeta->setInt32(kKeyEncoderDelay, encd);
-            mMeta->setInt32(kKeyEncoderPadding, encp);
-        }
-    }
-
-    if (mSeeker != NULL) {
-        // While it is safe to send the XING/VBRI frame to the decoder, this will
-        // result in an extra 1152 samples being output. The real first frame to
-        // decode is after the XING/VBRI frame, so skip there.
-        mFirstFramePos += frame_size;
-    }
-
     int64_t durationUs;
 
     if (mSeeker == NULL || !mSeeker->getDuration(&durationUs)) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d0f42cc..65d8a04 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -33,6 +33,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
@@ -221,8 +222,7 @@
 ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) {
     Mutex::Autolock autoLock(mLock);
 
-    if (offset >= mCachedOffset
-            && offset + size <= mCachedOffset + mCachedSize) {
+    if (isInRange(mCachedOffset, mCachedSize, offset, size)) {
         memcpy(data, &mCache[offset - mCachedOffset], size);
         return size;
     }
@@ -503,7 +503,7 @@
         } else if (offset <= orig_offset) {
             // only continue parsing if the offset was advanced,
             // otherwise we might end up in an infinite loop
-            ALOGE("did not advance: 0x%lld->0x%lld", orig_offset, offset);
+            ALOGE("did not advance: %lld->%lld", (long long)orig_offset, (long long)offset);
             err = ERROR_MALFORMED;
             break;
         } else if (err == UNKNOWN_ERROR) {
@@ -739,6 +739,17 @@
         && path[3] == FOURCC('i', 'l', 's', 't');
 }
 
+static bool underQTMetaPath(const Vector<uint32_t> &path, int32_t depth) {
+    return path.size() >= 2
+            && path[0] == FOURCC('m', 'o', 'o', 'v')
+            && path[1] == FOURCC('m', 'e', 't', 'a')
+            && (depth == 2
+            || (depth == 3
+                    && (path[2] == FOURCC('h', 'd', 'l', 'r')
+                    ||  path[2] == FOURCC('i', 'l', 's', 't')
+                    ||  path[2] == FOURCC('k', 'e', 'y', 's'))));
+}
+
 // Given a time in seconds since Jan 1 1904, produce a human-readable string.
 static void convertTimeToDate(int64_t time_1904, String8 *s) {
     time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600);
@@ -750,7 +761,7 @@
 }
 
 status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
-    ALOGV("entering parseChunk %lld/%d", *offset, depth);
+    ALOGV("entering parseChunk %lld/%d", (long long)*offset, depth);
     uint32_t hdr[2];
     if (mDataSource->readAt(*offset, hdr, 8) < 8) {
         return ERROR_IO;
@@ -794,7 +805,7 @@
 
     char chunk[5];
     MakeFourCCString(chunk_type, chunk);
-    ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth);
+    ALOGV("chunk: %s @ %lld, %d", chunk, (long long)*offset, depth);
 
     if (kUseHexDump) {
         static const char kWhitespace[] = "                                        ";
@@ -874,6 +885,9 @@
                     }
                 }
 
+                if (mLastTrack == NULL)
+                    return ERROR_MALFORMED;
+
                 mLastTrack->sampleTable = new SampleTable(mDataSource);
             }
 
@@ -1028,6 +1042,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 +1101,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 +1189,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 +1219,7 @@
                     duration = ntohl(duration32);
                 }
             }
-            if (duration != 0) {
+            if (duration != 0 && mLastTrack->timescale != 0) {
                 mLastTrack->meta->setInt64(
                         kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
             }
@@ -1257,6 +1283,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 +1333,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 +1397,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 +1425,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 +1443,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 +1462,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 +1534,9 @@
 
         case FOURCC('s', 't', 't', 's'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             *offset += chunk_size;
 
             status_t err =
@@ -1504,6 +1552,9 @@
 
         case FOURCC('c', 't', 't', 's'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             *offset += chunk_size;
 
             status_t err =
@@ -1519,6 +1570,9 @@
 
         case FOURCC('s', 't', 's', 's'):
         {
+            if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+                return ERROR_MALFORMED;
+
             *offset += chunk_size;
 
             status_t err =
@@ -1591,6 +1645,9 @@
                 return ERROR_MALFORMED;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(
                     kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
 
@@ -1623,6 +1680,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(
                     kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
 
@@ -1637,6 +1697,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(
                     kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);
 
@@ -1661,7 +1724,7 @@
             char buffer[23];
             if (chunk_data_size != 7 &&
                 chunk_data_size != 23) {
-                ALOGE("Incorrect D263 box size %lld", chunk_data_size);
+                ALOGE("Incorrect D263 box size %lld", (long long)chunk_data_size);
                 return ERROR_MALFORMED;
             }
 
@@ -1670,6 +1733,9 @@
                 return ERROR_IO;
             }
 
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
 
             break;
@@ -1677,31 +1743,35 @@
 
         case FOURCC('m', 'e', 't', 'a'):
         {
-            uint8_t buffer[4];
-            if (chunk_data_size < (off64_t)sizeof(buffer)) {
-                *offset += chunk_size;
-                return ERROR_MALFORMED;
-            }
-
-            if (mDataSource->readAt(
-                        data_offset, buffer, 4) < 4) {
-                *offset += chunk_size;
-                return ERROR_IO;
-            }
-
-            if (U32_AT(buffer) != 0) {
-                // Should be version 0, flags 0.
-
-                // If it's not, let's assume this is one of those
-                // apparently malformed chunks that don't have flags
-                // and completely different semantics than what's
-                // in the MPEG4 specs and skip it.
-                *offset += chunk_size;
-                return OK;
-            }
-
             off64_t stop_offset = *offset + chunk_size;
-            *offset = data_offset + sizeof(buffer);
+            *offset = data_offset;
+            bool isParsingMetaKeys = underQTMetaPath(mPath, 2);
+            if (!isParsingMetaKeys) {
+                uint8_t buffer[4];
+                if (chunk_data_size < (off64_t)sizeof(buffer)) {
+                    *offset += chunk_size;
+                    return ERROR_MALFORMED;
+                }
+
+                if (mDataSource->readAt(
+                            data_offset, buffer, 4) < 4) {
+                    *offset += chunk_size;
+                    return ERROR_IO;
+                }
+
+                if (U32_AT(buffer) != 0) {
+                    // Should be version 0, flags 0.
+
+                    // If it's not, let's assume this is one of those
+                    // apparently malformed chunks that don't have flags
+                    // and completely different semantics than what's
+                    // in the MPEG4 specs and skip it.
+                    *offset += chunk_size;
+                    return OK;
+                }
+                *offset +=  sizeof(buffer);
+            }
+
             while (*offset < stop_offset) {
                 status_t err = parseChunk(offset, depth + 1);
                 if (err != OK) {
@@ -1767,7 +1837,7 @@
                 }
                 duration = d32;
             }
-            if (duration != 0) {
+            if (duration != 0 && mHeaderTimescale != 0) {
                 mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
             }
 
@@ -1816,7 +1886,7 @@
                 return ERROR_MALFORMED;
             }
 
-            if (duration != 0) {
+            if (duration != 0 && mHeaderTimescale != 0) {
                 mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
             }
 
@@ -1845,12 +1915,19 @@
         {
             *offset += chunk_size;
 
+            if (underQTMetaPath(mPath, 3)) {
+                break;
+            }
+
             uint32_t buffer;
             if (mDataSource->readAt(
                         data_offset + 8, &buffer, 4) < 4) {
                 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
@@ -1862,6 +1939,16 @@
             break;
         }
 
+        case FOURCC('k', 'e', 'y', 's'):
+        {
+            *offset += chunk_size;
+
+            if (underQTMetaPath(mPath, 3)) {
+                parseQTMetaKey(data_offset, chunk_data_size);
+            }
+            break;
+        }
+
         case FOURCC('t', 'r', 'e', 'x'):
         {
             *offset += chunk_size;
@@ -1883,6 +1970,9 @@
 
         case FOURCC('t', 'x', '3', 'g'):
         {
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
             uint32_t type;
             const void *data;
             size_t size = 0;
@@ -1891,6 +1981,10 @@
                 size = 0;
             }
 
+            if (SIZE_MAX - chunk_size <= size) {
+                return ERROR_MALFORMED;
+            }
+
             uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
             if (buffer == NULL) {
                 return ERROR_MALFORMED;
@@ -1924,14 +2018,22 @@
             *offset += chunk_size;
 
             if (mFileMetaData != NULL) {
-                ALOGV("chunk_data_size = %lld and data_offset = %lld",
-                        chunk_data_size, data_offset);
+                ALOGV("chunk_data_size = %" PRId64 " and data_offset = %" PRId64,
+                      chunk_data_size, data_offset);
+
+                if (chunk_data_size < 0 || static_cast<uint64_t>(chunk_data_size) >= SIZE_MAX - 1) {
+                    return ERROR_MALFORMED;
+                }
                 sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
                 if (mDataSource->readAt(
                     data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
                     return ERROR_IO;
                 }
                 const int kSkipBytesOfDataBox = 16;
+                if (chunk_data_size <= kSkipBytesOfDataBox) {
+                    return ERROR_MALFORMED;
+                }
+
                 mFileMetaData->setData(
                     kKeyAlbumArt, MetaData::TYPE_NONE,
                     buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
@@ -1989,6 +2091,12 @@
 
         default:
         {
+            // check if we're parsing 'ilst' for meta keys
+            // if so, treat type as a number (key-id).
+            if (underQTMetaPath(mPath, 3)) {
+                parseQTMetaVal(chunk_type, data_offset, chunk_data_size);
+            }
+
             *offset += chunk_size;
             break;
         }
@@ -2024,6 +2132,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 +2217,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);
@@ -2114,7 +2227,108 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parseQTMetaKey(off64_t offset, size_t size) {
+    if (size < 8) {
+        return ERROR_MALFORMED;
+    }
 
+    uint32_t count;
+    if (!mDataSource->getUInt32(offset + 4, &count)) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mMetaKeyMap.size() > 0) {
+        ALOGW("'keys' atom seen again, discarding existing entries");
+        mMetaKeyMap.clear();
+    }
+
+    off64_t keyOffset = offset + 8;
+    off64_t stopOffset = offset + size;
+    for (size_t i = 1; i <= count; i++) {
+        if (keyOffset + 8 > stopOffset) {
+            return ERROR_MALFORMED;
+        }
+
+        uint32_t keySize;
+        if (!mDataSource->getUInt32(keyOffset, &keySize)
+                || keySize < 8
+                || keyOffset + keySize > stopOffset) {
+            return ERROR_MALFORMED;
+        }
+
+        uint32_t type;
+        if (!mDataSource->getUInt32(keyOffset + 4, &type)
+                || type != FOURCC('m', 'd', 't', 'a')) {
+            return ERROR_MALFORMED;
+        }
+
+        keySize -= 8;
+        keyOffset += 8;
+
+        sp<ABuffer> keyData = new ABuffer(keySize);
+        if (keyData->data() == NULL) {
+            return ERROR_MALFORMED;
+        }
+        if (mDataSource->readAt(
+                keyOffset, keyData->data(), keySize) < (ssize_t) keySize) {
+            return ERROR_MALFORMED;
+        }
+
+        AString key((const char *)keyData->data(), keySize);
+        mMetaKeyMap.add(i, key);
+
+        keyOffset += keySize;
+    }
+    return OK;
+}
+
+status_t MPEG4Extractor::parseQTMetaVal(
+        int32_t keyId, off64_t offset, size_t size) {
+    ssize_t index = mMetaKeyMap.indexOfKey(keyId);
+    if (index < 0) {
+        // corresponding key is not present, ignore
+        return ERROR_MALFORMED;
+    }
+
+    if (size <= 16) {
+        return ERROR_MALFORMED;
+    }
+    uint32_t dataSize;
+    if (!mDataSource->getUInt32(offset, &dataSize)
+            || dataSize > size || dataSize <= 16) {
+        return ERROR_MALFORMED;
+    }
+    uint32_t atomFourCC;
+    if (!mDataSource->getUInt32(offset + 4, &atomFourCC)
+            || atomFourCC != FOURCC('d', 'a', 't', 'a')) {
+        return ERROR_MALFORMED;
+    }
+    uint32_t dataType;
+    if (!mDataSource->getUInt32(offset + 8, &dataType)
+            || ((dataType & 0xff000000) != 0)) {
+        // not well-known type
+        return ERROR_MALFORMED;
+    }
+
+    dataSize -= 16;
+    offset += 16;
+
+    if (dataType == 23 && dataSize >= 4) {
+        // BE Float32
+        uint32_t val;
+        if (!mDataSource->getUInt32(offset, &val)) {
+            return ERROR_MALFORMED;
+        }
+        if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
+            mFileMetaData->setFloat(kKeyCaptureFramerate, *(float *)&val);
+        }
+    } else {
+        // add more keys if needed
+        ALOGV("ignoring key: type %d, size %d", dataType, dataSize);
+    }
+
+    return OK;
+}
 
 status_t MPEG4Extractor::parseTrackHeader(
         off64_t data_offset, off64_t data_size) {
@@ -2157,6 +2371,9 @@
         return ERROR_UNSUPPORTED;
     }
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     mLastTrack->meta->setInt32(kKeyTrackID, id);
 
     size_t matrixOffset = dynSize + 16;
@@ -2228,7 +2445,7 @@
     uint32_t metadataKey = 0;
     char chunk[5];
     MakeFourCCString(mPath[4], chunk);
-    ALOGV("meta: %s @ %lld", chunk, offset);
+    ALOGV("meta: %s @ %lld", chunk, (long long)offset);
     switch ((int32_t)mPath[4]) {
         case FOURCC(0xa9, 'a', 'l', 'b'):
         {
@@ -2339,6 +2556,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);
                     }
@@ -2397,11 +2617,11 @@
 }
 
 status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {
-    if (size < 4) {
+    if (size < 4 || size == SIZE_MAX) {
         return ERROR_MALFORMED;
     }
 
-    uint8_t *buffer = new (std::nothrow) uint8_t[size];
+    uint8_t *buffer = new (std::nothrow) uint8_t[size + 1];
     if (buffer == NULL) {
         return ERROR_MALFORMED;
     }
@@ -2470,6 +2690,10 @@
         int len16 = 0; // Number of UTF-16 characters
 
         // smallest possible valid UTF-16 string w BOM: 0xfe 0xff 0x00 0x00
+        if (size < 6) {
+            return ERROR_MALFORMED;
+        }
+
         if (size - 6 >= 4) {
             len16 = ((size - 6) / 2) - 1; // don't include 0x0000 terminator
             framedata = (char16_t *)(buffer + 6);
@@ -2493,6 +2717,7 @@
         }
 
         if (isUTF8) {
+            buffer[size] = 0;
             mFileMetaData->setCString(metadataKey, (const char *)buffer + 6);
         } else {
             // Convert from UTF-16 string to UTF-8 string.
@@ -2635,6 +2860,11 @@
         return ERROR_MALFORMED;
     }
 
+    if (track->timescale == 0) {
+        ALOGE("timescale invalid.");
+        return ERROR_MALFORMED;
+    }
+
     return OK;
 }
 
@@ -2701,6 +2931,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;
     }
@@ -2721,7 +2954,7 @@
     }
 
     if (kUseHexDump) {
-        printf("ESD of size %d\n", csd_size);
+        printf("ESD of size %zu\n", csd_size);
         hexdump(csd, csd_size);
     }
 
@@ -2749,6 +2982,9 @@
         objectType = 32 + br.getBits(6);
     }
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     //keep AOT type
     mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
 
@@ -2919,6 +3155,9 @@
         return ERROR_UNSUPPORTED;
     }
 
+    if (mLastTrack == NULL)
+        return ERROR_MALFORMED;
+
     int32_t prevSampleRate;
     CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
 
@@ -3122,7 +3361,7 @@
 
     char chunk[5];
     MakeFourCCString(chunk_type, chunk);
-    ALOGV("MPEG4Source chunk %s @ %llx", chunk, *offset);
+    ALOGV("MPEG4Source chunk %s @ %#llx", chunk, (long long)*offset);
 
     off64_t chunk_data_size = *offset + chunk_size - data_offset;
 
@@ -3579,7 +3818,7 @@
         sampleCtsOffset = 0;
     }
 
-    if (size < (off64_t)sampleCount * bytesPerSample) {
+    if (size < (off64_t)(sampleCount * bytesPerSample)) {
         return -EINVAL;
     }
 
@@ -3881,12 +4120,12 @@
             size_t dstOffset = 0;
 
             while (srcOffset < size) {
-                bool isMalFormed = (srcOffset + mNALLengthSize > size);
+                bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
                 size_t nalLength = 0;
                 if (!isMalFormed) {
                     nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
                     srcOffset += mNALLengthSize;
-                    isMalFormed = srcOffset + nalLength > size;
+                    isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
                 }
 
                 if (isMalFormed) {
@@ -4158,12 +4397,12 @@
             size_t dstOffset = 0;
 
             while (srcOffset < size) {
-                bool isMalFormed = (srcOffset + mNALLengthSize > size);
+                bool isMalFormed = !isInRange((size_t)0u, size, srcOffset, mNALLengthSize);
                 size_t nalLength = 0;
                 if (!isMalFormed) {
                     nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
                     srcOffset += mNALLengthSize;
-                    isMalFormed = srcOffset + nalLength > size;
+                    isMalFormed = !isInRange((size_t)0u, size, srcOffset, nalLength);
                 }
 
                 if (isMalFormed) {
@@ -4331,7 +4570,7 @@
 
         char chunkstring[5];
         MakeFourCCString(chunkType, chunkstring);
-        ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, offset);
+        ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, (long long)offset);
         switch (chunkType) {
             case FOURCC('f', 't', 'y', 'p'):
             {
@@ -4394,7 +4633,7 @@
         *meta = new AMessage;
         (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
 
-        ALOGV("found metadata size: %lld", moovAtomEndOffset);
+        ALOGV("found metadata size: %lld", (long long)moovAtomEndOffset);
     }
 
     return true;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 6f6e362..3bc22f2 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -63,9 +63,11 @@
 static const uint8_t kNalUnitTypePicParamSet = 0x08;
 static const int64_t kInitialDelayTimeUs     = 700000LL;
 
-static const char kMetaKey_Model[]      = "com.android.model";
 static const char kMetaKey_Version[]    = "com.android.version";
+#ifdef SHOW_MODEL_BUILD
+static const char kMetaKey_Model[]      = "com.android.model";
 static const char kMetaKey_Build[]      = "com.android.build";
+#endif
 static const char kMetaKey_CaptureFps[] = "com.android.capture.fps";
 
 /* uncomment to include model and build in meta */
@@ -92,6 +94,7 @@
     void addChunkOffset(off64_t offset);
     int32_t getTrackId() const { return mTrackId; }
     status_t dump(int fd, const Vector<String16>& args) const;
+    static const char *getFourCCForMime(const char *mime);
 
 private:
     enum {
@@ -372,8 +375,8 @@
       mLatitudex10000(0),
       mLongitudex10000(0),
       mAreGeoTagsAvailable(false),
-      mMetaKeys(new AMessage()),
-      mStartTimeOffsetMs(-1) {
+      mStartTimeOffsetMs(-1),
+      mMetaKeys(new AMessage()) {
     addDeviceMeta();
 }
 
@@ -424,6 +427,33 @@
     return OK;
 }
 
+// static
+const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) {
+    if (mime == NULL) {
+        return NULL;
+    }
+    if (!strncasecmp(mime, "audio/", 6)) {
+        if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+            return "samr";
+        } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+            return "sawb";
+        } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+            return "mp4a";
+        }
+    } else if (!strncasecmp(mime, "video/", 6)) {
+        if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+            return "mp4v";
+        } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+            return "s263";
+        } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+            return "avc1";
+        }
+    } else {
+        ALOGE("Track (%s) other than video or audio is not supported", mime);
+    }
+    return NULL;
+}
+
 status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
     Mutex::Autolock l(mLock);
     if (mStarted) {
@@ -439,14 +469,11 @@
 
     CHECK(source.get() != NULL);
 
-    // A track of type other than video or audio is not supported.
     const char *mime;
     source->getFormat()->findCString(kKeyMIMEType, &mime);
     bool isAudio = !strncasecmp(mime, "audio/", 6);
-    bool isVideo = !strncasecmp(mime, "video/", 6);
-    if (!isAudio && !isVideo) {
-        ALOGE("Track (%s) other than video or audio is not supported",
-            mime);
+    if (Track::getFourCCForMime(mime) == NULL) {
+        ALOGE("Unsupported mime '%s'", mime);
         return ERROR_UNSUPPORTED;
     }
 
@@ -841,6 +868,8 @@
     mFd = -1;
     mInitCheck = NO_INIT;
     mStarted = false;
+    free(mMoovBoxBuffer);
+    mMoovBoxBuffer = NULL;
 }
 
 status_t MPEG4Writer::reset() {
@@ -2726,17 +2755,13 @@
     const char *mime;
     bool success = mMeta->findCString(kKeyMIMEType, &mime);
     CHECK(success);
-    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
-        mOwner->beginBox("mp4v");
-    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
-        mOwner->beginBox("s263");
-    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
-        mOwner->beginBox("avc1");
-    } else {
+    const char *fourcc = getFourCCForMime(mime);
+    if (fourcc == NULL) {
         ALOGE("Unknown mime type '%s'.", mime);
         CHECK(!"should not be here, unknown mime type.");
     }
 
+    mOwner->beginBox(fourcc);        // video format
     mOwner->writeInt32(0);           // reserved
     mOwner->writeInt16(0);           // reserved
     mOwner->writeInt16(1);           // data ref index
@@ -2780,14 +2805,8 @@
     const char *mime;
     bool success = mMeta->findCString(kKeyMIMEType, &mime);
     CHECK(success);
-    const char *fourcc = NULL;
-    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
-        fourcc = "samr";
-    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
-        fourcc = "sawb";
-    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
-        fourcc = "mp4a";
-    } else {
+    const char *fourcc = getFourCCForMime(mime);
+    if (fourcc == NULL) {
         ALOGE("Unknown mime type '%s'.", mime);
         CHECK(!"should not be here, unknown mime type.");
     }
@@ -2839,8 +2858,10 @@
 
     mOwner->writeInt16(0x03);  // XXX
     mOwner->writeInt8(0x00);   // buffer size 24-bit
-    mOwner->writeInt32(96000); // max bit rate
-    mOwner->writeInt32(96000); // avg bit rate
+    int32_t bitRate;
+    bool success = mMeta->findInt32(kKeyBitRate, &bitRate);
+    mOwner->writeInt32(success ? bitRate : 96000); // max bit rate
+    mOwner->writeInt32(success ? bitRate : 96000); // avg bit rate
 
     mOwner->writeInt8(0x05);   // DecoderSpecificInfoTag
     mOwner->writeInt8(mCodecSpecificDataSize);
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..46c154d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -22,9 +22,14 @@
 #include "include/SoftwareRenderer.h"
 
 #include <binder/IBatteryStats.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
+#include <media/IOMX.h>
+#include <media/IResourceManagerService.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -38,25 +43,97 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaFilter.h>
 #include <media/stagefright/MetaData.h>
-#include <media/stagefright/NativeWindowWrapper.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/PersistentSurface.h>
+#include <media/stagefright/SurfaceUtils.h>
 #include <private/android_filesystem_config.h>
 #include <utils/Log.h>
 #include <utils/Singleton.h>
 
 namespace android {
 
+static inline int getCallingPid() {
+    return IPCThreadState::self()->getCallingPid();
+}
+
+static int64_t getId(sp<IResourceManagerClient> client) {
+    return (int64_t) client.get();
+}
+
+static bool isResourceError(status_t err) {
+    return (err == NO_MEMORY);
+}
+
+static const int kMaxRetry = 2;
+
+struct ResourceManagerClient : public BnResourceManagerClient {
+    ResourceManagerClient(MediaCodec* codec) : mMediaCodec(codec) {}
+
+    virtual bool reclaimResource() {
+        sp<MediaCodec> codec = mMediaCodec.promote();
+        if (codec == NULL) {
+            // codec is already gone.
+            return true;
+        }
+        status_t err = codec->reclaim();
+        if (err != OK) {
+            ALOGW("ResourceManagerClient failed to release codec with err %d", err);
+        }
+        return (err == OK);
+    }
+
+    virtual String8 getName() {
+        String8 ret;
+        sp<MediaCodec> codec = mMediaCodec.promote();
+        if (codec == NULL) {
+            // codec is already gone.
+            return ret;
+        }
+
+        AString name;
+        if (codec->getName(&name) == OK) {
+            ret.setTo(name.c_str());
+        }
+        return ret;
+    }
+
+protected:
+    virtual ~ResourceManagerClient() {}
+
+private:
+    wp<MediaCodec> mMediaCodec;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient);
+};
+
 struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> {
     BatteryNotifier();
+    virtual ~BatteryNotifier();
 
     void noteStartVideo();
     void noteStopVideo();
     void noteStartAudio();
     void noteStopAudio();
+    void onBatteryStatServiceDied();
 
 private:
+    struct DeathNotifier : public IBinder::DeathRecipient {
+        DeathNotifier() {}
+        virtual void binderDied(const wp<IBinder>& /*who*/) {
+            BatteryNotifier::getInstance().onBatteryStatServiceDied();
+        }
+    };
+
+    Mutex mLock;
     int32_t mVideoRefCount;
     int32_t mAudioRefCount;
     sp<IBatteryStats> mBatteryStatService;
+    sp<DeathNotifier> mDeathNotifier;
+
+    sp<IBatteryStats> getBatteryService_l();
+
+    DISALLOW_EVIL_CONSTRUCTORS(BatteryNotifier);
 };
 
 ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier)
@@ -64,54 +141,155 @@
 MediaCodec::BatteryNotifier::BatteryNotifier() :
     mVideoRefCount(0),
     mAudioRefCount(0) {
-    // get battery service
+}
+
+sp<IBatteryStats> MediaCodec::BatteryNotifier::getBatteryService_l() {
+    if (mBatteryStatService != NULL) {
+        return mBatteryStatService;
+    }
+    // get battery service from service manager
     const sp<IServiceManager> sm(defaultServiceManager());
     if (sm != NULL) {
         const String16 name("batterystats");
-        mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
+        mBatteryStatService =
+                interface_cast<IBatteryStats>(sm->getService(name));
         if (mBatteryStatService == NULL) {
             ALOGE("batterystats service unavailable!");
+            return NULL;
         }
+        mDeathNotifier = new DeathNotifier();
+        IInterface::asBinder(mBatteryStatService)->linkToDeath(mDeathNotifier);
+        // notify start now if media already started
+        if (mVideoRefCount > 0) {
+            mBatteryStatService->noteStartVideo(AID_MEDIA);
+        }
+        if (mAudioRefCount > 0) {
+            mBatteryStatService->noteStartAudio(AID_MEDIA);
+        }
+    }
+    return mBatteryStatService;
+}
+
+MediaCodec::BatteryNotifier::~BatteryNotifier() {
+    if (mDeathNotifier != NULL) {
+        IInterface::asBinder(mBatteryStatService)->
+                unlinkToDeath(mDeathNotifier);
     }
 }
 
 void MediaCodec::BatteryNotifier::noteStartVideo() {
-    if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
-        mBatteryStatService->noteStartVideo(AID_MEDIA);
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    if (mVideoRefCount == 0 && batteryService != NULL) {
+        batteryService->noteStartVideo(AID_MEDIA);
     }
     mVideoRefCount++;
 }
 
 void MediaCodec::BatteryNotifier::noteStopVideo() {
+    Mutex::Autolock _l(mLock);
     if (mVideoRefCount == 0) {
         ALOGW("BatteryNotifier::noteStop(): video refcount is broken!");
         return;
     }
 
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+
     mVideoRefCount--;
-    if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
-        mBatteryStatService->noteStopVideo(AID_MEDIA);
+    if (mVideoRefCount == 0 && batteryService != NULL) {
+        batteryService->noteStopVideo(AID_MEDIA);
     }
 }
 
 void MediaCodec::BatteryNotifier::noteStartAudio() {
-    if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
-        mBatteryStatService->noteStartAudio(AID_MEDIA);
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    if (mAudioRefCount == 0 && batteryService != NULL) {
+        batteryService->noteStartAudio(AID_MEDIA);
     }
     mAudioRefCount++;
 }
 
 void MediaCodec::BatteryNotifier::noteStopAudio() {
+    Mutex::Autolock _l(mLock);
     if (mAudioRefCount == 0) {
         ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!");
         return;
     }
 
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+
     mAudioRefCount--;
-    if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
-        mBatteryStatService->noteStopAudio(AID_MEDIA);
+    if (mAudioRefCount == 0 && batteryService != NULL) {
+        batteryService->noteStopAudio(AID_MEDIA);
     }
 }
+
+void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() {
+    Mutex::Autolock _l(mLock);
+    mBatteryStatService.clear();
+    mDeathNotifier.clear();
+    // Do not reset mVideoRefCount and mAudioRefCount here. The ref
+    // counting is independent of the battery service availability.
+    // We need this if battery service becomes available after media
+    // started.
+}
+
+MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy() {
+}
+
+MediaCodec::ResourceManagerServiceProxy::~ResourceManagerServiceProxy() {
+    if (mService != NULL) {
+        IInterface::asBinder(mService)->unlinkToDeath(this);
+    }
+}
+
+void MediaCodec::ResourceManagerServiceProxy::init() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
+    mService = interface_cast<IResourceManagerService>(binder);
+    if (mService == NULL) {
+        ALOGE("Failed to get ResourceManagerService");
+        return;
+    }
+    IInterface::asBinder(mService)->linkToDeath(this);
+}
+
+void MediaCodec::ResourceManagerServiceProxy::binderDied(const wp<IBinder>& /*who*/) {
+    ALOGW("ResourceManagerService died.");
+    Mutex::Autolock _l(mLock);
+    mService.clear();
+}
+
+void MediaCodec::ResourceManagerServiceProxy::addResource(
+        int pid,
+        int64_t clientId,
+        const sp<IResourceManagerClient> client,
+        const Vector<MediaResource> &resources) {
+    Mutex::Autolock _l(mLock);
+    if (mService == NULL) {
+        return;
+    }
+    mService->addResource(pid, clientId, client, resources);
+}
+
+void MediaCodec::ResourceManagerServiceProxy::removeResource(int64_t clientId) {
+    Mutex::Autolock _l(mLock);
+    if (mService == NULL) {
+        return;
+    }
+    mService->removeResource(clientId);
+}
+
+bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
+        int callingPid, const Vector<MediaResource> &resources) {
+    Mutex::Autolock _l(mLock);
+    if (mService == NULL) {
+        return false;
+    }
+    return mService->reclaimResource(callingPid, resources);
+}
+
 // static
 sp<MediaCodec> MediaCodec::CreateByType(
         const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) {
@@ -136,25 +314,52 @@
     return ret == OK ? codec : NULL; // NULL deallocates codec.
 }
 
+// static
+sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
+    OMXClient client;
+    CHECK_EQ(client.connect(), (status_t)OK);
+    sp<IOMX> omx = client.interface();
+
+    sp<IGraphicBufferProducer> bufferProducer;
+    sp<IGraphicBufferConsumer> bufferConsumer;
+
+    status_t err = omx->createPersistentInputSurface(
+            &bufferProducer, &bufferConsumer);
+
+    if (err != OK) {
+        ALOGE("Failed to create persistent input surface.");
+        return NULL;
+    }
+
+    return new PersistentSurface(bufferProducer, bufferConsumer);
+}
+
 MediaCodec::MediaCodec(const sp<ALooper> &looper)
     : mState(UNINITIALIZED),
+      mReleasedByResourceManager(false),
       mLooper(looper),
       mCodec(NULL),
       mReplyID(0),
       mFlags(0),
       mStickyError(OK),
       mSoftRenderer(NULL),
+      mResourceManagerClient(new ResourceManagerClient(this)),
+      mResourceManagerService(new ResourceManagerServiceProxy()),
       mBatteryStatNotified(false),
       mIsVideo(false),
+      mVideoWidth(0),
+      mVideoHeight(0),
       mDequeueInputTimeoutGeneration(0),
       mDequeueInputReplyID(0),
       mDequeueOutputTimeoutGeneration(0),
       mDequeueOutputReplyID(0),
-      mHaveInputSurface(false) {
+      mHaveInputSurface(false),
+      mHavePendingInputBuffers(false) {
 }
 
 MediaCodec::~MediaCodec() {
     CHECK_EQ(mState, UNINITIALIZED);
+    mResourceManagerService->removeResource(getId(mResourceManagerClient));
 }
 
 // static
@@ -173,14 +378,21 @@
     return err;
 }
 
-// static
 void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
+    int32_t finalErr = err;
+    if (mReleasedByResourceManager) {
+        // override the err code if MediaCodec has been released by ResourceManager.
+        finalErr = DEAD_OBJECT;
+    }
+
     sp<AMessage> response = new AMessage;
-    response->setInt32("err", err);
+    response->setInt32("err", finalErr);
     response->postReply(replyID);
 }
 
 status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
+    mResourceManagerService->init();
+
     // save init parameters for reset
     mInitName = name;
     mInitNameIsType = nameIsType;
@@ -200,15 +412,20 @@
         return NAME_NOT_FOUND;
     }
 
-    bool needDedicatedLooper = false;
+    bool secureCodec = false;
     if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) {
-        needDedicatedLooper = true;
+        mIsVideo = true;
     } else {
         AString tmp = name;
         if (tmp.endsWith(".secure")) {
+            secureCodec = true;
             tmp.erase(tmp.size() - 7, 7);
         }
         const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+        if (mcl == NULL) {
+            mCodec = NULL;  // remove the codec.
+            return NO_INIT; // if called from Java should raise IOException
+        }
         ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
         if (codecIdx >= 0) {
             const sp<MediaCodecInfo> info = mcl->getCodecInfo(codecIdx);
@@ -216,14 +433,15 @@
             info->getSupportedMimes(&mimes);
             for (size_t i = 0; i < mimes.size(); i++) {
                 if (mimes[i].startsWith("video/")) {
-                    needDedicatedLooper = true;
+                    mIsVideo = true;
                     break;
                 }
             }
         }
     }
 
-    if (needDedicatedLooper) {
+    if (mIsVideo) {
+        // video codec needs dedicated looper
         if (mCodecLooper == NULL) {
             mCodecLooper = new ALooper;
             mCodecLooper->setName("CodecLooper");
@@ -247,8 +465,26 @@
         msg->setInt32("encoder", encoder);
     }
 
-    sp<AMessage> response;
-    return PostAndAwaitResponse(msg, &response);
+    status_t err;
+    Vector<MediaResource> resources;
+    const char *type = secureCodec ? kResourceSecureCodec : kResourceNonSecureCodec;
+    const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+    resources.push_back(MediaResource(String8(type), String8(subtype), 1));
+    for (int i = 0; i <= kMaxRetry; ++i) {
+        if (i > 0) {
+            // Don't try to reclaim resource for the first time.
+            if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) {
+                break;
+            }
+        }
+
+        sp<AMessage> response;
+        err = PostAndAwaitResponse(msg, &response);
+        if (!isResourceError(err)) {
+            break;
+        }
+    }
+    return err;
 }
 
 status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
@@ -261,41 +497,80 @@
 
 status_t MediaCodec::configure(
         const sp<AMessage> &format,
-        const sp<Surface> &nativeWindow,
+        const sp<Surface> &surface,
         const sp<ICrypto> &crypto,
         uint32_t flags) {
     sp<AMessage> msg = new AMessage(kWhatConfigure, this);
 
+    if (mIsVideo) {
+        format->findInt32("width", &mVideoWidth);
+        format->findInt32("height", &mVideoHeight);
+    }
+
     msg->setMessage("format", format);
     msg->setInt32("flags", flags);
-
-    if (nativeWindow != NULL) {
-        msg->setObject(
-                "native-window",
-                new NativeWindowWrapper(nativeWindow));
-    }
+    msg->setObject("surface", surface);
 
     if (crypto != NULL) {
         msg->setPointer("crypto", crypto.get());
     }
 
-    sp<AMessage> response;
-    status_t err = PostAndAwaitResponse(msg, &response);
+    // save msg for reset
+    mConfigureMsg = msg;
 
-    if (err != OK && err != INVALID_OPERATION) {
-        // MediaCodec now set state to UNINITIALIZED upon any fatal error.
-        // To maintain backward-compatibility, do a reset() to put codec
-        // back into INITIALIZED state.
-        // But don't reset if the err is INVALID_OPERATION, which means
-        // the configure failure is due to wrong state.
+    status_t err;
+    Vector<MediaResource> resources;
+    const char *type = (mFlags & kFlagIsSecure) ?
+            kResourceSecureCodec : kResourceNonSecureCodec;
+    const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+    resources.push_back(MediaResource(String8(type), String8(subtype), 1));
+    // Don't know the buffer size at this point, but it's fine to use 1 because
+    // the reclaimResource call doesn't consider the requester's buffer size for now.
+    resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1));
+    for (int i = 0; i <= kMaxRetry; ++i) {
+        if (i > 0) {
+            // Don't try to reclaim resource for the first time.
+            if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) {
+                break;
+            }
+        }
 
-        ALOGE("configure failed with err 0x%08x, resetting...", err);
-        reset();
+        sp<AMessage> response;
+        err = PostAndAwaitResponse(msg, &response);
+        if (err != OK && err != INVALID_OPERATION) {
+            // MediaCodec now set state to UNINITIALIZED upon any fatal error.
+            // To maintain backward-compatibility, do a reset() to put codec
+            // back into INITIALIZED state.
+            // But don't reset if the err is INVALID_OPERATION, which means
+            // the configure failure is due to wrong state.
+
+            ALOGE("configure failed with err 0x%08x, resetting...", err);
+            reset();
+        }
+        if (!isResourceError(err)) {
+            break;
+        }
     }
-
     return err;
 }
 
+status_t MediaCodec::setInputSurface(
+        const sp<PersistentSurface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
+    msg->setObject("input-surface", surface.get());
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::setSurface(const sp<Surface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
+    msg->setObject("surface", surface);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::createInputSurface(
         sp<IGraphicBufferProducer>* bufferProducer) {
     sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, this);
@@ -316,15 +591,78 @@
     return err;
 }
 
+uint64_t MediaCodec::getGraphicBufferSize() {
+    if (!mIsVideo) {
+        return 0;
+    }
+
+    uint64_t size = 0;
+    size_t portNum = sizeof(mPortBuffers) / sizeof((mPortBuffers)[0]);
+    for (size_t i = 0; i < portNum; ++i) {
+        // TODO: this is just an estimation, we should get the real buffer size from ACodec.
+        size += mPortBuffers[i].size() * mVideoWidth * mVideoHeight * 3 / 2;
+    }
+    return size;
+}
+
+void MediaCodec::addResource(const String8 &type, const String8 &subtype, uint64_t value) {
+    Vector<MediaResource> resources;
+    resources.push_back(MediaResource(type, subtype, value));
+    mResourceManagerService->addResource(
+            getCallingPid(), getId(mResourceManagerClient), mResourceManagerClient, resources);
+}
+
 status_t MediaCodec::start() {
     sp<AMessage> msg = new AMessage(kWhatStart, this);
 
+    status_t err;
+    Vector<MediaResource> resources;
+    const char *type = (mFlags & kFlagIsSecure) ?
+            kResourceSecureCodec : kResourceNonSecureCodec;
+    const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+    resources.push_back(MediaResource(String8(type), String8(subtype), 1));
+    // Don't know the buffer size at this point, but it's fine to use 1 because
+    // the reclaimResource call doesn't consider the requester's buffer size for now.
+    resources.push_back(MediaResource(String8(kResourceGraphicMemory), 1));
+    for (int i = 0; i <= kMaxRetry; ++i) {
+        if (i > 0) {
+            // Don't try to reclaim resource for the first time.
+            if (!mResourceManagerService->reclaimResource(getCallingPid(), resources)) {
+                break;
+            }
+            // Recover codec from previous error before retry start.
+            err = reset();
+            if (err != OK) {
+                ALOGE("retrying start: failed to reset codec");
+                break;
+            }
+            sp<AMessage> response;
+            err = PostAndAwaitResponse(mConfigureMsg, &response);
+            if (err != OK) {
+                ALOGE("retrying start: failed to configure codec");
+                break;
+            }
+        }
+
+        sp<AMessage> response;
+        err = PostAndAwaitResponse(msg, &response);
+        if (!isResourceError(err)) {
+            break;
+        }
+    }
+    return err;
+}
+
+status_t MediaCodec::stop() {
+    sp<AMessage> msg = new AMessage(kWhatStop, this);
+
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
 }
 
-status_t MediaCodec::stop() {
-    sp<AMessage> msg = new AMessage(kWhatStop, this);
+status_t MediaCodec::reclaim() {
+    sp<AMessage> msg = new AMessage(kWhatRelease, this);
+    msg->setInt32("reclaimed", 1);
 
     sp<AMessage> response;
     return PostAndAwaitResponse(msg, &response);
@@ -544,6 +882,16 @@
     return OK;
 }
 
+status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const {
+    sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
+    msg->setInt32("portIndex", kPortIndexInput);
+    msg->setPointer("buffers", buffers);
+    msg->setInt32("widevine", true);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
     sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
     msg->setInt32("portIndex", kPortIndexInput);
@@ -586,6 +934,10 @@
         sp<ABuffer> *buffer, sp<AMessage> *format) {
     // use mutex instead of a context switch
 
+    if (mReleasedByResourceManager) {
+        return DEAD_OBJECT;
+    }
+
     buffer->clear();
     format->clear();
     if (!isExecuting()) {
@@ -675,20 +1027,19 @@
 }
 
 bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
-    sp<AMessage> response = new AMessage;
-
     if (!isExecuting() || (mFlags & kFlagIsAsync)
             || (newRequest && (mFlags & kFlagDequeueOutputPending))) {
-        response->setInt32("err", INVALID_OPERATION);
+        PostReplyWithError(replyID, INVALID_OPERATION);
     } else if (mFlags & kFlagStickyError) {
-        response->setInt32("err", getStickyError());
+        PostReplyWithError(replyID, getStickyError());
     } else if (mFlags & kFlagOutputBuffersChanged) {
-        response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
+        PostReplyWithError(replyID, INFO_OUTPUT_BUFFERS_CHANGED);
         mFlags &= ~kFlagOutputBuffersChanged;
     } else if (mFlags & kFlagOutputFormatChanged) {
-        response->setInt32("err", INFO_FORMAT_CHANGED);
+        PostReplyWithError(replyID, INFO_FORMAT_CHANGED);
         mFlags &= ~kFlagOutputFormatChanged;
     } else {
+        sp<AMessage> response = new AMessage;
         ssize_t index = dequeuePortBuffer(kPortIndexOutput);
 
         if (index < 0) {
@@ -723,10 +1074,9 @@
         }
 
         response->setInt32("flags", flags);
+        response->postReply(replyID);
     }
 
-    response->postReply(replyID);
-
     return true;
 }
 
@@ -884,12 +1234,18 @@
                         mFlags &= ~kFlagUsesSoftwareRenderer;
                     }
 
+                    String8 resourceType;
                     if (mComponentName.endsWith(".secure")) {
                         mFlags |= kFlagIsSecure;
+                        resourceType = String8(kResourceSecureCodec);
                     } else {
                         mFlags &= ~kFlagIsSecure;
+                        resourceType = String8(kResourceNonSecureCodec);
                     }
 
+                    const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
+                    addResource(resourceType, String8(subtype), 1);
+
                     (new AMessage)->postReply(mReplyID);
                     break;
                 }
@@ -918,7 +1274,7 @@
                 {
                     // response to initiateCreateInputSurface()
                     status_t err = NO_ERROR;
-                    sp<AMessage> response = new AMessage();
+                    sp<AMessage> response = new AMessage;
                     if (!msg->findInt32("err", &err)) {
                         sp<RefBase> obj;
                         msg->findObject("input-surface", &obj);
@@ -932,10 +1288,24 @@
                     break;
                 }
 
+                case CodecBase::kWhatInputSurfaceAccepted:
+                {
+                    // response to initiateSetInputSurface()
+                    status_t err = NO_ERROR;
+                    sp<AMessage> response = new AMessage();
+                    if (!msg->findInt32("err", &err)) {
+                        mHaveInputSurface = true;
+                    } else {
+                        response->setInt32("err", err);
+                    }
+                    response->postReply(mReplyID);
+                    break;
+                }
+
                 case CodecBase::kWhatSignaledInputEOS:
                 {
                     // response to signalEndOfInputStream()
-                    sp<AMessage> response = new AMessage();
+                    sp<AMessage> response = new AMessage;
                     status_t err;
                     if (msg->findInt32("err", &err)) {
                         response->setInt32("err", err);
@@ -969,6 +1339,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 +1357,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);
@@ -988,6 +1371,13 @@
                             // We're always allocating output buffers after
                             // allocating input buffers, so this is a good
                             // indication that now all buffers are allocated.
+                            if (mIsVideo) {
+                                String8 subtype;
+                                addResource(
+                                        String8(kResourceGraphicMemory),
+                                        subtype,
+                                        getGraphicBufferSize());
+                            }
                             setState(STARTED);
                             (new AMessage)->postReply(mReplyID);
                         } else {
@@ -1003,13 +1393,13 @@
                     ALOGV("codec output format changed");
 
                     if (mSoftRenderer == NULL &&
-                            mNativeWindow != NULL &&
+                            mSurface != NULL &&
                             (mFlags & kFlagUsesSoftwareRenderer)) {
                         AString mime;
                         CHECK(msg->findString("mime", &mime));
 
                         if (mime.startsWithIgnoreCase("video/")) {
-                            mSoftRenderer = new SoftwareRenderer(mNativeWindow);
+                            mSoftRenderer = new SoftwareRenderer(mSurface);
                         }
                     }
 
@@ -1078,7 +1468,11 @@
 
                     if (mFlags & kFlagIsAsync) {
                         if (!mHaveInputSurface) {
-                            onInputBufferAvailable();
+                            if (mState == FLUSHED) {
+                                mHavePendingInputBuffers = true;
+                            } else {
+                                onInputBufferAvailable();
+                            }
                         }
                     } else if (mFlags & kFlagDequeueInputPending) {
                         CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
@@ -1167,6 +1561,8 @@
                     }
                     mFlags &= ~kFlagIsComponentAllocated;
 
+                    mResourceManagerService->removeResource(getId(mResourceManagerClient));
+
                     (new AMessage)->postReply(mReplyID);
                     break;
                 }
@@ -1275,26 +1671,25 @@
             }
 
             sp<RefBase> obj;
-            if (!msg->findObject("native-window", &obj)) {
-                obj.clear();
-            }
+            CHECK(msg->findObject("surface", &obj));
 
             sp<AMessage> format;
             CHECK(msg->findMessage("format", &format));
 
+            int32_t push;
+            if (msg->findInt32("push-blank-buffers-on-shutdown", &push) && push != 0) {
+                mFlags |= kFlagPushBlankBuffersOnShutdown;
+            }
+
             if (obj != NULL) {
                 format->setObject("native-window", obj);
-
-                status_t err = setNativeWindow(
-                    static_cast<NativeWindowWrapper *>(obj.get())
-                        ->getSurfaceTextureClient());
-
+                status_t err = handleSetSurface(static_cast<Surface *>(obj.get()));
                 if (err != OK) {
                     PostReplyWithError(replyID, err);
                     break;
                 }
             } else {
-                setNativeWindow(NULL);
+                handleSetSurface(NULL);
             }
 
             mReplyID = replyID;
@@ -1321,7 +1716,67 @@
             break;
         }
 
+        case kWhatSetSurface:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            status_t err = OK;
+            sp<Surface> surface;
+
+            switch (mState) {
+                case CONFIGURED:
+                case STARTED:
+                case FLUSHED:
+                {
+                    sp<RefBase> obj;
+                    (void)msg->findObject("surface", &obj);
+                    sp<Surface> surface = static_cast<Surface *>(obj.get());
+                    if (mSurface == NULL) {
+                        // do not support setting surface if it was not set
+                        err = INVALID_OPERATION;
+                    } else if (obj == NULL) {
+                        // do not support unsetting surface
+                        err = BAD_VALUE;
+                    } else {
+                        err = connectToSurface(surface);
+                        if (err == BAD_VALUE) {
+                            // assuming reconnecting to same surface
+                            // TODO: check if it is the same surface
+                            err = OK;
+                        } else {
+                            if (err == OK) {
+                                if (mFlags & kFlagUsesSoftwareRenderer) {
+                                    if (mSoftRenderer != NULL
+                                            && (mFlags & kFlagPushBlankBuffersOnShutdown)) {
+                                        pushBlankBuffersToNativeWindow(mSurface.get());
+                                    }
+                                    mSoftRenderer = new SoftwareRenderer(surface);
+                                    // TODO: check if this was successful
+                                } else {
+                                    err = mCodec->setSurface(surface);
+                                }
+                            }
+                            if (err == OK) {
+                                (void)disconnectFromSurface();
+                                mSurface = surface;
+                            }
+                        }
+                    }
+                    break;
+                }
+
+                default:
+                    err = INVALID_OPERATION;
+                    break;
+            }
+
+            PostReplyWithError(replyID, err);
+            break;
+        }
+
         case kWhatCreateInputSurface:
+        case kWhatSetInputSurface:
         {
             sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
@@ -1333,10 +1788,17 @@
             }
 
             mReplyID = replyID;
-            mCodec->initiateCreateInputSurface();
+            if (msg->what() == kWhatCreateInputSurface) {
+                mCodec->initiateCreateInputSurface();
+            } else {
+                sp<RefBase> obj;
+                CHECK(msg->findObject("input-surface", &obj));
+
+                mCodec->initiateSetInputSurface(
+                        static_cast<PersistentSurface *>(obj.get()));
+            }
             break;
         }
-
         case kWhatStart:
         {
             sp<AReplyToken> replyID;
@@ -1344,6 +1806,10 @@
 
             if (mState == FLUSHED) {
                 setState(STARTED);
+                if (mHavePendingInputBuffers) {
+                    onInputBufferAvailable();
+                    mHavePendingInputBuffers = false;
+                }
                 mCodec->signalResume();
                 PostReplyWithError(replyID, OK);
                 break;
@@ -1368,6 +1834,20 @@
             sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
+            // already stopped/released
+            if (mState == UNINITIALIZED && mReleasedByResourceManager) {
+                sp<AMessage> response = new AMessage;
+                response->setInt32("err", OK);
+                response->postReply(replyID);
+                break;
+            }
+
+            int32_t reclaimed = 0;
+            msg->findInt32("reclaimed", &reclaimed);
+            if (reclaimed) {
+                mReleasedByResourceManager = true;
+            }
+
             if (!((mFlags & kFlagIsComponentAllocated) && targetState == UNINITIALIZED) // See 1
                     && mState != INITIALIZED
                     && mState != CONFIGURED && !isExecuting()) {
@@ -1381,6 +1861,8 @@
                 // and it should be in this case, no harm to allow a release()
                 // if we're already uninitialized.
                 sp<AMessage> response = new AMessage;
+                // TODO: we shouldn't throw an exception for stop/release. Change this to wait until
+                // the previous stop/release completes and then reply with OK.
                 status_t err = mState == targetState ? OK : INVALID_OPERATION;
                 response->setInt32("err", err);
                 if (err == OK && targetState == UNINITIALIZED) {
@@ -1408,6 +1890,10 @@
                     msg->what() == kWhatStop /* keepComponentAllocated */);
 
             returnBuffersToCodec();
+
+            if (mSoftRenderer != NULL && (mFlags & kFlagPushBlankBuffersOnShutdown)) {
+                pushBlankBuffersToNativeWindow(mSurface.get());
+            }
             break;
         }
 
@@ -1587,8 +2073,12 @@
         {
             sp<AReplyToken> replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
+            // Unfortunately widevine legacy source requires knowing all of the
+            // codec input buffers, so we have to provide them even in async mode.
+            int32_t widevine = 0;
+            msg->findInt32("widevine", &widevine);
 
-            if (!isExecuting() || (mFlags & kFlagIsAsync)) {
+            if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
             } else if (mFlags & kFlagStickyError) {
@@ -1769,7 +2259,7 @@
         mSoftRenderer = NULL;
 
         mCrypto.clear();
-        setNativeWindow(NULL);
+        handleSetSurface(NULL);
 
         mInputFormat.clear();
         mOutputFormat.clear();
@@ -1953,7 +2443,8 @@
                 key,
                 iv,
                 mode,
-                info->mEncryptedData->base() + offset,
+                info->mSharedEncryptedBuffer,
+                offset,
                 subSamples,
                 numSubSamples,
                 info->mData->base(),
@@ -2074,37 +2565,44 @@
     return index;
 }
 
-status_t MediaCodec::setNativeWindow(
-        const sp<Surface> &surfaceTextureClient) {
-    status_t err;
-
-    if (mNativeWindow != NULL) {
-        err = native_window_api_disconnect(
-                mNativeWindow.get(), NATIVE_WINDOW_API_MEDIA);
-
-        if (err != OK) {
-            ALOGW("native_window_api_disconnect returned an error: %s (%d)",
-                    strerror(-err), err);
+status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
+    status_t err = OK;
+    if (surface != NULL) {
+        err = native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA);
+        if (err == BAD_VALUE) {
+            ALOGI("native window already connected. Assuming no change of surface");
+        } else if (err != OK) {
+            ALOGE("native_window_api_connect returned an error: %s (%d)", strerror(-err), err);
         }
-
-        mNativeWindow.clear();
     }
+    return err;
+}
 
-    if (surfaceTextureClient != NULL) {
-        err = native_window_api_connect(
-                surfaceTextureClient.get(), NATIVE_WINDOW_API_MEDIA);
-
+status_t MediaCodec::disconnectFromSurface() {
+    status_t err = OK;
+    if (mSurface != NULL) {
+        err = native_window_api_disconnect(mSurface.get(), NATIVE_WINDOW_API_MEDIA);
         if (err != OK) {
-            ALOGE("native_window_api_connect returned an error: %s (%d)",
-                    strerror(-err), err);
-
-            return err;
+            ALOGW("native_window_api_disconnect returned an error: %s (%d)", strerror(-err), err);
         }
-
-        mNativeWindow = surfaceTextureClient;
+        // assume disconnected even on error
+        mSurface.clear();
     }
+    return err;
+}
 
-    return OK;
+status_t MediaCodec::handleSetSurface(const sp<Surface> &surface) {
+    status_t err = OK;
+    if (mSurface != NULL) {
+        (void)disconnectFromSurface();
+    }
+    if (surface != NULL) {
+        err = connectToSurface(surface);
+        if (err == OK) {
+            mSurface = surface;
+        }
+    }
+    return err;
 }
 
 void MediaCodec::onInputBufferAvailable() {
@@ -2263,12 +2761,6 @@
 
 void MediaCodec::updateBatteryStat() {
     if (mState == CONFIGURED && !mBatteryStatNotified) {
-        AString mime;
-        CHECK(mOutputFormat != NULL &&
-                mOutputFormat->findString("mime", &mime));
-
-        mIsVideo = mime.startsWithIgnoreCase("video/");
-
         BatteryNotifier& notifier(BatteryNotifier::getInstance());
 
         if (mIsVideo) {
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index cf6e937..d2352bc 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -18,11 +18,15 @@
 #define LOG_TAG "MediaCodecList"
 #include <utils/Log.h>
 
+#include "MediaCodecListOverrides.h"
+
 #include <binder/IServiceManager.h>
 
 #include <media/IMediaCodecList.h>
 #include <media/IMediaPlayerService.h>
+#include <media/IResourceManagerService.h>
 #include <media/MediaCodecInfo.h>
+#include <media/MediaResourcePolicy.h>
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -31,6 +35,7 @@
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
 
+#include <sys/stat.h>
 #include <utils/threads.h>
 
 #include <libexpat/expat.h>
@@ -41,21 +46,59 @@
 
 static MediaCodecList *gCodecList = NULL;
 
+static bool parseBoolean(const char *s) {
+    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
+        return true;
+    }
+    char *end;
+    unsigned long res = strtoul(s, &end, 10);
+    return *s != '\0' && *end == '\0' && res > 0;
+}
+
 // static
 sp<IMediaCodecList> MediaCodecList::sCodecList;
 
 // static
 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
-    Mutex::Autolock autoLock(sInitMutex);
+    bool profilingNeeded = false;
+    Vector<sp<MediaCodecInfo>> infos;
 
-    if (gCodecList == NULL) {
-        gCodecList = new MediaCodecList;
-        if (gCodecList->initCheck() == OK) {
-            sCodecList = gCodecList;
+    {
+        Mutex::Autolock autoLock(sInitMutex);
+
+        if (gCodecList == NULL) {
+            gCodecList = new MediaCodecList;
+            if (gCodecList->initCheck() == OK) {
+                sCodecList = gCodecList;
+
+                struct stat s;
+                if (stat(kProfilingResults, &s) == -1) {
+                    // profiling results doesn't existed
+                    profilingNeeded = true;
+                    for (size_t i = 0; i < gCodecList->countCodecs(); ++i) {
+                        infos.push_back(gCodecList->getCodecInfo(i));
+                    }
+                }
+            } else {
+                // failure to initialize may be temporary. retry on next call.
+                delete gCodecList;
+                gCodecList = NULL;
+            }
         }
     }
 
-    return sCodecList;
+    if (profilingNeeded) {
+        profileCodecs(infos);
+    }
+
+    {
+        Mutex::Autolock autoLock(sInitMutex);
+        if (profilingNeeded) {
+            gCodecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
+        }
+
+        return sCodecList;
+    }
 }
 
 static Mutex sRemoteInitMutex;
@@ -94,11 +137,14 @@
 }
 
 MediaCodecList::MediaCodecList()
-    : mInitCheck(NO_INIT) {
+    : mInitCheck(NO_INIT),
+      mUpdate(false),
+      mGlobalSettings(new AMessage()) {
     parseTopLevelXMLFile("/etc/media_codecs.xml");
+    parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
 }
 
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
+void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
     // get href_base
     char *href_base_end = strrchr(codecs_xml, '/');
     if (href_base_end != NULL) {
@@ -112,20 +158,42 @@
     OMXClient client;
     mInitCheck = client.connect();
     if (mInitCheck != OK) {
-        return;
+        return;  // this may fail if IMediaPlayerService is not available.
     }
     mOMX = client.interface();
     parseXMLFile(codecs_xml);
     mOMX.clear();
 
     if (mInitCheck != OK) {
+        if (ignore_errors) {
+            mInitCheck = OK;
+            return;
+        }
         mCodecInfos.clear();
         return;
     }
 
+    Vector<MediaResourcePolicy> policies;
+    AString value;
+    if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
+        policies.push_back(
+                MediaResourcePolicy(
+                        String8(kPolicySupportsMultipleSecureCodecs),
+                        String8(value.c_str())));
+    }
+    if (policies.size() > 0) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
+        sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
+        if (service == NULL) {
+            ALOGE("MediaCodecList: failed to get ResourceManagerService");
+        } else {
+            service->config(policies);
+        }
+    }
+
     for (size_t i = mCodecInfos.size(); i-- > 0;) {
         const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-
         if (info.mCaps.size() == 0) {
             // No types supported by this component???
             ALOGW("Component %s does not support any type of media?",
@@ -169,6 +237,16 @@
                     }
                     ALOGV("    levels=[%s]", nice.c_str());
                 }
+                {
+                    AString quirks;
+                    for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
+                        if (ix > 0) {
+                            quirks.append(", ");
+                        }
+                        quirks.append(info.mQuirks[ix]);
+                    }
+                    ALOGV("    quirks=[%s]", quirks.c_str());
+                }
             }
 #endif
         }
@@ -328,6 +406,16 @@
                 mCurrentSection = SECTION_DECODERS;
             } else if (!strcmp(name, "Encoders")) {
                 mCurrentSection = SECTION_ENCODERS;
+            } else if (!strcmp(name, "Settings")) {
+                mCurrentSection = SECTION_SETTINGS;
+            }
+            break;
+        }
+
+        case SECTION_SETTINGS:
+        {
+            if (!strcmp(name, "Setting")) {
+                mInitCheck = addSettingFromAttributes(attrs);
             }
             break;
         }
@@ -397,6 +485,14 @@
     }
 
     switch (mCurrentSection) {
+        case SECTION_SETTINGS:
+        {
+            if (!strcmp(name, "Settings")) {
+                mCurrentSection = SECTION_TOPLEVEL;
+            }
+            break;
+        }
+
         case SECTION_DECODERS:
         {
             if (!strcmp(name, "Decoders")) {
@@ -462,10 +558,81 @@
     --mDepth;
 }
 
+status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
+    const char *name = NULL;
+    const char *value = NULL;
+    const char *update = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "value")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            value = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
+        } else {
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL || value == NULL) {
+        return -EINVAL;
+    }
+
+    mUpdate = (update != NULL) && parseBoolean(update);
+    if (mUpdate != mGlobalSettings->contains(name)) {
+        return -EINVAL;
+    }
+
+    mGlobalSettings->setString(name, value);
+    return OK;
+}
+
+void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
+    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+        if (AString(name) == mCodecInfos[i]->getCodecName()) {
+            if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
+                ALOGW("Overrides with an unexpected mime %s", type);
+                // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
+                // overrides we don't want.
+                mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+            } else {
+                mCurrentInfo = mCodecInfos.editItemAt(i);
+                mCurrentInfo->updateMime(type);  // to set the current cap
+            }
+            return;
+        }
+    }
+    mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+    // The next step involves trying to load the codec, which may
+    // fail.  Only list the codec if this succeeds.
+    // However, keep mCurrentInfo object around until parsing
+    // of full codec info is completed.
+    if (initializeCapabilities(type) == OK) {
+        mCodecInfos.push_back(mCurrentInfo);
+    }
+}
+
 status_t MediaCodecList::addMediaCodecFromAttributes(
         bool encoder, const char **attrs) {
     const char *name = NULL;
     const char *type = NULL;
+    const char *update = NULL;
 
     size_t i = 0;
     while (attrs[i] != NULL) {
@@ -481,6 +648,12 @@
             }
             type = attrs[i + 1];
             ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
         } else {
             return -EINVAL;
         }
@@ -492,14 +665,39 @@
         return -EINVAL;
     }
 
-    mCurrentInfo = new MediaCodecInfo(name, encoder, type);
-    // The next step involves trying to load the codec, which may
-    // fail.  Only list the codec if this succeeds.
-    // However, keep mCurrentInfo object around until parsing
-    // of full codec info is completed.
-    if (initializeCapabilities(type) == OK) {
-        mCodecInfos.push_back(mCurrentInfo);
+    mUpdate = (update != NULL) && parseBoolean(update);
+    ssize_t index = -1;
+    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+        if (AString(name) == mCodecInfos[i]->getCodecName()) {
+            index = i;
+        }
     }
+    if (mUpdate != (index >= 0)) {
+        return -EINVAL;
+    }
+
+    if (index >= 0) {
+        // existing codec
+        mCurrentInfo = mCodecInfos.editItemAt(index);
+        if (type != NULL) {
+            // existing type
+            if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
+                return -EINVAL;
+            }
+            mCurrentInfo->updateMime(type);
+        }
+    } else {
+        // new codec
+        mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+        // The next step involves trying to load the codec, which may
+        // fail.  Only list the codec if this succeeds.
+        // However, keep mCurrentInfo object around until parsing
+        // of full codec info is completed.
+        if (initializeCapabilities(type) == OK) {
+            mCodecInfos.push_back(mCurrentInfo);
+        }
+    }
+
     return OK;
 }
 
@@ -553,6 +751,7 @@
 
 status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
     const char *name = NULL;
+    const char *update = NULL;
 
     size_t i = 0;
     while (attrs[i] != NULL) {
@@ -562,6 +761,12 @@
             }
             name = attrs[i + 1];
             ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
         } else {
             return -EINVAL;
         }
@@ -573,14 +778,25 @@
         return -EINVAL;
     }
 
-    status_t ret = mCurrentInfo->addMime(name);
+    bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
+    if (mUpdate != isExistingType) {
+        return -EINVAL;
+    }
+
+    status_t ret;
+    if (mUpdate) {
+        ret = mCurrentInfo->updateMime(name);
+    } else {
+        ret = mCurrentInfo->addMime(name);
+    }
+
     if (ret != OK) {
         return ret;
     }
 
     // The next step involves trying to load the codec, which may
     // fail.  Handle this gracefully (by not reporting such mime).
-    if (initializeCapabilities(name) != OK) {
+    if (!mUpdate && initializeCapabilities(name) != OK) {
         mCurrentInfo->removeMime(name);
     }
     return OK;
@@ -675,14 +891,16 @@
         return -EINVAL;
     }
 
-    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range
+    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
+    // measured-frame-rate, measured-blocks-per-second: range
     // quality: range + default + [scale]
     // complexity: range + default
     bool found;
 
     if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
             || name == "blocks-per-second" || name == "complexity"
-            || name == "frame-rate" || name == "quality" || name == "size") {
+            || name == "frame-rate" || name == "quality" || name == "size"
+            || name == "measured-blocks-per-second" || name == "measured-frame-rate") {
         AString min, max;
         if (msg->findString("min", &min) && msg->findString("max", &max)) {
             min.append("-");
@@ -758,7 +976,8 @@
             return limitFoundMissingAttr(name, "ranges", found);
         } else if (msg->contains("scale")) {
             return limitFoundMissingAttr(name, "scale");
-        } else if ((name == "alignment" || name == "block-size") ^
+        } else if ((name == "alignment" || name == "block-size"
+                || name == "max-supported-instances") ^
                 (found = msg->findString("value", &value))) {
             return limitFoundMissingAttr(name, "value", found);
         }
@@ -780,15 +999,6 @@
     return OK;
 }
 
-static bool parseBoolean(const char *s) {
-    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
-        return true;
-    }
-    char *end;
-    unsigned long res = strtoul(s, &end, 10);
-    return *s != '\0' && *end == '\0' && res > 0;
-}
-
 status_t MediaCodecList::addFeature(const char **attrs) {
     size_t i = 0;
     const char *name = NULL;
@@ -860,4 +1070,8 @@
     return mCodecInfos.size();
 }
 
+const sp<AMessage> MediaCodecList::getGlobalSettings() const {
+    return mGlobalSettings;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
new file mode 100644
index 0000000..0d95676
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecListOverrides"
+#include <utils/Log.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <gui/Surface.h>
+#include <media/ICrypto.h>
+#include <media/IMediaCodecList.h>
+#include <media/MediaCodecInfo.h>
+#include <media/MediaResourcePolicy.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+
+namespace android {
+
+const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
+
+// a limit to avoid allocating unreasonable number of codec instances in the measurement.
+// this should be in sync with the MAX_SUPPORTED_INSTANCES defined in MediaCodecInfo.java.
+static const int kMaxInstances = 32;
+
+// TODO: move MediaCodecInfo to C++. Until then, some temp methods to parse out info.
+static bool getMeasureSize(sp<MediaCodecInfo::Capabilities> caps, int32_t *width, int32_t *height) {
+    AString sizeRange;
+    if (!caps->getDetails()->findString("size-range", &sizeRange)) {
+        return false;
+    }
+    AString minSize;
+    AString maxSize;
+    if (!splitString(sizeRange, "-", &minSize, &maxSize)) {
+        return false;
+    }
+    AString sWidth;
+    AString sHeight;
+    if (!splitString(minSize, "x", &sWidth, &sHeight)) {
+        if (!splitString(minSize, "*", &sWidth, &sHeight)) {
+            return false;
+        }
+    }
+
+    *width = strtol(sWidth.c_str(), NULL, 10);
+    *height = strtol(sHeight.c_str(), NULL, 10);
+    return (*width > 0) && (*height > 0);
+}
+
+static void getMeasureBitrate(sp<MediaCodecInfo::Capabilities> caps, int32_t *bitrate) {
+    // Until have native MediaCodecInfo, we cannot get bitrates based on profile/levels.
+    // We use 200000 as default value for our measurement.
+    *bitrate = 200000;
+    AString bitrateRange;
+    if (!caps->getDetails()->findString("bitrate-range", &bitrateRange)) {
+        return;
+    }
+    AString minBitrate;
+    AString maxBitrate;
+    if (!splitString(bitrateRange, "-", &minBitrate, &maxBitrate)) {
+        return;
+    }
+
+    *bitrate = strtol(minBitrate.c_str(), NULL, 10);
+}
+
+static sp<AMessage> getMeasureFormat(
+        bool isEncoder, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+    sp<AMessage> format = new AMessage();
+    format->setString("mime", mime);
+
+    if (isEncoder) {
+        int32_t bitrate = 0;
+        getMeasureBitrate(caps, &bitrate);
+        format->setInt32("bitrate", bitrate);
+    }
+
+    if (mime.startsWith("video/")) {
+        int32_t width = 0;
+        int32_t height = 0;
+        if (!getMeasureSize(caps, &width, &height)) {
+            return NULL;
+        }
+        format->setInt32("width", width);
+        format->setInt32("height", height);
+
+        Vector<uint32_t> colorFormats;
+        caps->getSupportedColorFormats(&colorFormats);
+        if (colorFormats.size() == 0) {
+            return NULL;
+        }
+        format->setInt32("color-format", colorFormats[0]);
+
+        format->setFloat("frame-rate", 10.0);
+        format->setInt32("i-frame-interval", 10);
+    } else {
+        // TODO: profile hw audio
+        return NULL;
+    }
+
+    return format;
+}
+
+static size_t doProfileCodecs(
+        bool isEncoder, AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+    sp<AMessage> format = getMeasureFormat(isEncoder, mime, caps);
+    if (format == NULL) {
+        return 0;
+    }
+    if (isEncoder) {
+        format->setInt32("encoder", 1);
+    }
+    ALOGV("doProfileCodecs %s %s %s %s",
+            name.c_str(), mime.c_str(), isEncoder ? "encoder" : "decoder",
+            format->debugString().c_str());
+
+    status_t err = OK;
+    Vector<sp<MediaCodec>> codecs;
+    while (err == OK && codecs.size() < kMaxInstances) {
+        sp<ALooper> looper = new ALooper;
+        looper->setName("MediaCodec_looper");
+        ALOGV("doProfileCodecs for codec #%zu", codecs.size());
+        ALOGV("doProfileCodecs start looper");
+        looper->start(
+                false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO);
+        ALOGV("doProfileCodecs CreateByComponentName");
+        sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err);
+        if (err != OK) {
+            ALOGV("Failed to create codec: %s", name.c_str());
+            break;
+        }
+        const sp<Surface> nativeWindow;
+        const sp<ICrypto> crypto;
+        uint32_t flags = 0;
+        ALOGV("doProfileCodecs configure");
+        err = codec->configure(format, nativeWindow, crypto, flags);
+        if (err != OK) {
+            ALOGV("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str());
+            codec->release();
+            break;
+        }
+        ALOGV("doProfileCodecs start");
+        err = codec->start();
+        if (err != OK) {
+            ALOGV("Failed to start codec: %s with mime: %s", name.c_str(), mime.c_str());
+            codec->release();
+            break;
+        }
+        codecs.push_back(codec);
+    }
+
+    for (size_t i = 0; i < codecs.size(); ++i) {
+        ALOGV("doProfileCodecs release %s", name.c_str());
+        err = codecs[i]->release();
+        if (err != OK) {
+            ALOGE("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str());
+        }
+    }
+
+    return codecs.size();
+}
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2) {
+    ssize_t pos = s.find(delimiter.c_str());
+    if (pos < 0) {
+        return false;
+    }
+    *s1 = AString(s, 0, pos);
+    *s2 = AString(s, pos + 1, s.size() - pos - 1);
+    return true;
+}
+
+bool splitString(
+        const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3) {
+    AString temp;
+    if (!splitString(s, delimiter, s1, &temp)) {
+        return false;
+    }
+    if (!splitString(temp, delimiter, s2, s3)) {
+        return false;
+    }
+    return true;
+}
+
+void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) {
+    CodecSettings global_results;
+    KeyedVector<AString, CodecSettings> encoder_results;
+    KeyedVector<AString, CodecSettings> decoder_results;
+    profileCodecs(infos, &global_results, &encoder_results, &decoder_results);
+    exportResultsToXML(kProfilingResults, global_results, encoder_results, decoder_results);
+}
+
+void profileCodecs(
+        const Vector<sp<MediaCodecInfo>> &infos,
+        CodecSettings *global_results,
+        KeyedVector<AString, CodecSettings> *encoder_results,
+        KeyedVector<AString, CodecSettings> *decoder_results,
+        bool forceToMeasure) {
+    KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
+    AString supportMultipleSecureCodecs = "true";
+    for (size_t i = 0; i < infos.size(); ++i) {
+        const sp<MediaCodecInfo> info = infos[i];
+        AString name = info->getCodecName();
+        if (name.startsWith("OMX.google.") ||
+                // TODO: reenable below codecs once fixed
+                name == "OMX.Intel.VideoDecoder.VP9.hybrid") {
+            continue;
+        }
+
+        Vector<AString> mimes;
+        info->getSupportedMimes(&mimes);
+        for (size_t i = 0; i < mimes.size(); ++i) {
+            const sp<MediaCodecInfo::Capabilities> &caps =
+                    info->getCapabilitiesFor(mimes[i].c_str());
+            if (!forceToMeasure && caps->getDetails()->contains("max-supported-instances")) {
+                continue;
+            }
+
+            size_t max = doProfileCodecs(info->isEncoder(), name, mimes[i], caps);
+            if (max > 0) {
+                CodecSettings settings;
+                char maxStr[32];
+                sprintf(maxStr, "%zu", max);
+                settings.add("max-supported-instances", maxStr);
+
+                AString key = name;
+                key.append(" ");
+                key.append(mimes[i]);
+
+                if (info->isEncoder()) {
+                    encoder_results->add(key, settings);
+                } else {
+                    decoder_results->add(key, settings);
+                }
+
+                if (name.endsWith(".secure")) {
+                    if (max <= 1) {
+                        supportMultipleSecureCodecs = "false";
+                    }
+                }
+            }
+        }
+    }
+    global_results->add(kPolicySupportsMultipleSecureCodecs, supportMultipleSecureCodecs);
+}
+
+static AString globalResultsToXml(const CodecSettings& results) {
+    AString ret;
+    for (size_t i = 0; i < results.size(); ++i) {
+        AString setting = AStringPrintf(
+                "        <Setting name=\"%s\" value=\"%s\" />\n",
+                results.keyAt(i).c_str(),
+                results.valueAt(i).c_str());
+        ret.append(setting);
+    }
+    return ret;
+}
+
+static AString codecResultsToXml(const KeyedVector<AString, CodecSettings>& results) {
+    AString ret;
+    for (size_t i = 0; i < results.size(); ++i) {
+        AString name;
+        AString mime;
+        if (!splitString(results.keyAt(i), " ", &name, &mime)) {
+            continue;
+        }
+        AString codec =
+                AStringPrintf("        <MediaCodec name=\"%s\" type=\"%s\" update=\"true\" >\n",
+                              name.c_str(),
+                              mime.c_str());
+        ret.append(codec);
+        CodecSettings settings = results.valueAt(i);
+        for (size_t i = 0; i < settings.size(); ++i) {
+            // WARNING: we assume all the settings are "Limit". Currently we have only one type
+            // of setting in this case, which is "max-supported-instances".
+            AString setting = AStringPrintf(
+                    "            <Limit name=\"%s\" value=\"%s\" />\n",
+                    settings.keyAt(i).c_str(),
+                    settings.valueAt(i).c_str());
+            ret.append(setting);
+        }
+        ret.append("        </MediaCodec>\n");
+    }
+    return ret;
+}
+
+void exportResultsToXML(
+        const char *fileName,
+        const CodecSettings& global_results,
+        const KeyedVector<AString, CodecSettings>& encoder_results,
+        const KeyedVector<AString, CodecSettings>& decoder_results) {
+    if (global_results.size() == 0 && encoder_results.size() == 0 && decoder_results.size() == 0) {
+        return;
+    }
+
+    AString overrides;
+    overrides.append("<MediaCodecs>\n");
+    if (global_results.size() > 0) {
+        overrides.append("    <Settings>\n");
+        overrides.append(globalResultsToXml(global_results));
+        overrides.append("    </Settings>\n");
+    }
+    if (encoder_results.size() > 0) {
+        overrides.append("    <Encoders>\n");
+        overrides.append(codecResultsToXml(encoder_results));
+        overrides.append("    </Encoders>\n");
+    }
+    if (decoder_results.size() > 0) {
+        overrides.append("    <Decoders>\n");
+        overrides.append(codecResultsToXml(decoder_results));
+        overrides.append("    </Decoders>\n");
+    }
+    overrides.append("</MediaCodecs>\n");
+
+    FILE *f = fopen(fileName, "wb");
+    if (f == NULL) {
+        ALOGE("Failed to open %s for writing.", fileName);
+        return;
+    }
+    if (fwrite(overrides.c_str(), 1, overrides.size(), f) != overrides.size()) {
+        ALOGE("Failed to write to %s.", fileName);
+    }
+    fclose(f);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
new file mode 100644
index 0000000..e350d2a
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#define MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+extern const char *kProfilingResults;
+
+struct MediaCodecInfo;
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2);
+
+// profile codecs and save the result to xml file named kProfilingResults.
+void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos);
+
+// profile codecs and save the result to global_results, encoder_results and decoder_results.
+void profileCodecs(
+        const Vector<sp<MediaCodecInfo>> &infos,
+        CodecSettings *global_results,
+        KeyedVector<AString, CodecSettings> *encoder_results,
+        KeyedVector<AString, CodecSettings> *decoder_results,
+        bool forceToMeasure = false);
+
+void exportResultsToXML(
+        const char *fileName,
+        const CodecSettings& global_results,
+        const KeyedVector<AString, CodecSettings>& encoder_results,
+        const KeyedVector<AString, CodecSettings>& decoder_results);
+
+}  // namespace android
+
+#endif  // MEDIA_CODEC_LIST_OVERRIDES_H_
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index b6fa810..e089c46 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -20,6 +20,7 @@
 
 #include <inttypes.h>
 
+#include <gui/IGraphicBufferConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
@@ -29,10 +30,11 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaCodec.h>
-#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaCodecSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MediaCodecSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/PersistentSurface.h>
 #include <media/stagefright/Utils.h>
 
 namespace android {
@@ -258,9 +260,10 @@
         const sp<ALooper> &looper,
         const sp<AMessage> &format,
         const sp<MediaSource> &source,
+        const sp<IGraphicBufferConsumer> &consumer,
         uint32_t flags) {
     sp<MediaCodecSource> mediaSource =
-            new MediaCodecSource(looper, format, source, flags);
+            new MediaCodecSource(looper, format, source, consumer, flags);
 
     if (mediaSource->init() == OK) {
         return mediaSource;
@@ -328,6 +331,7 @@
         const sp<ALooper> &looper,
         const sp<AMessage> &outputFormat,
         const sp<MediaSource> &source,
+        const sp<IGraphicBufferConsumer> &consumer,
         uint32_t flags)
     : mLooper(looper),
       mOutputFormat(outputFormat),
@@ -337,6 +341,7 @@
       mStarted(false),
       mStopping(false),
       mDoMoreWorkPending(false),
+      mGraphicBufferConsumer(consumer),
       mFirstSampleTimeUs(-1ll),
       mEncoderReachedEOS(false),
       mErrorCode(OK) {
@@ -399,6 +404,9 @@
 
     ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
 
+    mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
+    mEncoder->setCallback(mEncoderActivityNotify);
+
     status_t err = mEncoder->configure(
                 mOutputFormat,
                 NULL /* nativeWindow */,
@@ -415,16 +423,21 @@
     if (mFlags & FLAG_USE_SURFACE_INPUT) {
         CHECK(mIsVideo);
 
-        err = mEncoder->createInputSurface(&mGraphicBufferProducer);
+        if (mGraphicBufferConsumer != NULL) {
+            // When using persistent surface, we are only interested in the
+            // consumer, but have to use PersistentSurface as a wrapper to
+            // pass consumer over messages (similar to BufferProducerWrapper)
+            err = mEncoder->setInputSurface(
+                    new PersistentSurface(NULL, mGraphicBufferConsumer));
+        } else {
+            err = mEncoder->createInputSurface(&mGraphicBufferProducer);
+        }
 
         if (err != OK) {
             return err;
         }
     }
 
-    mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
-    mEncoder->setCallback(mEncoderActivityNotify);
-
     err = mEncoder->start();
 
     if (err != OK) {
@@ -682,7 +695,6 @@
             size_t size;
             int64_t timeUs;
             int32_t flags;
-            native_handle_t* handle = NULL;
 
             CHECK(msg->findInt32("index", &index));
             CHECK(msg->findSize("offset", &offset));
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index c48a5ae..b0a65d2 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -62,5 +62,6 @@
 const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
 const char *MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
 const char *MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+const char *MEDIA_MIMETYPE_DATA_METADATA = "application/octet-stream";
 
 }  // namespace android
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index 7b6c7d9..b402e48 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -49,6 +49,7 @@
         mMutex(),
         mReleaseCondition(),
         mNumOutstandingBuffers(0),
+        mUsageFlagsFromOutput(0),
         mNativeSampleRateInHz(0),
         mNumFramesWritten(0),
         mHasAudio(false),
@@ -56,6 +57,10 @@
         mPlaybackRate(0.0) {
     mMediaClock = new MediaClock;
 
+    // initialize settings
+    mPlaybackSettings = AUDIO_PLAYBACK_RATE_DEFAULT;
+    mPlaybackSettings.mSpeed = mPlaybackRate;
+
     mLooper = new ALooper;
     mLooper->setName("MediaSync");
     mLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
@@ -75,18 +80,37 @@
     }
 }
 
-status_t MediaSync::configureSurface(const sp<IGraphicBufferProducer> &output) {
+status_t MediaSync::setSurface(const sp<IGraphicBufferProducer> &output) {
     Mutex::Autolock lock(mMutex);
 
-    // TODO: support suface change.
-    if (mOutput != NULL) {
-        ALOGE("configureSurface: output surface has already been configured.");
+    if (output == mOutput) {
+        return NO_ERROR;  // same output surface.
+    }
+
+    if (output == NULL && mSyncSettings.mSource == AVSYNC_SOURCE_VSYNC) {
+        ALOGE("setSurface: output surface is used as sync source and cannot be removed.");
         return INVALID_OPERATION;
     }
 
     if (output != NULL) {
+        int newUsage = 0;
+        output->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &newUsage);
+
+        // Check usage flags only when current output surface has been used to create input surface.
+        if (mOutput != NULL && mInput != NULL) {
+            int ignoredFlags = (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER
+                    | GRALLOC_USAGE_EXTERNAL_DISP);
+            // New output surface is not allowed to add new usage flag except ignored ones.
+            if ((newUsage & ~(mUsageFlagsFromOutput | ignoredFlags)) != 0) {
+                ALOGE("setSurface: new output surface has new usage flag not used by current one.");
+                return BAD_VALUE;
+            }
+        }
+
+        // Try to connect to new output surface. If failed, current output surface will not
+        // be changed.
         IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
-        sp<OutputListener> listener(new OutputListener(this));
+        sp<OutputListener> listener(new OutputListener(this, output));
         IInterface::asBinder(output)->linkToDeath(listener);
         status_t status =
             output->connect(listener,
@@ -94,31 +118,63 @@
                             true /* producerControlledByApp */,
                             &queueBufferOutput);
         if (status != NO_ERROR) {
-            ALOGE("configureSurface: failed to connect (%d)", status);
+            ALOGE("setSurface: failed to connect (%d)", status);
             return status;
         }
-
-        mOutput = output;
     }
 
+    if (mOutput != NULL) {
+        mOutput->disconnect(NATIVE_WINDOW_API_MEDIA);
+        while (!mBuffersSentToOutput.isEmpty()) {
+            returnBufferToInput_l(mBuffersSentToOutput.valueAt(0), Fence::NO_FENCE);
+            mBuffersSentToOutput.removeItemsAt(0);
+        }
+    }
+
+    mOutput = output;
+
     return NO_ERROR;
 }
 
 // |audioTrack| is used only for querying information.
-status_t MediaSync::configureAudioTrack(
-        const sp<AudioTrack> &audioTrack, uint32_t nativeSampleRateInHz) {
+status_t MediaSync::setAudioTrack(const sp<AudioTrack> &audioTrack) {
     Mutex::Autolock lock(mMutex);
 
     // TODO: support audio track change.
     if (mAudioTrack != NULL) {
-        ALOGE("configureAudioTrack: audioTrack has already been configured.");
+        ALOGE("setAudioTrack: audioTrack has already been configured.");
         return INVALID_OPERATION;
     }
 
-    mAudioTrack = audioTrack;
-    mNativeSampleRateInHz = nativeSampleRateInHz;
+    if (audioTrack == NULL && mSyncSettings.mSource == AVSYNC_SOURCE_AUDIO) {
+        ALOGE("setAudioTrack: audioTrack is used as sync source and cannot be removed.");
+        return INVALID_OPERATION;
+    }
 
-    return NO_ERROR;
+    if (audioTrack != NULL) {
+        // check if audio track supports the playback settings
+        if (mPlaybackSettings.mSpeed != 0.f
+                && audioTrack->setPlaybackRate(mPlaybackSettings) != OK) {
+            ALOGE("playback settings are not supported by the audio track");
+            return INVALID_OPERATION;
+        }
+        uint32_t nativeSampleRateInHz = audioTrack->getOriginalSampleRate();
+        if (nativeSampleRateInHz <= 0) {
+            ALOGE("setAudioTrack: native sample rate should be positive.");
+            return BAD_VALUE;
+        }
+        mAudioTrack = audioTrack;
+        mNativeSampleRateInHz = nativeSampleRateInHz;
+        (void)setPlaybackSettings_l(mPlaybackSettings);
+    }
+    else {
+        mAudioTrack = NULL;
+        mNativeSampleRateInHz = 0;
+    }
+
+    // potentially resync to new source
+    resync_l();
+    return OK;
 }
 
 status_t MediaSync::createInputSurface(
@@ -147,33 +203,79 @@
         bufferConsumer->consumerConnect(listener, false /* controlledByApp */);
     if (status == NO_ERROR) {
         bufferConsumer->setConsumerName(String8("MediaSync"));
+        // propagate usage bits from output surface
+        mUsageFlagsFromOutput = 0;
+        mOutput->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &mUsageFlagsFromOutput);
+        bufferConsumer->setConsumerUsageBits(mUsageFlagsFromOutput);
         *outBufferProducer = bufferProducer;
         mInput = bufferConsumer;
     }
     return status;
 }
 
-status_t MediaSync::setPlaybackRate(float rate) {
-    if (rate < 0.0) {
-        return BAD_VALUE;
+void MediaSync::resync_l() {
+    AVSyncSource src = mSyncSettings.mSource;
+    if (src == AVSYNC_SOURCE_DEFAULT) {
+        if (mAudioTrack != NULL) {
+            src = AVSYNC_SOURCE_AUDIO;
+        } else {
+            src = AVSYNC_SOURCE_SYSTEM_CLOCK;
+        }
     }
 
-    Mutex::Autolock lock(mMutex);
+    // TODO: resync ourselves to the current clock (e.g. on sync source change)
+    updatePlaybackRate_l(mPlaybackRate);
+}
 
+void MediaSync::updatePlaybackRate_l(float rate) {
     if (rate > mPlaybackRate) {
         mNextBufferItemMediaUs = -1;
     }
     mPlaybackRate = rate;
     mMediaClock->setPlaybackRate(rate);
     onDrainVideo_l();
-
-    return OK;
 }
 
 sp<const MediaClock> MediaSync::getMediaClock() {
     return mMediaClock;
 }
 
+status_t MediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) {
+    Mutex::Autolock lock(mMutex);
+    // User should check the playback rate if it doesn't want to receive a
+    // huge number for play time.
+    if (mPlaybackRate == 0.0f) {
+        *outTimeUs = INT64_MAX;
+        return OK;
+    }
+
+    uint32_t numFramesPlayed = 0;
+    if (mAudioTrack != NULL) {
+        status_t res = mAudioTrack->getPosition(&numFramesPlayed);
+        if (res != OK) {
+            return res;
+        }
+    }
+
+    int64_t numPendingFrames = mNumFramesWritten - numFramesPlayed;
+    if (numPendingFrames < 0) {
+        numPendingFrames = 0;
+        ALOGW("getPlayTimeForPendingAudioFrames: pending frame count is negative.");
+    }
+    double timeUs = numPendingFrames * 1000000.0
+            / (mNativeSampleRateInHz * (double)mPlaybackRate);
+    if (timeUs > (double)INT64_MAX) {
+        // Overflow.
+        *outTimeUs = INT64_MAX;
+        ALOGW("getPlayTimeForPendingAudioFrames: play time for pending audio frames "
+              "is too high, possibly due to super low playback rate(%f)", mPlaybackRate);
+    } else {
+        *outTimeUs = (int64_t)timeUs;
+    }
+
+    return OK;
+}
+
 status_t MediaSync::updateQueuedAudioData(
         size_t sizeInBytes, int64_t presentationTimeUs) {
     if (sizeInBytes == 0) {
@@ -190,13 +292,14 @@
     int64_t numFrames = sizeInBytes / mAudioTrack->frameSize();
     int64_t maxMediaTimeUs = presentationTimeUs
             + getDurationIfPlayedAtNativeSampleRate_l(numFrames);
-    mNumFramesWritten += numFrames;
 
     int64_t nowUs = ALooper::GetNowUs();
-    int64_t nowMediaUs = maxMediaTimeUs
+    int64_t nowMediaUs = presentationTimeUs
             - getDurationIfPlayedAtNativeSampleRate_l(mNumFramesWritten)
             + getPlayedOutAudioDurationMedia_l(nowUs);
 
+    mNumFramesWritten += numFrames;
+
     int64_t oldRealTime = -1;
     if (mNextBufferItemMediaUs != -1) {
         oldRealTime = getRealTime(mNextBufferItemMediaUs, nowUs);
@@ -207,12 +310,13 @@
 
     if (oldRealTime != -1) {
         int64_t newRealTime = getRealTime(mNextBufferItemMediaUs, nowUs);
-        if (newRealTime < oldRealTime) {
-            mNextBufferItemMediaUs = -1;
-            onDrainVideo_l();
+        if (newRealTime >= oldRealTime) {
+            return OK;
         }
     }
 
+    mNextBufferItemMediaUs = -1;
+    onDrainVideo_l();
     return OK;
 }
 
@@ -221,6 +325,95 @@
     mInput->setConsumerName(String8(name.c_str()));
 }
 
+status_t MediaSync::setVideoFrameRateHint(float rate) {
+    // ignored until we add the FrameScheduler
+    return rate >= 0.f ? OK : BAD_VALUE;
+}
+
+float MediaSync::getVideoFrameRate() {
+    // we don't know the frame rate
+    return -1.f;
+}
+
+status_t MediaSync::setSyncSettings(const AVSyncSettings &syncSettings) {
+    // validate settings
+    if (syncSettings.mSource >= AVSYNC_SOURCE_MAX
+            || syncSettings.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
+            || syncSettings.mTolerance < 0.f
+            || syncSettings.mTolerance >= AVSYNC_TOLERANCE_MAX) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mMutex);
+
+    // verify that we have the sync source
+    switch (syncSettings.mSource) {
+        case AVSYNC_SOURCE_AUDIO:
+            if (mAudioTrack == NULL) {
+                ALOGE("setSyncSettings: audio sync source requires an audio track");
+                return BAD_VALUE;
+            }
+            break;
+        case AVSYNC_SOURCE_VSYNC:
+            if (mOutput == NULL) {
+                ALOGE("setSyncSettings: vsync sync source requires an output surface");
+                return BAD_VALUE;
+            }
+            break;
+        default:
+            break;
+    }
+
+    mSyncSettings = syncSettings;
+    resync_l();
+    return OK;
+}
+
+void MediaSync::getSyncSettings(AVSyncSettings *syncSettings) {
+    Mutex::Autolock lock(mMutex);
+    *syncSettings = mSyncSettings;
+}
+
+status_t MediaSync::setPlaybackSettings(const AudioPlaybackRate &rate) {
+    Mutex::Autolock lock(mMutex);
+
+    status_t err = setPlaybackSettings_l(rate);
+    if (err == OK) {
+        // TODO: adjust rate if using VSYNC as source
+        updatePlaybackRate_l(rate.mSpeed);
+    }
+    return err;
+}
+
+status_t MediaSync::setPlaybackSettings_l(const AudioPlaybackRate &rate) {
+    if (rate.mSpeed < 0.f || rate.mPitch < 0.f) {
+        // We don't validate other audio settings.
+        // They will be validated when/if audiotrack is set.
+        return BAD_VALUE;
+    }
+
+    if (mAudioTrack != NULL) {
+        if (rate.mSpeed == 0.f) {
+            mAudioTrack->pause();
+        } else {
+            status_t err = mAudioTrack->setPlaybackRate(rate);
+            if (err != OK) {
+                return BAD_VALUE;
+            }
+
+            // ignore errors
+            (void)mAudioTrack->start();
+        }
+    }
+    mPlaybackSettings = rate;
+    return OK;
+}
+
+void MediaSync::getPlaybackSettings(AudioPlaybackRate *rate) {
+    Mutex::Autolock lock(mMutex);
+    *rate = mPlaybackSettings;
+}
+
 int64_t MediaSync::getRealTime(int64_t mediaTimeUs, int64_t nowUs) {
     int64_t realUs;
     if (mMediaClock->getRealTimeFor(mediaTimeUs, &realUs) != OK) {
@@ -311,12 +504,12 @@
         return;
     }
 
-    int64_t nowUs = ALooper::GetNowUs();
-
     while (!mBufferItems.empty()) {
+        int64_t nowUs = ALooper::GetNowUs();
         BufferItem *bufferItem = &*mBufferItems.begin();
         int64_t itemMediaUs = bufferItem->mTimestamp / 1000;
         int64_t itemRealUs = getRealTime(itemMediaUs, nowUs);
+
         if (itemRealUs <= nowUs) {
             if (mHasAudio) {
                 if (nowUs - itemRealUs <= kMaxAllowedVideoLateTimeUs) {
@@ -336,15 +529,13 @@
             }
 
             mBufferItems.erase(mBufferItems.begin());
-
-            if (mBufferItems.empty()) {
-                mNextBufferItemMediaUs = -1;
-            }
+            mNextBufferItemMediaUs = -1;
         } else {
             if (mNextBufferItemMediaUs == -1
-                    || mNextBufferItemMediaUs != itemMediaUs) {
+                    || mNextBufferItemMediaUs > itemMediaUs) {
                 sp<AMessage> msg = new AMessage(kWhatDrainVideo, this);
                 msg->post(itemRealUs - nowUs);
+                mNextBufferItemMediaUs = itemMediaUs;
             }
             break;
         }
@@ -389,8 +580,19 @@
         return;
     }
 
+    if (mBuffersFromInput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) {
+        // Something is wrong since this buffer should be at our hands, bail.
+        mInput->consumerDisconnect();
+        onAbandoned_l(true /* isInput */);
+        return;
+    }
+    mBuffersFromInput.add(bufferItem.mGraphicBuffer->getId(), bufferItem.mGraphicBuffer);
+
     mBufferItems.push_back(bufferItem);
-    onDrainVideo_l();
+
+    if (mBufferItems.size() == 1) {
+        onDrainVideo_l();
+    }
 }
 
 void MediaSync::renderOneBufferItem_l( const BufferItem &bufferItem) {
@@ -423,12 +625,24 @@
         return;
     }
 
+    if (mBuffersSentToOutput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) {
+        // Something is wrong since this buffer should be held by output now, bail.
+        mInput->consumerDisconnect();
+        onAbandoned_l(true /* isInput */);
+        return;
+    }
+    mBuffersSentToOutput.add(bufferItem.mGraphicBuffer->getId(), bufferItem.mGraphicBuffer);
+
     ALOGV("queued buffer %#llx to output", (long long)bufferItem.mGraphicBuffer->getId());
 }
 
-void MediaSync::onBufferReleasedByOutput() {
+void MediaSync::onBufferReleasedByOutput(sp<IGraphicBufferProducer> &output) {
     Mutex::Autolock lock(mMutex);
 
+    if (output != mOutput) {
+        return;  // This is not the current output, ignore.
+    }
+
     sp<GraphicBuffer> buffer;
     sp<Fence> fence;
     status_t status = mOutput->detachNextBuffer(&buffer, &fence);
@@ -449,14 +663,31 @@
         return;
     }
 
+    ssize_t ix = mBuffersSentToOutput.indexOfKey(buffer->getId());
+    if (ix < 0) {
+        // The buffer is unknown, maybe leftover, ignore.
+        return;
+    }
+    mBuffersSentToOutput.removeItemsAt(ix);
+
     returnBufferToInput_l(buffer, fence);
 }
 
 void MediaSync::returnBufferToInput_l(
         const sp<GraphicBuffer> &buffer, const sp<Fence> &fence) {
+    ssize_t ix = mBuffersFromInput.indexOfKey(buffer->getId());
+    if (ix < 0) {
+        // The buffer is unknown, something is wrong, bail.
+        mOutput->disconnect(NATIVE_WINDOW_API_MEDIA);
+        onAbandoned_l(false /* isInput */);
+        return;
+    }
+    sp<GraphicBuffer> oldBuffer = mBuffersFromInput.valueAt(ix);
+    mBuffersFromInput.removeItemsAt(ix);
+
     // Attach and release the buffer back to the input.
     int consumerSlot;
-    status_t status = mInput->attachBuffer(&consumerSlot, buffer);
+    status_t status = mInput->attachBuffer(&consumerSlot, oldBuffer);
     ALOGE_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status);
     if (status == NO_ERROR) {
         status = mInput->releaseBuffer(consumerSlot, 0 /* frameNumber */,
@@ -469,7 +700,7 @@
         return;
     }
 
-    ALOGV("released buffer %#llx to input", (long long)buffer->getId());
+    ALOGV("released buffer %#llx to input", (long long)oldBuffer->getId());
 
     // Notify any waiting onFrameAvailable calls.
     --mNumOutstandingBuffers;
@@ -494,6 +725,20 @@
         case kWhatDrainVideo:
         {
             Mutex::Autolock lock(mMutex);
+            if (mNextBufferItemMediaUs != -1) {
+                int64_t nowUs = ALooper::GetNowUs();
+                int64_t itemRealUs = getRealTime(mNextBufferItemMediaUs, nowUs);
+
+                // The message could arrive earlier than expected due to
+                // various reasons, e.g., media clock has been changed because
+                // of new anchor time or playback rate. In such cases, the
+                // message needs to be re-posted.
+                if (itemRealUs > nowUs) {
+                    msg->post(itemRealUs - nowUs);
+                    break;
+                }
+            }
+
             onDrainVideo_l();
             break;
         }
@@ -524,13 +769,15 @@
     mSync->onAbandoned_l(true /* isInput */);
 }
 
-MediaSync::OutputListener::OutputListener(const sp<MediaSync> &sync)
-      : mSync(sync) {}
+MediaSync::OutputListener::OutputListener(const sp<MediaSync> &sync,
+        const sp<IGraphicBufferProducer> &output)
+      : mSync(sync),
+        mOutput(output) {}
 
 MediaSync::OutputListener::~OutputListener() {}
 
 void MediaSync::OutputListener::onBufferReleased() {
-    mSync->onBufferReleasedByOutput();
+    mSync->onBufferReleasedByOutput(mOutput);
 }
 
 void MediaSync::OutputListener::binderDied(const wp<IBinder> &/* who */) {
diff --git a/media/libstagefright/MidiExtractor.cpp b/media/libstagefright/MidiExtractor.cpp
index 66fab77..f6b8c84 100644
--- a/media/libstagefright/MidiExtractor.cpp
+++ b/media/libstagefright/MidiExtractor.cpp
@@ -217,7 +217,7 @@
 }
 
 status_t MidiEngine::seekTo(int64_t positionUs) {
-    ALOGV("seekTo %lld", positionUs);
+    ALOGV("seekTo %lld", (long long)positionUs);
     EAS_RESULT result = EAS_Locate(mEasData, mEasHandle, positionUs / 1000, false);
     return result == EAS_SUCCESS ? OK : UNKNOWN_ERROR;
 }
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 8d70e50..1c53b40 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -503,7 +503,7 @@
 ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
     Mutex::Autolock autoSerializer(mSerializer);
 
-    ALOGV("readAt offset %lld, size %zu", offset, size);
+    ALOGV("readAt offset %lld, size %zu", (long long)offset, size);
 
     Mutex::Autolock autoLock(mLock);
     if (mDisconnecting) {
@@ -579,7 +579,7 @@
 ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
     CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
 
-    ALOGV("readInternal offset %lld size %zu", offset, size);
+    ALOGV("readInternal offset %lld size %zu", (long long)offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -640,7 +640,7 @@
         return OK;
     }
 
-    ALOGI("new range: offset= %lld", offset);
+    ALOGI("new range: offset= %lld", (long long)offset);
 
     mCacheOffset = offset;
 
@@ -719,10 +719,10 @@
         mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
     }
 
-    ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %" PRId64 " us",
+    ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %lld us",
          mLowwaterThresholdBytes,
          mHighwaterThresholdBytes,
-         mKeepAliveIntervalUs);
+         (long long)mKeepAliveIntervalUs);
 }
 
 // static
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 230c1f7..f1ebea2 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -104,6 +104,14 @@
             node_id node, OMX_U32 port_index,
             sp<IGraphicBufferProducer> *bufferProducer);
 
+    virtual status_t createPersistentInputSurface(
+            sp<IGraphicBufferProducer> *bufferProducer,
+            sp<IGraphicBufferConsumer> *bufferConsumer);
+
+    virtual status_t setInputSurface(
+            node_id node, OMX_U32 port_index,
+            const sp<IGraphicBufferConsumer> &bufferConsumer);
+
     virtual status_t signalEndOfInputStream(node_id node);
 
     virtual status_t allocateBuffer(
@@ -340,6 +348,20 @@
     return err;
 }
 
+status_t MuxOMX::createPersistentInputSurface(
+        sp<IGraphicBufferProducer> *bufferProducer,
+        sp<IGraphicBufferConsumer> *bufferConsumer) {
+    // TODO: local or remote? Always use remote for now
+    return mRemoteOMX->createPersistentInputSurface(
+            bufferProducer, bufferConsumer);
+}
+
+status_t MuxOMX::setInputSurface(
+        node_id node, OMX_U32 port_index,
+        const sp<IGraphicBufferConsumer> &bufferConsumer) {
+    return getOMX(node)->setInputSurface(node, port_index, bufferConsumer);
+}
+
 status_t MuxOMX::signalEndOfInputStream(node_id node) {
     return getOMX(node)->signalEndOfInputStream(node);
 }
@@ -400,10 +422,16 @@
     sp<IBinder> binder = sm->getService(String16("media.player"));
     sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
 
-    CHECK(service.get() != NULL);
+    if (service.get() == NULL) {
+        ALOGE("Cannot obtain IMediaPlayerService");
+        return NO_INIT;
+    }
 
     mOMX = service->getOMX();
-    CHECK(mOMX.get() != NULL);
+    if (mOMX.get() == NULL) {
+        ALOGE("Cannot obtain IOMX");
+        return NO_INIT;
+    }
 
     if (!mOMX->livesLocally(0 /* node */, getpid())) {
         ALOGI("Using client-side OMX mux.");
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4d30069..aa6a7c0 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -43,6 +43,7 @@
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SurfaceUtils.h>
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/SkipCutBuffer.h>
 #include <utils/Vector.h>
@@ -1057,7 +1058,7 @@
         const sp<MetaData>& meta,
         const CodecProfileLevel& defaultProfileLevel,
         CodecProfileLevel &profileLevel) {
-    CODEC_LOGV("Default profile: %ld, level %ld",
+    CODEC_LOGV("Default profile: %u, level #x%x",
             defaultProfileLevel.mProfile, defaultProfileLevel.mLevel);
 
     // Are the default profile and level overwriten?
@@ -1283,7 +1284,7 @@
     success = success && meta->findInt32(kKeyHeight, &height);
     CHECK(success);
 
-    CODEC_LOGV("setVideoOutputFormat width=%ld, height=%ld", width, height);
+    CODEC_LOGV("setVideoOutputFormat width=%d, height=%d", width, height);
 
     OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
     if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
@@ -1650,7 +1651,7 @@
         return err;
     }
 
-    CODEC_LOGV("allocating %lu buffers of size %lu on %s port",
+    CODEC_LOGV("allocating %u buffers of size %u on %s port",
             def.nBufferCountActual, def.nBufferSize,
             portIndex == kPortIndexInput ? "input" : "output");
 
@@ -1723,7 +1724,7 @@
 
         mPortBuffers[portIndex].push(info);
 
-        CODEC_LOGV("allocated buffer %p on %s port", buffer,
+        CODEC_LOGV("allocated buffer %u on %s port", buffer,
              portIndex == kPortIndexInput ? "input" : "output");
     }
 
@@ -1745,7 +1746,7 @@
                 if (mSkipCutBuffer != NULL) {
                     size_t prevbuffersize = mSkipCutBuffer->size();
                     if (prevbuffersize != 0) {
-                        ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbuffersize);
+                        ALOGW("Replacing SkipCutBuffer holding %zu bytes", prevbuffersize);
                     }
                 }
                 mSkipCutBuffer = new SkipCutBuffer(delay * frameSize, padding * frameSize);
@@ -1783,35 +1784,6 @@
     return OK;
 }
 
-status_t OMXCodec::applyRotation() {
-    sp<MetaData> meta = mSource->getFormat();
-
-    int32_t rotationDegrees;
-    if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
-        rotationDegrees = 0;
-    }
-
-    uint32_t transform;
-    switch (rotationDegrees) {
-        case 0: transform = 0; break;
-        case 90: transform = HAL_TRANSFORM_ROT_90; break;
-        case 180: transform = HAL_TRANSFORM_ROT_180; break;
-        case 270: transform = HAL_TRANSFORM_ROT_270; break;
-        default: transform = 0; break;
-    }
-
-    status_t err = OK;
-
-    if (transform) {
-        err = native_window_set_buffers_transform(
-                mNativeWindow.get(), transform);
-        ALOGE("native_window_set_buffers_transform failed: %s (%d)",
-                strerror(-err), -err);
-    }
-
-    return err;
-}
-
 status_t OMXCodec::allocateOutputBuffersFromNativeWindow() {
     // Get the number of buffers needed.
     OMX_PARAM_PORTDEFINITIONTYPE def;
@@ -1825,21 +1797,11 @@
         return err;
     }
 
-    err = native_window_set_buffers_geometry(
-            mNativeWindow.get(),
-            def.format.video.nFrameWidth,
-            def.format.video.nFrameHeight,
-            def.format.video.eColorFormat);
+    sp<MetaData> meta = mSource->getFormat();
 
-    if (err != 0) {
-        ALOGE("native_window_set_buffers_geometry failed: %s (%d)",
-                strerror(-err), -err);
-        return err;
-    }
-
-    err = applyRotation();
-    if (err != OK) {
-        return err;
+    int32_t rotationDegrees;
+    if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
+        rotationDegrees = 0;
     }
 
     // Set up the native window.
@@ -1850,34 +1812,19 @@
         // XXX: Currently this error is logged, but not fatal.
         usage = 0;
     }
+
     if (mFlags & kEnableGrallocUsageProtected) {
         usage |= GRALLOC_USAGE_PROTECTED;
     }
 
-    // Make sure to check whether either Stagefright or the video decoder
-    // requested protected buffers.
-    if (usage & GRALLOC_USAGE_PROTECTED) {
-        // Verify that the ANativeWindow sends images directly to
-        // SurfaceFlinger.
-        int queuesToNativeWindow = 0;
-        err = mNativeWindow->query(
-                mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
-                &queuesToNativeWindow);
-        if (err != 0) {
-            ALOGE("error authenticating native window: %d", err);
-            return err;
-        }
-        if (queuesToNativeWindow != 1) {
-            ALOGE("native window could not be authenticated");
-            return PERMISSION_DENIED;
-        }
-    }
-
-    ALOGV("native_window_set_usage usage=0x%lx", usage);
-    err = native_window_set_usage(
-            mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+    err = setNativeWindowSizeFormatAndUsage(
+            mNativeWindow.get(),
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            def.format.video.eColorFormat,
+            rotationDegrees,
+            usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
     if (err != 0) {
-        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
         return err;
     }
 
@@ -2044,150 +1991,6 @@
     return bufInfo;
 }
 
-status_t OMXCodec::pushBlankBuffersToNativeWindow() {
-    status_t err = NO_ERROR;
-    ANativeWindowBuffer* anb = NULL;
-    int numBufs = 0;
-    int minUndequeuedBufs = 0;
-
-    // We need to reconnect to the ANativeWindow as a CPU client to ensure that
-    // no frames get dropped by SurfaceFlinger assuming that these are video
-    // frames.
-    err = native_window_api_disconnect(mNativeWindow.get(),
-            NATIVE_WINDOW_API_MEDIA);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
-                strerror(-err), -err);
-        return err;
-    }
-
-    err = native_window_api_connect(mNativeWindow.get(),
-            NATIVE_WINDOW_API_CPU);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
-                strerror(-err), -err);
-        return err;
-    }
-
-    err = native_window_set_buffers_geometry(mNativeWindow.get(), 1, 1,
-            HAL_PIXEL_FORMAT_RGBX_8888);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: set_buffers_geometry failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    err = native_window_set_usage(mNativeWindow.get(),
-            GRALLOC_USAGE_SW_WRITE_OFTEN);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: set_usage failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    err = native_window_set_scaling_mode(mNativeWindow.get(),
-            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-    if (err != OK) {
-        ALOGE("error pushing blank frames: set_scaling_mode failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    err = mNativeWindow->query(mNativeWindow.get(),
-            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
-                "failed: %s (%d)", strerror(-err), -err);
-        goto error;
-    }
-
-    numBufs = minUndequeuedBufs + 1;
-    err = native_window_set_buffer_count(mNativeWindow.get(), numBufs);
-    if (err != NO_ERROR) {
-        ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)",
-                strerror(-err), -err);
-        goto error;
-    }
-
-    // We  push numBufs + 1 buffers to ensure that we've drawn into the same
-    // buffer twice.  This should guarantee that the buffer has been displayed
-    // on the screen and then been replaced, so an previous video frames are
-    // guaranteed NOT to be currently displayed.
-    for (int i = 0; i < numBufs + 1; i++) {
-        err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
-
-        // Fill the buffer with the a 1x1 checkerboard pattern ;)
-        uint32_t* img = NULL;
-        err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: lock failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        *img = 0;
-
-        err = buf->unlock();
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: unlock failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        err = mNativeWindow->queueBuffer(mNativeWindow.get(),
-                buf->getNativeBuffer(), -1);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)",
-                    strerror(-err), -err);
-            goto error;
-        }
-
-        anb = NULL;
-    }
-
-error:
-
-    if (err != NO_ERROR) {
-        // Clean up after an error.
-        if (anb != NULL) {
-            mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1);
-        }
-
-        native_window_api_disconnect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_CPU);
-        native_window_api_connect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_MEDIA);
-
-        return err;
-    } else {
-        // Clean up after success.
-        err = native_window_api_disconnect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_CPU);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)",
-                    strerror(-err), -err);
-            return err;
-        }
-
-        err = native_window_api_connect(mNativeWindow.get(),
-                NATIVE_WINDOW_API_MEDIA);
-        if (err != NO_ERROR) {
-            ALOGE("error pushing blank frames: api_connect failed: %s (%d)",
-                    strerror(-err), -err);
-            return err;
-        }
-
-        return NO_ERROR;
-    }
-}
-
 int64_t OMXCodec::getDecodingTimeUs() {
     CHECK(mIsEncoder && mIsVideo);
 
@@ -2708,7 +2511,7 @@
 
         default:
         {
-            CODEC_LOGV("CMD_COMPLETE(%d, %ld)", cmd, data);
+            CODEC_LOGV("CMD_COMPLETE(%d, %u)", cmd, data);
             break;
         }
     }
@@ -2734,7 +2537,7 @@
                 if (countBuffersWeOwn(mPortBuffers[kPortIndexInput]) !=
                     mPortBuffers[kPortIndexInput].size()) {
                     ALOGE("Codec did not return all input buffers "
-                          "(received %d / %d)",
+                          "(received %zu / %zu)",
                             countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
                             mPortBuffers[kPortIndexInput].size());
                     TRESPASS();
@@ -2743,7 +2546,7 @@
                 if (countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) !=
                     mPortBuffers[kPortIndexOutput].size()) {
                     ALOGE("Codec did not return all output buffers "
-                          "(received %d / %d)",
+                          "(received %zu / %zu)",
                             countBuffersWeOwn(mPortBuffers[kPortIndexOutput]),
                             mPortBuffers[kPortIndexOutput].size());
                     TRESPASS();
@@ -2769,7 +2572,7 @@
                     // them has made it to the display.  This allows the OMX
                     // component teardown to zero out any protected buffers
                     // without the risk of scanning out one of those buffers.
-                    pushBlankBuffersToNativeWindow();
+                    pushBlankBuffersToNativeWindow(mNativeWindow.get());
                 }
 
                 setState(IDLE_TO_LOADED);
@@ -2847,7 +2650,7 @@
         CHECK(info->mStatus == OWNED_BY_US
                 || info->mStatus == OWNED_BY_NATIVE_WINDOW);
 
-        CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex);
+        CODEC_LOGV("freeing buffer %u on port %u", info->mBuffer, portIndex);
 
         status_t err = freeBuffer(portIndex, i);
 
@@ -2894,7 +2697,7 @@
 }
 
 void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
-    CODEC_LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
+    CODEC_LOGV("PORT_SETTINGS_CHANGED(%u)", portIndex);
 
     CHECK(mState == EXECUTING || mState == EXECUTING_TO_IDLE);
     CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput);
@@ -2921,7 +2724,7 @@
     CHECK(mState == EXECUTING || mState == RECONFIGURING
             || mState == EXECUTING_TO_IDLE);
 
-    CODEC_LOGV("flushPortAsync(%ld): we own %d out of %d buffers already.",
+    CODEC_LOGV("flushPortAsync(%u): we own %zu out of %zu buffers already.",
          portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
          mPortBuffers[portIndex].size());
 
@@ -2950,7 +2753,7 @@
     CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED);
     mPortStatus[portIndex] = DISABLING;
 
-    CODEC_LOGV("sending OMX_CommandPortDisable(%ld)", portIndex);
+    CODEC_LOGV("sending OMX_CommandPortDisable(%u)", portIndex);
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
     CHECK_EQ(err, (status_t)OK);
@@ -2964,7 +2767,7 @@
     CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLED);
     mPortStatus[portIndex] = ENABLING;
 
-    CODEC_LOGV("sending OMX_CommandPortEnable(%ld)", portIndex);
+    CODEC_LOGV("sending OMX_CommandPortEnable(%u)", portIndex);
     return mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
 }
 
@@ -3037,7 +2840,7 @@
 
         if (info->mData == ptr) {
             CODEC_LOGV(
-                    "input buffer data ptr = %p, buffer_id = %p",
+                    "input buffer data ptr = %p, buffer_id = %u",
                     ptr,
                     info->mBuffer);
 
@@ -3147,7 +2950,7 @@
                 if (srcBuffer->meta_data()->findInt64(
                             kKeyTargetTime, &targetTimeUs)
                         && targetTimeUs >= 0) {
-                    CODEC_LOGV("targetTimeUs = %lld us", targetTimeUs);
+                    CODEC_LOGV("targetTimeUs = %lld us", (long long)targetTimeUs);
                     mTargetTimeUs = targetTimeUs;
                 } else {
                     mTargetTimeUs = -1;
@@ -3181,7 +2984,7 @@
             if (offset == 0) {
                 CODEC_LOGE(
                      "Codec's input buffers are too small to accomodate "
-                     "buffer read from source (info->mSize = %d, srcLength = %d)",
+                     "buffer read from source (info->mSize = %zu, srcLength = %zu)",
                      info->mSize, srcBuffer->range_length());
 
                 srcBuffer->release();
@@ -3287,10 +3090,10 @@
         info = findEmptyInputBuffer();
     }
 
-    CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d), "
+    CODEC_LOGV("Calling emptyBuffer on buffer %u (length %zu), "
                "timestamp %lld us (%.2f secs)",
                info->mBuffer, offset,
-               timestampUs, timestampUs / 1E6);
+               (long long)timestampUs, timestampUs / 1E6);
 
     err = mOMX->emptyBuffer(
             mNode, info->mBuffer, 0, offset,
@@ -3315,7 +3118,7 @@
         return;
     }
 
-    CODEC_LOGV("Calling fillBuffer on buffer %p", info->mBuffer);
+    CODEC_LOGV("Calling fillBuffer on buffer %u", info->mBuffer);
     status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
 
     if (err != OK) {
@@ -3372,7 +3175,7 @@
     }
     status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs);
     if (err != OK) {
-        CODEC_LOGE("Timed out waiting for output buffers: %d/%d",
+        CODEC_LOGE("Timed out waiting for output buffers: %zu/%zu",
             countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
             countBuffersWeOwn(mPortBuffers[kPortIndexOutput]));
     }
@@ -3627,7 +3430,7 @@
 
 void OMXCodec::setImageOutputFormat(
         OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
-    CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height);
+    CODEC_LOGV("setImageOutputFormat(%u, %u)", width, height);
 
 #if 0
     OMX_INDEXTYPE index;
@@ -4281,14 +4084,14 @@
                 if ((OMX_U32)numChannels != params.nChannels) {
                     ALOGV("Codec outputs a different number of channels than "
                          "the input stream contains (contains %d channels, "
-                         "codec outputs %ld channels).",
+                         "codec outputs %u channels).",
                          numChannels, params.nChannels);
                 }
 
                 if (sampleRate != (int32_t)params.nSamplingRate) {
                     ALOGV("Codec outputs at different sampling rate than "
                          "what the input stream contains (contains data at "
-                         "%d Hz, codec outputs %lu Hz)",
+                         "%d Hz, codec outputs %u Hz)",
                          sampleRate, params.nSamplingRate);
                 }
 
@@ -4390,8 +4193,7 @@
                             mNode, OMX_IndexConfigCommonOutputCrop,
                             &rect, sizeof(rect));
 
-                CODEC_LOGI(
-                        "video dimensions are %ld x %ld",
+                CODEC_LOGI("video dimensions are %u x %u",
                         video_def->nFrameWidth, video_def->nFrameHeight);
 
                 if (err == OK) {
@@ -4409,8 +4211,7 @@
                             rect.nLeft + rect.nWidth - 1,
                             rect.nTop + rect.nHeight - 1);
 
-                    CODEC_LOGI(
-                            "Crop rect is %ld x %ld @ (%ld, %ld)",
+                    CODEC_LOGI("Crop rect is %u x %u @ (%d, %d)",
                             rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop);
                 } else {
                     mOutputFormat->setRect(
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 6e32494..4297549 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -250,7 +250,7 @@
         if (!memcmp(signature, "OggS", 4)) {
             if (*pageOffset > startOffset) {
                 ALOGV("skipped %lld bytes of junk to reach next frame",
-                     *pageOffset - startOffset);
+                     (long long)(*pageOffset - startOffset));
             }
 
             return OK;
@@ -277,7 +277,7 @@
             prevGuess = 0;
         }
 
-        ALOGV("backing up %lld bytes", pageOffset - prevGuess);
+        ALOGV("backing up %lld bytes", (long long)(pageOffset - prevGuess));
 
         status_t err = findNextPage(prevGuess, &prevPageOffset);
         if (err != OK) {
@@ -295,7 +295,7 @@
     }
 
     ALOGV("prevPageOffset at %lld, pageOffset at %lld",
-         prevPageOffset, pageOffset);
+            (long long)prevPageOffset, (long long)pageOffset);
 
     for (;;) {
         Page prevPage;
@@ -320,7 +320,7 @@
 
         off64_t pos = timeUs * approxBitrate() / 8000000ll;
 
-        ALOGV("seeking to offset %lld", pos);
+        ALOGV("seeking to offset %lld", (long long)pos);
         return seekToOffset(pos);
     }
 
@@ -348,7 +348,7 @@
     const TOCEntry &entry = mTableOfContents.itemAt(left);
 
     ALOGV("seeking to entry %zu / %zu at offset %lld",
-         left, mTableOfContents.size(), entry.mPageOffset);
+         left, mTableOfContents.size(), (long long)entry.mPageOffset);
 
     return seekToOffset(entry.mPageOffset);
 }
@@ -391,8 +391,8 @@
     ssize_t n;
     if ((n = mSource->readAt(offset, header, sizeof(header)))
             < (ssize_t)sizeof(header)) {
-        ALOGV("failed to read %zu bytes at offset 0x%016llx, got %zd bytes",
-             sizeof(header), offset, n);
+        ALOGV("failed to read %zu bytes at offset %#016llx, got %zd bytes",
+                sizeof(header), (long long)offset, n);
 
         if (n < 0) {
             return n;
@@ -505,8 +505,8 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                ALOGV("failed to read %zu bytes at 0x%016llx, got %zd bytes",
-                     packetSize, dataOffset, n);
+                ALOGV("failed to read %zu bytes at %#016llx, got %zd bytes",
+                        packetSize, (long long)dataOffset, n);
                 return ERROR_IO;
             }
 
@@ -753,7 +753,9 @@
     oggpack_buffer bits;
     oggpack_readinit(&bits, &ref);
 
-    CHECK_EQ(oggpack_read(&bits, 8), type);
+    if (oggpack_read(&bits, 8) != type) {
+        return ERROR_MALFORMED;
+    }
     for (size_t i = 0; i < 6; ++i) {
         oggpack_read(&bits, 8);  // skip 'vorbis'
     }
@@ -761,7 +763,9 @@
     switch (type) {
         case 1:
         {
-            CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
+            if (0 != _vorbis_unpack_info(&mVi, &bits)) {
+                return ERROR_MALFORMED;
+            }
 
             mMeta->setData(kKeyVorbisInfo, 0, data, size);
             mMeta->setInt32(kKeySampleRate, mVi.rate);
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index bdd6d56..7f98485 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -230,8 +230,13 @@
         return ERROR_MALFORMED;
     }
 
+    if (SIZE_MAX / sizeof(SampleToChunkEntry) <= (size_t)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/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 101fc8a..e9566f2 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -47,10 +47,7 @@
 
 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
     ALOGV("~StagefrightMetadataRetriever()");
-
-    delete mAlbumArt;
-    mAlbumArt = NULL;
-
+    clearMetadata();
     mClient.disconnect();
 }
 
@@ -60,11 +57,7 @@
         const KeyedVector<String8, String8> *headers) {
     ALOGV("setDataSource(%s)", uri);
 
-    mParsedMetaData = false;
-    mMetaData.clear();
-    delete mAlbumArt;
-    mAlbumArt = NULL;
-
+    clearMetadata();
     mSource = DataSource::CreateFromURI(httpService, uri, headers);
 
     if (mSource == NULL) {
@@ -92,11 +85,7 @@
 
     ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
 
-    mParsedMetaData = false;
-    mMetaData.clear();
-    delete mAlbumArt;
-    mAlbumArt = NULL;
-
+    clearMetadata();
     mSource = new FileSource(fd, offset, length);
 
     status_t err;
@@ -117,6 +106,23 @@
     return OK;
 }
 
+status_t StagefrightMetadataRetriever::setDataSource(
+        const sp<DataSource>& source) {
+    ALOGV("setDataSource(DataSource)");
+
+    clearMetadata();
+    mSource = source;
+    mExtractor = MediaExtractor::Create(mSource);
+
+    if (mExtractor == NULL) {
+        ALOGE("Failed to instantiate a MediaExtractor.");
+        mSource.clear();
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
 static bool isYUV420PlanarSupported(
             OMXClient *client,
             const sp<MetaData> &trackMeta) {
@@ -519,6 +525,12 @@
 
     mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
 
+    float captureFps;
+    if (meta->findFloat(kKeyCaptureFramerate, &captureFps)) {
+        sprintf(tmp, "%f", captureFps);
+        mMetaData.add(METADATA_KEY_CAPTURE_FRAMERATE, String8(tmp));
+    }
+
     bool hasAudio = false;
     bool hasVideo = false;
     int32_t videoWidth = -1;
@@ -629,4 +641,11 @@
     }
 }
 
+void StagefrightMetadataRetriever::clearMetadata() {
+    mParsedMetaData = false;
+    mMetaData.clear();
+    delete mAlbumArt;
+    mAlbumArt = NULL;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
new file mode 100644
index 0000000..6b62e43
--- /dev/null
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceUtils"
+#include <utils/Log.h>
+
+#include <media/stagefright/SurfaceUtils.h>
+
+#include <gui/Surface.h>
+
+namespace android {
+
+status_t setNativeWindowSizeFormatAndUsage(
+        ANativeWindow *nativeWindow /* nonnull */,
+        int width, int height, int format, int rotation, int usage) {
+    status_t err = native_window_set_buffers_dimensions(nativeWindow, width, height);
+    if (err != NO_ERROR) {
+        ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    err = native_window_set_buffers_format(nativeWindow, format);
+    if (err != NO_ERROR) {
+        ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    int transform = 0;
+    if ((rotation % 90) == 0) {
+        switch ((rotation / 90) & 3) {
+            case 1:  transform = HAL_TRANSFORM_ROT_90;  break;
+            case 2:  transform = HAL_TRANSFORM_ROT_180; break;
+            case 3:  transform = HAL_TRANSFORM_ROT_270; break;
+            default: transform = 0;                     break;
+        }
+    }
+
+    err = native_window_set_buffers_transform(nativeWindow, transform);
+    if (err != NO_ERROR) {
+        ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    // Make sure to check whether either Stagefright or the video decoder
+    // requested protected buffers.
+    if (usage & GRALLOC_USAGE_PROTECTED) {
+        // Verify that the ANativeWindow sends images directly to
+        // SurfaceFlinger.
+        int queuesToNativeWindow = 0;
+        err = nativeWindow->query(
+                nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow);
+        if (err != NO_ERROR) {
+            ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+        if (queuesToNativeWindow != 1) {
+            ALOGE("native window could not be authenticated");
+            return PERMISSION_DENIED;
+        }
+    }
+
+    int consumerUsage = 0;
+    err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage);
+    if (err != NO_ERROR) {
+        ALOGW("failed to get consumer usage bits. ignoring");
+        err = NO_ERROR;
+    }
+
+    int finalUsage = usage | consumerUsage;
+    ALOGV("gralloc usage: %#x(producer) + %#x(consumer) = %#x", usage, consumerUsage, finalUsage);
+    err = native_window_set_usage(nativeWindow, finalUsage);
+    if (err != NO_ERROR) {
+        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    err = native_window_set_scaling_mode(
+            nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+    if (err != NO_ERROR) {
+        ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x",
+            nativeWindow, width, height, format, rotation, finalUsage);
+    return NO_ERROR;
+}
+
+status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */) {
+    status_t err = NO_ERROR;
+    ANativeWindowBuffer* anb = NULL;
+    int numBufs = 0;
+    int minUndequeuedBufs = 0;
+
+    // We need to reconnect to the ANativeWindow as a CPU client to ensure that
+    // no frames get dropped by SurfaceFlinger assuming that these are video
+    // frames.
+    err = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+    if (err != NO_ERROR) {
+        ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    err = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_CPU);
+    if (err != NO_ERROR) {
+        ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err);
+        (void)native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+        return err;
+    }
+
+    err = setNativeWindowSizeFormatAndUsage(
+            nativeWindow, 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN);
+    if (err != NO_ERROR) {
+        goto error;
+    }
+
+    static_cast<Surface*>(nativeWindow)->getIGraphicBufferProducer()->allowAllocation(true);
+
+    err = nativeWindow->query(nativeWindow,
+            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
+    if (err != NO_ERROR) {
+        ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
+                "failed: %s (%d)", strerror(-err), -err);
+        goto error;
+    }
+
+    numBufs = minUndequeuedBufs + 1;
+    err = native_window_set_buffer_count(nativeWindow, numBufs);
+    if (err != NO_ERROR) {
+        ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", strerror(-err), -err);
+        goto error;
+    }
+
+    // We push numBufs + 1 buffers to ensure that we've drawn into the same
+    // buffer twice.  This should guarantee that the buffer has been displayed
+    // on the screen and then been replaced, so an previous video frames are
+    // guaranteed NOT to be currently displayed.
+    for (int i = 0; i < numBufs + 1; i++) {
+        err = native_window_dequeue_buffer_and_wait(nativeWindow, &anb);
+        if (err != NO_ERROR) {
+            ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
+                    strerror(-err), -err);
+            break;
+        }
+
+        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+
+        // Fill the buffer with the a 1x1 checkerboard pattern ;)
+        uint32_t *img = NULL;
+        err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+        if (err != NO_ERROR) {
+            ALOGE("error pushing blank frames: lock failed: %s (%d)", strerror(-err), -err);
+            break;
+        }
+
+        *img = 0;
+
+        err = buf->unlock();
+        if (err != NO_ERROR) {
+            ALOGE("error pushing blank frames: unlock failed: %s (%d)", strerror(-err), -err);
+            break;
+        }
+
+        err = nativeWindow->queueBuffer(nativeWindow, buf->getNativeBuffer(), -1);
+        if (err != NO_ERROR) {
+            ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err);
+            break;
+        }
+
+        anb = NULL;
+    }
+
+error:
+
+    if (anb != NULL) {
+        nativeWindow->cancelBuffer(nativeWindow, anb, -1);
+        anb = NULL;
+    }
+
+    // Clean up after success or error.
+    status_t err2 = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_CPU);
+    if (err2 != NO_ERROR) {
+        ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err2), -err2);
+        if (err == NO_ERROR) {
+            err = err2;
+        }
+    }
+
+    err2 = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
+    if (err2 != NO_ERROR) {
+        ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err);
+        if (err == NO_ERROR) {
+            err = err2;
+        }
+    }
+
+    return err;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 8506e37..413628d 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -166,11 +166,26 @@
         msg->setInt32("max-input-size", maxInputSize);
     }
 
+    int32_t maxWidth;
+    if (meta->findInt32(kKeyMaxWidth, &maxWidth)) {
+        msg->setInt32("max-width", maxWidth);
+    }
+
+    int32_t maxHeight;
+    if (meta->findInt32(kKeyMaxHeight, &maxHeight)) {
+        msg->setInt32("max-height", maxHeight);
+    }
+
     int32_t rotationDegrees;
     if (meta->findInt32(kKeyRotation, &rotationDegrees)) {
         msg->setInt32("rotation-degrees", rotationDegrees);
     }
 
+    int32_t fps;
+    if (meta->findInt32(kKeyFrameRate, &fps)) {
+        msg->setInt32("frame-rate", fps);
+    }
+
     uint32_t type;
     const void *data;
     size_t size;
@@ -568,6 +583,21 @@
         meta->setInt32(kKeyMaxInputSize, maxInputSize);
     }
 
+    int32_t maxWidth;
+    if (msg->findInt32("max-width", &maxWidth)) {
+        meta->setInt32(kKeyMaxWidth, maxWidth);
+    }
+
+    int32_t maxHeight;
+    if (msg->findInt32("max-height", &maxHeight)) {
+        meta->setInt32(kKeyMaxHeight, maxHeight);
+    }
+
+    int32_t fps;
+    if (msg->findInt32("frame-rate", &fps)) {
+        meta->setInt32(kKeyFrameRate, fps);
+    }
+
     // reassemble the csd data into its original form
     sp<ABuffer> csd0;
     if (msg->findBuffer("csd-0", &csd0)) {
@@ -832,14 +862,32 @@
     }
 }
 
-int64_t HLSTime::getSegmentTimeUs(bool midpoint) const {
+int64_t HLSTime::getSegmentTimeUs() const {
     int64_t segmentStartTimeUs = -1ll;
     if (mMeta != NULL) {
         CHECK(mMeta->findInt64("segmentStartTimeUs", &segmentStartTimeUs));
-        if (midpoint) {
+
+        int64_t segmentFirstTimeUs;
+        if (mMeta->findInt64("segmentFirstTimeUs", &segmentFirstTimeUs)) {
+            segmentStartTimeUs += mTimeUs - segmentFirstTimeUs;
+        }
+
+        // adjust segment time by playlist age (for live streaming)
+        int64_t playlistTimeUs;
+        if (mMeta->findInt64("playlistTimeUs", &playlistTimeUs)) {
+            int64_t playlistAgeUs = ALooper::GetNowUs() - playlistTimeUs;
+
             int64_t durationUs;
             CHECK(mMeta->findInt64("segmentDurationUs", &durationUs));
-            segmentStartTimeUs += durationUs / 2;
+
+            // round to nearest whole segment
+            playlistAgeUs = (playlistAgeUs + durationUs / 2)
+                    / durationUs * durationUs;
+
+            segmentStartTimeUs -= playlistAgeUs;
+            if (segmentStartTimeUs < 0) {
+                segmentStartTimeUs = 0;
+            }
         }
     }
     return segmentStartTimeUs;
@@ -853,5 +901,39 @@
             || (t0.mSeq == t1.mSeq && t0.mTimeUs < t1.mTimeUs);
 }
 
+void writeToAMessage(sp<AMessage> msg, const AudioPlaybackRate &rate) {
+    msg->setFloat("speed", rate.mSpeed);
+    msg->setFloat("pitch", rate.mPitch);
+    msg->setInt32("audio-fallback-mode", rate.mFallbackMode);
+    msg->setInt32("audio-stretch-mode", rate.mStretchMode);
+}
+
+void readFromAMessage(const sp<AMessage> &msg, AudioPlaybackRate *rate /* nonnull */) {
+    *rate = AUDIO_PLAYBACK_RATE_DEFAULT;
+    CHECK(msg->findFloat("speed", &rate->mSpeed));
+    CHECK(msg->findFloat("pitch", &rate->mPitch));
+    CHECK(msg->findInt32("audio-fallback-mode", (int32_t *)&rate->mFallbackMode));
+    CHECK(msg->findInt32("audio-stretch-mode", (int32_t *)&rate->mStretchMode));
+}
+
+void writeToAMessage(sp<AMessage> msg, const AVSyncSettings &sync, float videoFpsHint) {
+    msg->setInt32("sync-source", sync.mSource);
+    msg->setInt32("audio-adjust-mode", sync.mAudioAdjustMode);
+    msg->setFloat("tolerance", sync.mTolerance);
+    msg->setFloat("video-fps", videoFpsHint);
+}
+
+void readFromAMessage(
+        const sp<AMessage> &msg,
+        AVSyncSettings *sync /* nonnull */,
+        float *videoFps /* nonnull */) {
+    AVSyncSettings settings;
+    CHECK(msg->findInt32("sync-source", (int32_t *)&settings.mSource));
+    CHECK(msg->findInt32("audio-adjust-mode", (int32_t *)&settings.mAudioAdjustMode));
+    CHECK(msg->findFloat("tolerance", &settings.mTolerance));
+    CHECK(msg->findFloat("video-fps", videoFps));
+    *sync = settings;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp
index e988f6d..8a0fcac 100644
--- a/media/libstagefright/VBRISeeker.cpp
+++ b/media/libstagefright/VBRISeeker.cpp
@@ -122,7 +122,7 @@
 
         seeker->mSegments.push(numBytes);
 
-        ALOGV("entry #%zu: %u offset 0x%016llx", i, numBytes, offset);
+        ALOGV("entry #%zu: %u offset %#016llx", i, numBytes, (long long)offset);
         offset += numBytes;
     }
 
@@ -163,7 +163,7 @@
         *pos += mSegments.itemAt(segmentIndex++);
     }
 
-    ALOGV("getOffsetForTime %" PRId64 " us => 0x%016llx", *timeUs, *pos);
+    ALOGV("getOffsetForTime %lld us => 0x%016llx", (long long)*timeUs, (long long)*pos);
 
     *timeUs = nowUs;
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 10937ec..965c55e 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -75,7 +75,7 @@
 
 SoftAAC2::~SoftAAC2() {
     aacDecoder_Close(mAACDecoder);
-    delete mOutputDelayRingBuffer;
+    delete[] mOutputDelayRingBuffer;
 }
 
 void SoftAAC2::initPorts() {
diff --git a/media/libstagefright/codecs/avcdec/Android.mk b/media/libstagefright/codecs/avcdec/Android.mk
new file mode 100644
index 0000000..902ab57
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/Android.mk
@@ -0,0 +1,27 @@
+#ifeq ($(if $(wildcard external/libh264),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE            := libstagefright_soft_avcdec
+LOCAL_MODULE_TAGS       := optional
+
+LOCAL_STATIC_LIBRARIES  := libavcdec
+LOCAL_SRC_FILES         := SoftAVCDec.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libavc/decoder
+LOCAL_C_INCLUDES += $(TOP)/external/libavc/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES  := libstagefright
+LOCAL_SHARED_LIBRARIES  += libstagefright_omx
+LOCAL_SHARED_LIBRARIES  += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES  += libutils
+LOCAL_SHARED_LIBRARIES  += liblog
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
+
+#endif
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
new file mode 100644
index 0000000..08e956a
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftAVCDec"
+#include <utils/Log.h>
+
+#include "ih264_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+#include "ithread.h"
+#include "ih264d.h"
+#include "SoftAVCDec.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <OMX_VideoExt.h>
+
+namespace android {
+
+#define PRINT_TIME  ALOGV
+
+#define componentName                   "video_decoder.avc"
+#define codingType                      OMX_VIDEO_CodingAVC
+#define CODEC_MIME_TYPE                 MEDIA_MIMETYPE_VIDEO_AVC
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function              ih264d_api_function
+#define ivdext_init_ip_t                ih264d_init_ip_t
+#define ivdext_init_op_t                ih264d_init_op_t
+#define ivdext_fill_mem_rec_ip_t        ih264d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t        ih264d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t   ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   ih264d_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+
+static const CodecProfileLevel kProfileLevels[] = {
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
+
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel1  },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel1b },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel11 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel12 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel13 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel2  },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel21 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel22 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel3  },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel31 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel32 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel4  },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel41 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel42 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel5  },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel51 },
+    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel52 },
+
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel1  },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel1b },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel11 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel12 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel13 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel2  },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel21 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel22 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel3  },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel31 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel32 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel4  },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel41 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel42 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel5  },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel51 },
+    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel52 },
+};
+
+SoftAVC::SoftAVC(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SoftVideoDecoderOMXComponent(
+            name, componentName, codingType,
+            kProfileLevels, ARRAY_SIZE(kProfileLevels),
+            320 /* width */, 240 /* height */, callbacks,
+            appData, component),
+      mMemRecords(NULL),
+      mFlushOutBuffer(NULL),
+      mOmxColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mIvColorFormat(IV_YUV_420P),
+      mNewWidth(mWidth),
+      mNewHeight(mHeight),
+      mChangingResolution(false) {
+    initPorts(
+            kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
+
+    GETTIME(&mTimeStart, NULL);
+
+    // If input dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAVC::~SoftAVC() {
+    CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+static size_t GetCPUCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGD("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+void SoftAVC::logVersion() {
+    ivd_ctl_getversioninfo_ip_t s_ctl_ip;
+    ivd_ctl_getversioninfo_op_t s_ctl_op;
+    UWORD8 au1_buf[512];
+    IV_API_CALL_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+    s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+    s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+    s_ctl_ip.pv_version_buffer = au1_buf;
+    s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);
+
+    status =
+        ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in getting version number: 0x%x",
+                s_ctl_op.u4_error_code);
+    } else {
+        ALOGV("Ittiam decoder version number: %s",
+                (char *)s_ctl_ip.pv_version_buffer);
+    }
+    return;
+}
+
+status_t SoftAVC::setParams(size_t stride) {
+    ivd_ctl_set_config_ip_t s_ctl_ip;
+    ivd_ctl_set_config_op_t s_ctl_op;
+    IV_API_CALL_STATUS_T status;
+    s_ctl_ip.u4_disp_wd = (UWORD32)stride;
+    s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+
+    s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+    s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+
+    ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
+    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in setting the run-time parameters: 0x%x",
+                s_ctl_op.u4_error_code);
+
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+status_t SoftAVC::resetPlugin() {
+    mIsInFlush = false;
+    mReceivedEOS = false;
+    memset(mTimeStamps, 0, sizeof(mTimeStamps));
+    memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+    /* Initialize both start and end times */
+    gettimeofday(&mTimeStart, NULL);
+    gettimeofday(&mTimeEnd, NULL);
+
+    return OK;
+}
+
+status_t SoftAVC::resetDecoder() {
+    ivd_ctl_reset_ip_t s_ctl_ip;
+    ivd_ctl_reset_op_t s_ctl_op;
+    IV_API_CALL_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+
+    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    /* Set the run-time (dynamic) parameters */
+    setParams(outputBufferWidth());
+
+    /* Set number of cores/threads to be used by the codec */
+    setNumCores();
+
+    return OK;
+}
+
+status_t SoftAVC::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_cores_ip;
+    ivdext_ctl_set_num_cores_op_t s_set_cores_op;
+    IV_API_CALL_STATUS_T status;
+    s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES);
+    s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    status = ivdec_api_function(
+            mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in setting number of cores: 0x%x",
+                s_set_cores_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+status_t SoftAVC::setFlushMode() {
+    IV_API_CALL_STATUS_T status;
+    ivd_ctl_flush_ip_t s_video_flush_ip;
+    ivd_ctl_flush_op_t s_video_flush_op;
+
+    s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+    s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+    s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+
+    /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+    status = ivdec_api_function(
+            mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
+                s_video_flush_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    mIsInFlush = true;
+    return OK;
+}
+
+status_t SoftAVC::initDecoder() {
+    IV_API_CALL_STATUS_T status;
+
+    UWORD32 u4_num_reorder_frames;
+    UWORD32 u4_num_ref_frames;
+    UWORD32 u4_share_disp_buf;
+    WORD32 i4_level;
+
+    mNumCores = GetCPUCoreCount();
+
+    /* Initialize number of ref and reorder modes (for H264) */
+    u4_num_reorder_frames = 16;
+    u4_num_ref_frames = 16;
+    u4_share_disp_buf = 0;
+
+    uint32_t displayStride = outputBufferWidth();
+    uint32_t displayHeight = outputBufferHeight();
+    uint32_t displaySizeY = displayStride * displayHeight;
+
+    if (displaySizeY > (1920 * 1088)) {
+        i4_level = 50;
+    } else if (displaySizeY > (1280 * 720)) {
+        i4_level = 40;
+    } else if (displaySizeY > (720 * 576)) {
+        i4_level = 31;
+    } else if (displaySizeY > (624 * 320)) {
+        i4_level = 30;
+    } else if (displaySizeY > (352 * 288)) {
+        i4_level = 21;
+    } else {
+        i4_level = 20;
+    }
+
+    {
+        iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+        iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+        s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+        s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+        s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+        ALOGV("Get number of mem records");
+        status = ivdec_api_function(
+                mCodecCtx, (void *)&s_num_mem_rec_ip, (void *)&s_num_mem_rec_op);
+        if (IV_SUCCESS != status) {
+            ALOGE("Error in getting mem records: 0x%x",
+                    s_num_mem_rec_op.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+
+        mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+    }
+
+    mMemRecords = (iv_mem_rec_t *)ivd_aligned_malloc(
+            128, mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (mMemRecords == NULL) {
+        ALOGE("Allocation failure");
+        return NO_MEMORY;
+    }
+
+    memset(mMemRecords, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+
+    {
+        size_t i;
+        ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+        ivdext_fill_mem_rec_op_t s_fill_mem_op;
+        iv_mem_rec_t *ps_mem_rec;
+
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size =
+            sizeof(ivdext_fill_mem_rec_ip_t);
+        s_fill_mem_ip.i4_level = i4_level;
+        s_fill_mem_ip.u4_num_reorder_frames = u4_num_reorder_frames;
+        s_fill_mem_ip.u4_num_ref_frames = u4_num_ref_frames;
+        s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf;
+        s_fill_mem_ip.u4_num_extra_disp_buf = 0;
+        s_fill_mem_ip.e_output_format = mIvColorFormat;
+
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = displayStride;
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = displayHeight;
+        s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size =
+            sizeof(ivdext_fill_mem_rec_op_t);
+
+        ps_mem_rec = mMemRecords;
+        for (i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+        }
+
+        status = ivdec_api_function(
+                mCodecCtx, (void *)&s_fill_mem_ip, (void *)&s_fill_mem_op);
+
+        if (IV_SUCCESS != status) {
+            ALOGE("Error in filling mem records: 0x%x",
+                    s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+        mNumMemRecords =
+            s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled;
+
+        ps_mem_rec = mMemRecords;
+
+        for (i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->pv_base = ivd_aligned_malloc(
+                    ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+            if (ps_mem_rec->pv_base == NULL) {
+                ALOGE("Allocation failure for memory record #%zu of size %u",
+                        i, ps_mem_rec->u4_mem_size);
+                status = IV_FAIL;
+                return NO_MEMORY;
+            }
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Initialize the decoder */
+    {
+        ivdext_init_ip_t s_init_ip;
+        ivdext_init_op_t s_init_op;
+
+        void *dec_fxns = (void *)ivdec_api_function;
+
+        s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+        s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+        s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+        s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = displayStride;
+        s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = displayHeight;
+
+        s_init_ip.i4_level = i4_level;
+        s_init_ip.u4_num_reorder_frames = u4_num_reorder_frames;
+        s_init_ip.u4_num_ref_frames = u4_num_ref_frames;
+        s_init_ip.u4_share_disp_buf = u4_share_disp_buf;
+        s_init_ip.u4_num_extra_disp_buf = 0;
+
+        s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op);
+
+        s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+        s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat;
+
+        mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+        mCodecCtx->pv_fxns = dec_fxns;
+        mCodecCtx->u4_size = sizeof(iv_obj_t);
+
+        status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op);
+        if (status != IV_SUCCESS) {
+            ALOGE("Error in init: 0x%x",
+                    s_init_op.s_ivd_init_op_t.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    /* Reset the plugin state */
+    resetPlugin();
+
+    /* Set the run time (dynamic) parameters */
+    setParams(displayStride);
+
+    /* Set number of cores/threads to be used by the codec */
+    setNumCores();
+
+    /* Get codec version */
+    logVersion();
+
+    /* Allocate internal picture buffer */
+    uint32_t bufferSize = displaySizeY * 3 / 2;
+    mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+    if (NULL == mFlushOutBuffer) {
+        ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
+        return NO_MEMORY;
+    }
+
+    mInitNeeded = false;
+    mFlushNeeded = false;
+    return OK;
+}
+
+status_t SoftAVC::deInitDecoder() {
+    size_t i;
+
+    if (mMemRecords) {
+        iv_mem_rec_t *ps_mem_rec;
+
+        ps_mem_rec = mMemRecords;
+        for (i = 0; i < mNumMemRecords; i++) {
+            if (ps_mem_rec->pv_base) {
+                ivd_aligned_free(ps_mem_rec->pv_base);
+            }
+            ps_mem_rec++;
+        }
+        ivd_aligned_free(mMemRecords);
+        mMemRecords = NULL;
+    }
+
+    if (mFlushOutBuffer) {
+        ivd_aligned_free(mFlushOutBuffer);
+        mFlushOutBuffer = NULL;
+    }
+
+    mInitNeeded = true;
+    mChangingResolution = false;
+
+    return OK;
+}
+
+status_t SoftAVC::reInitDecoder() {
+    status_t ret;
+
+    deInitDecoder();
+
+    ret = initDecoder();
+    if (OK != ret) {
+        ALOGE("Create failure");
+        deInitDecoder();
+        return NO_MEMORY;
+    }
+    return OK;
+}
+
+void SoftAVC::onReset() {
+    SoftVideoDecoderOMXComponent::onReset();
+
+    resetDecoder();
+    resetPlugin();
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
+    const uint32_t oldWidth = mWidth;
+    const uint32_t oldHeight = mHeight;
+    OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params);
+    if (mWidth != oldWidth || mHeight != oldHeight) {
+        reInitDecoder();
+    }
+    return ret;
+}
+
+void SoftAVC::setDecodeArgs(
+        ivd_video_decode_ip_t *ps_dec_ip,
+        ivd_video_decode_op_t *ps_dec_op,
+        OMX_BUFFERHEADERTYPE *inHeader,
+        OMX_BUFFERHEADERTYPE *outHeader,
+        size_t timeStampIx) {
+    size_t sizeY = outputBufferWidth() * outputBufferHeight();
+    size_t sizeUV;
+    uint8_t *pBuf;
+
+    ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+    ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+    ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+
+    /* When in flush and after EOS with zero byte input,
+     * inHeader is set to zero. Hence check for non-null */
+    if (inHeader) {
+        ps_dec_ip->u4_ts = timeStampIx;
+        ps_dec_ip->pv_stream_buffer =
+            inHeader->pBuffer + inHeader->nOffset;
+        ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen;
+    } else {
+        ps_dec_ip->u4_ts = 0;
+        ps_dec_ip->pv_stream_buffer = NULL;
+        ps_dec_ip->u4_num_Bytes = 0;
+    }
+
+    if (outHeader) {
+        pBuf = outHeader->pBuffer;
+    } else {
+        pBuf = mFlushOutBuffer;
+    }
+
+    sizeUV = sizeY / 4;
+    ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+    ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+    ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+    ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf;
+    ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY;
+    ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV;
+    ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
+    return;
+}
+void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
+    /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
+    if (kOutputPortIndex == portIndex) {
+        setFlushMode();
+
+        while (true) {
+            ivd_video_decode_ip_t s_dec_ip;
+            ivd_video_decode_op_t s_dec_op;
+            IV_API_CALL_STATUS_T status;
+            size_t sizeY, sizeUV;
+
+            setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0);
+
+            status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+            if (0 == s_dec_op.u4_output_present) {
+                resetPlugin();
+                break;
+            }
+        }
+    }
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+    UNUSED(portIndex);
+
+    if (mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
+    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
+
+    /* If input EOS is seen and decoder is not in flush mode,
+     * set the decoder in flush mode.
+     * There can be a case where EOS is sent along with last picture data
+     * In that case, only after decoding that input data, decoder has to be
+     * put in flush. This case is handled here  */
+
+    if (mReceivedEOS && !mIsInFlush) {
+        setFlushMode();
+    }
+
+    while (!outQueue.empty()) {
+        BufferInfo *inInfo;
+        OMX_BUFFERHEADERTYPE *inHeader;
+
+        BufferInfo *outInfo;
+        OMX_BUFFERHEADERTYPE *outHeader;
+        size_t timeStampIx;
+
+        inInfo = NULL;
+        inHeader = NULL;
+
+        if (!mIsInFlush) {
+            if (!inQueue.empty()) {
+                inInfo = *inQueue.begin();
+                inHeader = inInfo->mHeader;
+            } else {
+                break;
+            }
+        }
+
+        outInfo = *outQueue.begin();
+        outHeader = outInfo->mHeader;
+        outHeader->nFlags = 0;
+        outHeader->nTimeStamp = 0;
+        outHeader->nOffset = 0;
+
+        if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+            mReceivedEOS = true;
+            if (inHeader->nFilledLen == 0) {
+                inQueue.erase(inQueue.begin());
+                inInfo->mOwnedByUs = false;
+                notifyEmptyBufferDone(inHeader);
+                inHeader = NULL;
+                setFlushMode();
+            }
+        }
+
+        // When there is an init required and the decoder is not in flush mode,
+        // update output port's definition and reinitialize decoder.
+        if (mInitNeeded && !mIsInFlush) {
+            bool portWillReset = false;
+            handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight);
+
+            CHECK_EQ(reInitDecoder(), (status_t)OK);
+            return;
+        }
+
+        /* Get a free slot in timestamp array to hold input timestamp */
+        {
+            size_t i;
+            timeStampIx = 0;
+            for (i = 0; i < MAX_TIME_STAMPS; i++) {
+                if (!mTimeStampsValid[i]) {
+                    timeStampIx = i;
+                    break;
+                }
+            }
+            if (inHeader != NULL) {
+                mTimeStampsValid[timeStampIx] = true;
+                mTimeStamps[timeStampIx] = inHeader->nTimeStamp;
+            }
+        }
+
+        {
+            ivd_video_decode_ip_t s_dec_ip;
+            ivd_video_decode_op_t s_dec_op;
+            WORD32 timeDelay, timeTaken;
+            size_t sizeY, sizeUV;
+
+            setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+            // If input dump is enabled, then write to file
+            DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes);
+
+            GETTIME(&mTimeStart, NULL);
+            /* Compute time elapsed between end of previous decode()
+             * to start of current decode() */
+            TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+            IV_API_CALL_STATUS_T status;
+            status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+
+            bool unsupportedDimensions =
+                (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
+            bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+
+            GETTIME(&mTimeEnd, NULL);
+            /* Compute time taken for decode() */
+            TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+            PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+                   s_dec_op.u4_num_bytes_consumed);
+            if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
+                mFlushNeeded = true;
+            }
+
+            if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) {
+                /* If the input did not contain picture data, then ignore
+                 * the associated timestamp */
+                mTimeStampsValid[timeStampIx] = false;
+            }
+
+            // This is needed to handle CTS DecoderTest testCodecResetsH264WithoutSurface,
+            // which is not sending SPS/PPS after port reconfiguration and flush to the codec.
+            if (unsupportedDimensions && !mFlushNeeded) {
+                bool portWillReset = false;
+                handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht);
+
+                CHECK_EQ(reInitDecoder(), (status_t)OK);
+
+                setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+
+                ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+                return;
+            }
+
+            // If the decoder is in the changing resolution mode and there is no output present,
+            // that means the switching is done and it's ready to reset the decoder and the plugin.
+            if (mChangingResolution && !s_dec_op.u4_output_present) {
+                mChangingResolution = false;
+                resetDecoder();
+                resetPlugin();
+                continue;
+            }
+
+            if (unsupportedDimensions || resChanged) {
+                mChangingResolution = true;
+                if (mFlushNeeded) {
+                    setFlushMode();
+                }
+
+                if (unsupportedDimensions) {
+                    mNewWidth = s_dec_op.u4_pic_wd;
+                    mNewHeight = s_dec_op.u4_pic_ht;
+                    mInitNeeded = true;
+                }
+                continue;
+            }
+
+            if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
+                uint32_t width = s_dec_op.u4_pic_wd;
+                uint32_t height = s_dec_op.u4_pic_ht;
+                bool portWillReset = false;
+                handlePortSettingsChange(&portWillReset, width, height);
+
+                if (portWillReset) {
+                    resetDecoder();
+                    return;
+                }
+            }
+
+            if (s_dec_op.u4_output_present) {
+                outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+
+                outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts];
+                mTimeStampsValid[s_dec_op.u4_ts] = false;
+
+                outInfo->mOwnedByUs = false;
+                outQueue.erase(outQueue.begin());
+                outInfo = NULL;
+                notifyFillBufferDone(outHeader);
+                outHeader = NULL;
+            } else {
+                /* If in flush mode and no output is returned by the codec,
+                 * then come out of flush mode */
+                mIsInFlush = false;
+
+                /* If EOS was recieved on input port and there is no output
+                 * from the codec, then signal EOS on output port */
+                if (mReceivedEOS) {
+                    outHeader->nFilledLen = 0;
+                    outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+                    outInfo->mOwnedByUs = false;
+                    outQueue.erase(outQueue.begin());
+                    outInfo = NULL;
+                    notifyFillBufferDone(outHeader);
+                    outHeader = NULL;
+                    resetPlugin();
+                }
+            }
+        }
+
+        if (inHeader != NULL) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
+        OMX_COMPONENTTYPE **component) {
+    return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
new file mode 100644
index 0000000..191a71d
--- /dev/null
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_H264_DEC_H_
+
+#define SOFT_H264_DEC_H_
+
+#include "SoftVideoDecoderOMXComponent.h"
+#include <sys/time.h>
+
+namespace android {
+
+#define ivd_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ivd_aligned_free(buf) free(buf)
+
+/** Number of entries in the time-stamp array */
+#define MAX_TIME_STAMPS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH     1920
+
+#define CODEC_MAX_HEIGHT    1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+    diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+            (end.tv_usec - start.tv_usec);
+
+struct SoftAVC : public SoftVideoDecoderOMXComponent {
+    SoftAVC(const char *name, const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData, OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftAVC();
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onReset();
+    virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params);
+private:
+    // Number of input and output buffers
+    enum {
+        kNumBuffers = 8
+    };
+
+    iv_obj_t *mCodecCtx;         // Codec context
+    iv_mem_rec_t *mMemRecords;   // Memory records requested by the codec
+    size_t mNumMemRecords;       // Number of memory records requested by the codec
+
+    size_t mNumCores;            // Number of cores to be uesd by the codec
+
+    struct timeval mTimeStart;   // Time at the start of decode()
+    struct timeval mTimeEnd;     // Time at the end of decode()
+
+    // Internal buffer to be used to flush out the buffers from decoder
+    uint8_t *mFlushOutBuffer;
+
+    // Status of entries in the timestamp array
+    bool mTimeStampsValid[MAX_TIME_STAMPS];
+
+    // Timestamp array - Since codec does not take 64 bit timestamps,
+    // they are maintained in the plugin
+    OMX_S64 mTimeStamps[MAX_TIME_STAMPS];
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    OMX_COLOR_FORMATTYPE mOmxColorFormat;    // OMX Color format
+    IV_COLOR_FORMAT_T mIvColorFormat;        // Ittiam Color format
+
+    bool mIsInFlush;        // codec is flush mode
+    bool mReceivedEOS;      // EOS is receieved on input port
+    bool mInitNeeded;
+    uint32_t mNewWidth;
+    uint32_t mNewHeight;
+    // The input stream has changed to a different resolution, which is still supported by the
+    // codec. So the codec is switching to decode the new resolution.
+    bool mChangingResolution;
+    bool mFlushNeeded;
+
+    status_t initDecoder();
+    status_t deInitDecoder();
+    status_t setFlushMode();
+    status_t setParams(size_t stride);
+    void logVersion();
+    status_t setNumCores();
+    status_t resetDecoder();
+    status_t resetPlugin();
+    status_t reInitDecoder();
+
+    void setDecodeArgs(
+            ivd_video_decode_ip_t *ps_dec_ip,
+            ivd_video_decode_op_t *ps_dec_op,
+            OMX_BUFFERHEADERTYPE *inHeader,
+            OMX_BUFFERHEADERTYPE *outHeader,
+            size_t timeStampIx);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH     "/sdcard/media/avcd_input"
+#define INPUT_DUMP_EXT      "h264"
+
+#define GENERATE_FILE_NAMES() {                         \
+    GETTIME(&mTimeStart, NULL);                         \
+    strcpy(mInFile, "");                                \
+    sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            INPUT_DUMP_EXT);                            \
+}
+
+#define CREATE_DUMP_FILE(m_filename) {                  \
+    FILE *fp = fopen(m_filename, "wb");                 \
+    if (fp != NULL) {                                   \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not open file %s", m_filename);    \
+    }                                                   \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+{                                                       \
+    FILE *fp = fopen(m_filename, "ab");                 \
+    if (fp != NULL && m_buf != NULL) {                  \
+        int i;                                          \
+        i = fwrite(m_buf, 1, m_size, fp);               \
+        ALOGD("fwrite ret %d to write %d", i, m_size);  \
+        if (i != (int) m_size) {                        \
+            ALOGD("Error in fwrite, returned %d", i);   \
+            perror("Error in write to file");           \
+        }                                               \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not write to file %s", m_filename);\
+    }                                                   \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif  // SOFT_H264_DEC_H_
diff --git a/media/libstagefright/codecs/avcenc/Android.mk b/media/libstagefright/codecs/avcenc/Android.mk
new file mode 100644
index 0000000..24a4db9
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/Android.mk
@@ -0,0 +1,30 @@
+#ifeq ($(if $(wildcard external/libh264),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE            := libstagefright_soft_avcenc
+LOCAL_MODULE_TAGS       := optional
+
+LOCAL_STATIC_LIBRARIES  := libavcenc
+LOCAL_SRC_FILES         := SoftAVCEnc.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libavc/encoder
+LOCAL_C_INCLUDES += $(TOP)/external/libavc/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/hardware
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES  := libstagefright
+LOCAL_SHARED_LIBRARIES  += libstagefright_omx
+LOCAL_SHARED_LIBRARIES  += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES  += libutils
+LOCAL_SHARED_LIBRARIES  += liblog
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
+
+#endif
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
new file mode 100755
index 0000000..6afac74
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -0,0 +1,1335 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftAVCEnc"
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "OMX_Video.h"
+
+#include <HardwareAPI.h>
+#include <MetadataBufferType.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <ui/Rect.h>
+
+#include "ih264_typedefs.h"
+#include "iv2.h"
+#include "ive2.h"
+#include "ih264e.h"
+#include "SoftAVCEnc.h"
+
+namespace android {
+
+    #define ive_api_function ih264e_api_function
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+typedef struct LevelConversion {
+    OMX_VIDEO_AVCLEVELTYPE omxLevel;
+    WORD32 avcLevel;
+} LevelConcersion;
+
+static LevelConversion ConversionTable[] = {
+    { OMX_VIDEO_AVCLevel1,  10 },
+    { OMX_VIDEO_AVCLevel1b, 9  },
+    { OMX_VIDEO_AVCLevel11, 11 },
+    { OMX_VIDEO_AVCLevel12, 12 },
+    { OMX_VIDEO_AVCLevel13, 13 },
+    { OMX_VIDEO_AVCLevel2,  20 },
+    { OMX_VIDEO_AVCLevel21, 21 },
+    { OMX_VIDEO_AVCLevel22, 22 },
+    { OMX_VIDEO_AVCLevel3,  30 },
+    { OMX_VIDEO_AVCLevel31, 31 },
+    { OMX_VIDEO_AVCLevel32, 32 },
+    { OMX_VIDEO_AVCLevel4,  40 },
+    { OMX_VIDEO_AVCLevel41, 41 },
+    { OMX_VIDEO_AVCLevel42, 42 },
+    { OMX_VIDEO_AVCLevel5,  50 },
+    { OMX_VIDEO_AVCLevel51, 51 },
+};
+
+static const CodecProfileLevel kProfileLevels[] = {
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4  },
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+};
+
+
+static size_t GetCPUCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGD("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+static status_t ConvertOmxAvcLevelToAvcSpecLevel(
+        OMX_VIDEO_AVCLEVELTYPE omxLevel, WORD32 *avcLevel) {
+    for (size_t i = 0; i < NELEM(ConversionTable); ++i) {
+        if (omxLevel == ConversionTable[i].omxLevel) {
+            *avcLevel = ConversionTable[i].avcLevel;
+            return OK;
+        }
+    }
+
+    ALOGE("ConvertOmxAvcLevelToAvcSpecLevel: %d level not supported",
+            (int32_t)omxLevel);
+
+    return BAD_VALUE;
+}
+
+static status_t ConvertAvcSpecLevelToOmxAvcLevel(
+        WORD32 avcLevel, OMX_VIDEO_AVCLEVELTYPE *omxLevel) {
+    for (size_t i = 0; i < NELEM(ConversionTable); ++i) {
+        if (avcLevel == ConversionTable[i].avcLevel) {
+            *omxLevel = ConversionTable[i].omxLevel;
+            return OK;
+        }
+    }
+
+    ALOGE("ConvertAvcSpecLevelToOmxAvcLevel: %d level not supported",
+            (int32_t)avcLevel);
+
+    return BAD_VALUE;
+}
+
+
+SoftAVC::SoftAVC(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SoftVideoEncoderOMXComponent(
+            name, "video_encoder.avc", OMX_VIDEO_CodingAVC,
+            kProfileLevels, NELEM(kProfileLevels),
+            176 /* width */, 144 /* height */,
+            callbacks, appData, component),
+      mIvVideoColorFormat(IV_YUV_420P),
+      mIDRFrameRefreshIntervalInSec(1),
+      mAVCEncProfile(IV_PROFILE_BASE),
+      mAVCEncLevel(31),
+      mPrevTimestampUs(-1),
+      mStarted(false),
+      mSawInputEOS(false),
+      mSignalledError(false),
+      mConversionBuffer(NULL),
+      mCodecCtx(NULL) {
+
+    initPorts(kNumBuffers, kNumBuffers, ((mWidth * mHeight * 3) >> 1),
+            MEDIA_MIMETYPE_VIDEO_AVC, 2);
+
+    // If dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+    CREATE_DUMP_FILE(mOutFile);
+
+}
+
+SoftAVC::~SoftAVC() {
+    releaseEncoder();
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    CHECK(outQueue.empty());
+    CHECK(inQueue.empty());
+}
+
+OMX_ERRORTYPE SoftAVC::initEncParams() {
+    mCodecCtx = NULL;
+    mMemRecords = NULL;
+    mNumMemRecords = DEFAULT_MEM_REC_CNT;
+    mHeaderGenerated = 0;
+    mNumCores = GetCPUCoreCount();
+    mArch = DEFAULT_ARCH;
+    mSliceMode = DEFAULT_SLICE_MODE;
+    mSliceParam = DEFAULT_SLICE_PARAM;
+    mHalfPelEnable = DEFAULT_HPEL;
+    mIInterval = DEFAULT_I_INTERVAL;
+    mIDRInterval = DEFAULT_IDR_INTERVAL;
+    mDisableDeblkLevel = DEFAULT_DISABLE_DEBLK_LEVEL;
+    mFrameRate = DEFAULT_SRC_FRAME_RATE;
+    mEnableFastSad = DEFAULT_ENABLE_FAST_SAD;
+    mEnableAltRef = DEFAULT_ENABLE_ALT_REF;
+    mEncSpeed = DEFAULT_ENC_SPEED;
+    mIntra4x4 = DEFAULT_INTRA4x4;
+    mAIRMode = DEFAULT_AIR;
+    mAIRRefreshPeriod = DEFAULT_AIR_REFRESH_PERIOD;
+    mPSNREnable = DEFAULT_PSNR_ENABLE;
+    mReconEnable = DEFAULT_RECON_ENABLE;
+
+    gettimeofday(&mTimeStart, NULL);
+    gettimeofday(&mTimeEnd, NULL);
+
+    return OMX_ErrorNone;
+}
+
+
+OMX_ERRORTYPE SoftAVC::setDimensions() {
+    ive_ctl_set_dimensions_ip_t s_dimensions_ip;
+    ive_ctl_set_dimensions_op_t s_dimensions_op;
+    IV_STATUS_T status;
+
+    s_dimensions_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_dimensions_ip.e_sub_cmd = IVE_CMD_CTL_SET_DIMENSIONS;
+    s_dimensions_ip.u4_ht = mHeight;
+    s_dimensions_ip.u4_wd = mWidth;
+    s_dimensions_ip.u4_strd = mStride;
+
+    s_dimensions_ip.u4_timestamp_high = -1;
+    s_dimensions_ip.u4_timestamp_low = -1;
+
+    s_dimensions_ip.u4_size = sizeof(ive_ctl_set_dimensions_ip_t);
+    s_dimensions_op.u4_size = sizeof(ive_ctl_set_dimensions_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_dimensions_ip, &s_dimensions_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame dimensions = 0x%x\n",
+                s_dimensions_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setNumCores() {
+    IV_STATUS_T status;
+    ive_ctl_set_num_cores_ip_t s_num_cores_ip;
+    ive_ctl_set_num_cores_op_t s_num_cores_op;
+    s_num_cores_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_num_cores_ip.e_sub_cmd = IVE_CMD_CTL_SET_NUM_CORES;
+    s_num_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_CORES);
+    s_num_cores_ip.u4_timestamp_high = -1;
+    s_num_cores_ip.u4_timestamp_low = -1;
+    s_num_cores_ip.u4_size = sizeof(ive_ctl_set_num_cores_ip_t);
+
+    s_num_cores_op.u4_size = sizeof(ive_ctl_set_num_cores_op_t);
+
+    status = ive_api_function(
+            mCodecCtx, (void *) &s_num_cores_ip, (void *) &s_num_cores_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set processor params = 0x%x\n",
+                s_num_cores_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setFrameRate() {
+    ive_ctl_set_frame_rate_ip_t s_frame_rate_ip;
+    ive_ctl_set_frame_rate_op_t s_frame_rate_op;
+    IV_STATUS_T status;
+
+    s_frame_rate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_frame_rate_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMERATE;
+
+    s_frame_rate_ip.u4_src_frame_rate = mFrameRate;
+    s_frame_rate_ip.u4_tgt_frame_rate = mFrameRate;
+
+    s_frame_rate_ip.u4_timestamp_high = -1;
+    s_frame_rate_ip.u4_timestamp_low = -1;
+
+    s_frame_rate_ip.u4_size = sizeof(ive_ctl_set_frame_rate_ip_t);
+    s_frame_rate_op.u4_size = sizeof(ive_ctl_set_frame_rate_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_frame_rate_ip, &s_frame_rate_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame rate = 0x%x\n",
+                s_frame_rate_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setIpeParams() {
+    ive_ctl_set_ipe_params_ip_t s_ipe_params_ip;
+    ive_ctl_set_ipe_params_op_t s_ipe_params_op;
+    IV_STATUS_T status;
+
+    s_ipe_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_ipe_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_IPE_PARAMS;
+
+    s_ipe_params_ip.u4_enable_intra_4x4 = mIntra4x4;
+    s_ipe_params_ip.u4_enc_speed_preset = mEncSpeed;
+
+    s_ipe_params_ip.u4_timestamp_high = -1;
+    s_ipe_params_ip.u4_timestamp_low = -1;
+
+    s_ipe_params_ip.u4_size = sizeof(ive_ctl_set_ipe_params_ip_t);
+    s_ipe_params_op.u4_size = sizeof(ive_ctl_set_ipe_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_ipe_params_ip, &s_ipe_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set ipe params = 0x%x\n",
+                s_ipe_params_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setBitRate() {
+    ive_ctl_set_bitrate_ip_t s_bitrate_ip;
+    ive_ctl_set_bitrate_op_t s_bitrate_op;
+    IV_STATUS_T status;
+
+    s_bitrate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_bitrate_ip.e_sub_cmd = IVE_CMD_CTL_SET_BITRATE;
+
+    s_bitrate_ip.u4_target_bitrate = mBitrate;
+
+    s_bitrate_ip.u4_timestamp_high = -1;
+    s_bitrate_ip.u4_timestamp_low = -1;
+
+    s_bitrate_ip.u4_size = sizeof(ive_ctl_set_bitrate_ip_t);
+    s_bitrate_op.u4_size = sizeof(ive_ctl_set_bitrate_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_bitrate_ip, &s_bitrate_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set bit rate = 0x%x\n", s_bitrate_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type) {
+    ive_ctl_set_frame_type_ip_t s_frame_type_ip;
+    ive_ctl_set_frame_type_op_t s_frame_type_op;
+    IV_STATUS_T status;
+
+    s_frame_type_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_frame_type_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMETYPE;
+
+    s_frame_type_ip.e_frame_type = e_frame_type;
+
+    s_frame_type_ip.u4_timestamp_high = -1;
+    s_frame_type_ip.u4_timestamp_low = -1;
+
+    s_frame_type_ip.u4_size = sizeof(ive_ctl_set_frame_type_ip_t);
+    s_frame_type_op.u4_size = sizeof(ive_ctl_set_frame_type_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_frame_type_ip, &s_frame_type_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame type = 0x%x\n",
+                s_frame_type_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setQp() {
+    ive_ctl_set_qp_ip_t s_qp_ip;
+    ive_ctl_set_qp_op_t s_qp_op;
+    IV_STATUS_T status;
+
+    s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
+
+    s_qp_ip.u4_i_qp = DEFAULT_I_QP;
+    s_qp_ip.u4_i_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_i_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_p_qp = DEFAULT_P_QP;
+    s_qp_ip.u4_p_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_p_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_b_qp = DEFAULT_P_QP;
+    s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_timestamp_high = -1;
+    s_qp_ip.u4_timestamp_low = -1;
+
+    s_qp_ip.u4_size = sizeof(ive_ctl_set_qp_ip_t);
+    s_qp_op.u4_size = sizeof(ive_ctl_set_qp_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_qp_ip, &s_qp_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set qp 0x%x\n", s_qp_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setEncMode(IVE_ENC_MODE_T e_enc_mode) {
+    IV_STATUS_T status;
+    ive_ctl_set_enc_mode_ip_t s_enc_mode_ip;
+    ive_ctl_set_enc_mode_op_t s_enc_mode_op;
+
+    s_enc_mode_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_enc_mode_ip.e_sub_cmd = IVE_CMD_CTL_SET_ENC_MODE;
+
+    s_enc_mode_ip.e_enc_mode = e_enc_mode;
+
+    s_enc_mode_ip.u4_timestamp_high = -1;
+    s_enc_mode_ip.u4_timestamp_low = -1;
+
+    s_enc_mode_ip.u4_size = sizeof(ive_ctl_set_enc_mode_ip_t);
+    s_enc_mode_op.u4_size = sizeof(ive_ctl_set_enc_mode_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_enc_mode_ip, &s_enc_mode_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set in header encode mode = 0x%x\n",
+                s_enc_mode_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setVbvParams() {
+    ive_ctl_set_vbv_params_ip_t s_vbv_ip;
+    ive_ctl_set_vbv_params_op_t s_vbv_op;
+    IV_STATUS_T status;
+
+    s_vbv_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_vbv_ip.e_sub_cmd = IVE_CMD_CTL_SET_VBV_PARAMS;
+
+    s_vbv_ip.u4_vbv_buf_size = 0;
+    s_vbv_ip.u4_vbv_buffer_delay = 1000;
+
+    s_vbv_ip.u4_timestamp_high = -1;
+    s_vbv_ip.u4_timestamp_low = -1;
+
+    s_vbv_ip.u4_size = sizeof(ive_ctl_set_vbv_params_ip_t);
+    s_vbv_op.u4_size = sizeof(ive_ctl_set_vbv_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_vbv_ip, &s_vbv_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set VBC params = 0x%x\n", s_vbv_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setAirParams() {
+    ive_ctl_set_air_params_ip_t s_air_ip;
+    ive_ctl_set_air_params_op_t s_air_op;
+    IV_STATUS_T status;
+
+    s_air_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_air_ip.e_sub_cmd = IVE_CMD_CTL_SET_AIR_PARAMS;
+
+    s_air_ip.e_air_mode = mAIRMode;
+    s_air_ip.u4_air_refresh_period = mAIRRefreshPeriod;
+
+    s_air_ip.u4_timestamp_high = -1;
+    s_air_ip.u4_timestamp_low = -1;
+
+    s_air_ip.u4_size = sizeof(ive_ctl_set_air_params_ip_t);
+    s_air_op.u4_size = sizeof(ive_ctl_set_air_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_air_ip, &s_air_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set air params = 0x%x\n", s_air_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setMeParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_me_params_ip_t s_me_params_ip;
+    ive_ctl_set_me_params_op_t s_me_params_op;
+
+    s_me_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_me_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_ME_PARAMS;
+
+    s_me_params_ip.u4_enable_fast_sad = mEnableFastSad;
+    s_me_params_ip.u4_enable_alt_ref = mEnableAltRef;
+
+    s_me_params_ip.u4_enable_hpel = mHalfPelEnable;
+    s_me_params_ip.u4_enable_qpel = DEFAULT_QPEL;
+    s_me_params_ip.u4_me_speed_preset = DEFAULT_ME_SPEED;
+    s_me_params_ip.u4_srch_rng_x = DEFAULT_SRCH_RNG_X;
+    s_me_params_ip.u4_srch_rng_y = DEFAULT_SRCH_RNG_Y;
+
+    s_me_params_ip.u4_timestamp_high = -1;
+    s_me_params_ip.u4_timestamp_low = -1;
+
+    s_me_params_ip.u4_size = sizeof(ive_ctl_set_me_params_ip_t);
+    s_me_params_op.u4_size = sizeof(ive_ctl_set_me_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_me_params_ip, &s_me_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set me params = 0x%x\n", s_me_params_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setGopParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_gop_params_ip_t s_gop_params_ip;
+    ive_ctl_set_gop_params_op_t s_gop_params_op;
+
+    s_gop_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_gop_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_GOP_PARAMS;
+
+    s_gop_params_ip.u4_i_frm_interval = mIInterval;
+    s_gop_params_ip.u4_idr_frm_interval = mIDRInterval;
+    s_gop_params_ip.u4_num_b_frames = DEFAULT_B_FRAMES;
+
+    s_gop_params_ip.u4_timestamp_high = -1;
+    s_gop_params_ip.u4_timestamp_low = -1;
+
+    s_gop_params_ip.u4_size = sizeof(ive_ctl_set_gop_params_ip_t);
+    s_gop_params_op.u4_size = sizeof(ive_ctl_set_gop_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_gop_params_ip, &s_gop_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set ME params = 0x%x\n",
+                s_gop_params_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setProfileParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_profile_params_ip_t s_profile_params_ip;
+    ive_ctl_set_profile_params_op_t s_profile_params_op;
+
+    s_profile_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_profile_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_PROFILE_PARAMS;
+
+    s_profile_params_ip.e_profile = DEFAULT_EPROFILE;
+
+    s_profile_params_ip.u4_timestamp_high = -1;
+    s_profile_params_ip.u4_timestamp_low = -1;
+
+    s_profile_params_ip.u4_size = sizeof(ive_ctl_set_profile_params_ip_t);
+    s_profile_params_op.u4_size = sizeof(ive_ctl_set_profile_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_profile_params_ip, &s_profile_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set profile params = 0x%x\n",
+                s_profile_params_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setDeblockParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_deblock_params_ip_t s_deblock_params_ip;
+    ive_ctl_set_deblock_params_op_t s_deblock_params_op;
+
+    s_deblock_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_deblock_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_DEBLOCK_PARAMS;
+
+    s_deblock_params_ip.u4_disable_deblock_level = mDisableDeblkLevel;
+
+    s_deblock_params_ip.u4_timestamp_high = -1;
+    s_deblock_params_ip.u4_timestamp_low = -1;
+
+    s_deblock_params_ip.u4_size = sizeof(ive_ctl_set_deblock_params_ip_t);
+    s_deblock_params_op.u4_size = sizeof(ive_ctl_set_deblock_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_deblock_params_ip, &s_deblock_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to enable/disable deblock params = 0x%x\n",
+                s_deblock_params_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+    return OMX_ErrorNone;
+}
+
+void SoftAVC::logVersion() {
+    ive_ctl_getversioninfo_ip_t s_ctl_ip;
+    ive_ctl_getversioninfo_op_t s_ctl_op;
+    UWORD8 au1_buf[512];
+    IV_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVE_CMD_CTL_GETVERSION;
+    s_ctl_ip.u4_size = sizeof(ive_ctl_getversioninfo_ip_t);
+    s_ctl_op.u4_size = sizeof(ive_ctl_getversioninfo_op_t);
+    s_ctl_ip.pu1_version = au1_buf;
+    s_ctl_ip.u4_version_bufsize = sizeof(au1_buf);
+
+    status = ive_api_function(mCodecCtx, (void *) &s_ctl_ip, (void *) &s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in getting version: 0x%x", s_ctl_op.u4_error_code);
+    } else {
+        ALOGV("Ittiam encoder version: %s", (char *)s_ctl_ip.pu1_version);
+    }
+    return;
+}
+
+OMX_ERRORTYPE SoftAVC::initEncoder() {
+    IV_STATUS_T status;
+    size_t i;
+    WORD32 level;
+    uint32_t displaySizeY;
+    CHECK(!mStarted);
+
+    OMX_ERRORTYPE errType = OMX_ErrorNone;
+
+    displaySizeY = mWidth * mHeight;
+    if (displaySizeY > (1920 * 1088)) {
+        level = 50;
+    } else if (displaySizeY > (1280 * 720)) {
+        level = 40;
+    } else if (displaySizeY > (720 * 576)) {
+        level = 31;
+    } else if (displaySizeY > (624 * 320)) {
+        level = 30;
+    } else if (displaySizeY > (352 * 288)) {
+        level = 21;
+    } else {
+        level = 20;
+    }
+    mAVCEncLevel = MAX(level, mAVCEncLevel);
+
+    if (OMX_ErrorNone != (errType = initEncParams())) {
+        ALOGE("Failed to initialize encoder params");
+        mSignalledError = true;
+        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+        return errType;
+    }
+
+    mStride = mWidth;
+
+    if (mInputDataIsMeta) {
+        if (mConversionBuffer) {
+            free(mConversionBuffer);
+            mConversionBuffer = NULL;
+        }
+
+        if (mConversionBuffer == NULL) {
+            mConversionBuffer = (uint8_t *)malloc(mStride * mHeight * 3 / 2);
+            if (mConversionBuffer == NULL) {
+                ALOGE("Allocating conversion buffer failed.");
+                return OMX_ErrorUndefined;
+            }
+        }
+    }
+
+    switch (mColorFormat) {
+        case OMX_COLOR_FormatYUV420SemiPlanar:
+            mIvVideoColorFormat = IV_YUV_420SP_UV;
+            ALOGV("colorFormat YUV_420SP");
+            break;
+        default:
+        case OMX_COLOR_FormatYUV420Planar:
+            mIvVideoColorFormat = IV_YUV_420P;
+            ALOGV("colorFormat YUV_420P");
+            break;
+    }
+
+    ALOGV("Params width %d height %d level %d colorFormat %d", mWidth,
+            mHeight, mAVCEncLevel, mIvVideoColorFormat);
+
+    /* Getting Number of MemRecords */
+    {
+        iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+        iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+        s_num_mem_rec_ip.u4_size = sizeof(iv_num_mem_rec_ip_t);
+        s_num_mem_rec_op.u4_size = sizeof(iv_num_mem_rec_op_t);
+
+        s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+        status = ive_api_function(0, &s_num_mem_rec_ip, &s_num_mem_rec_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Get number of memory records failed = 0x%x\n",
+                    s_num_mem_rec_op.u4_error_code);
+            return OMX_ErrorUndefined;
+        }
+
+        mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+    }
+
+    /* Allocate array to hold memory records */
+    mMemRecords = (iv_mem_rec_t *)malloc(mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (NULL == mMemRecords) {
+        ALOGE("Unable to allocate memory for hold memory records: Size %zu",
+                mNumMemRecords * sizeof(iv_mem_rec_t));
+        mSignalledError = true;
+        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+        return OMX_ErrorUndefined;
+    }
+
+    {
+        iv_mem_rec_t *ps_mem_rec;
+        ps_mem_rec = mMemRecords;
+        for (i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->u4_size = sizeof(iv_mem_rec_t);
+            ps_mem_rec->pv_base = NULL;
+            ps_mem_rec->u4_mem_size = 0;
+            ps_mem_rec->u4_mem_alignment = 0;
+            ps_mem_rec->e_mem_type = IV_NA_MEM_TYPE;
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Getting MemRecords Attributes */
+    {
+        iv_fill_mem_rec_ip_t s_fill_mem_rec_ip;
+        iv_fill_mem_rec_op_t s_fill_mem_rec_op;
+
+        s_fill_mem_rec_ip.u4_size = sizeof(iv_fill_mem_rec_ip_t);
+        s_fill_mem_rec_op.u4_size = sizeof(iv_fill_mem_rec_op_t);
+
+        s_fill_mem_rec_ip.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+        s_fill_mem_rec_ip.ps_mem_rec = mMemRecords;
+        s_fill_mem_rec_ip.u4_num_mem_rec = mNumMemRecords;
+        s_fill_mem_rec_ip.u4_max_wd = mWidth;
+        s_fill_mem_rec_ip.u4_max_ht = mHeight;
+        s_fill_mem_rec_ip.u4_max_level = mAVCEncLevel;
+        s_fill_mem_rec_ip.e_color_format = DEFAULT_INP_COLOR_FORMAT;
+        s_fill_mem_rec_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+        s_fill_mem_rec_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+        s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+        s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+
+        status = ive_api_function(0, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Fill memory records failed = 0x%x\n",
+                    s_fill_mem_rec_op.u4_error_code);
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+            return OMX_ErrorUndefined;
+        }
+    }
+
+    /* Allocating Memory for Mem Records */
+    {
+        WORD32 total_size;
+        iv_mem_rec_t *ps_mem_rec;
+        total_size = 0;
+
+        ps_mem_rec = mMemRecords;
+        for (i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->pv_base = ive_aligned_malloc(
+                    ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+            if (ps_mem_rec->pv_base == NULL) {
+                ALOGE("Allocation failure for mem record id %zu size %u\n", i,
+                        ps_mem_rec->u4_mem_size);
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                return OMX_ErrorUndefined;
+
+            }
+            total_size += ps_mem_rec->u4_mem_size;
+
+            ps_mem_rec++;
+        }
+        printf("\nTotal memory for codec %d\n", total_size);
+    }
+
+    /* Codec Instance Creation */
+    {
+        ive_init_ip_t s_init_ip;
+        ive_init_op_t s_init_op;
+
+        mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+        mCodecCtx->u4_size = sizeof(iv_obj_t);
+        mCodecCtx->pv_fxns = (void *)ive_api_function;
+
+        s_init_ip.u4_size = sizeof(ive_init_ip_t);
+        s_init_op.u4_size = sizeof(ive_init_op_t);
+
+        s_init_ip.e_cmd = IV_CMD_INIT;
+        s_init_ip.u4_num_mem_rec = mNumMemRecords;
+        s_init_ip.ps_mem_rec = mMemRecords;
+        s_init_ip.u4_max_wd = mWidth;
+        s_init_ip.u4_max_ht = mHeight;
+        s_init_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+        s_init_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+        s_init_ip.u4_max_level = mAVCEncLevel;
+        s_init_ip.e_inp_color_fmt = mIvVideoColorFormat;
+
+        if (mReconEnable || mPSNREnable) {
+            s_init_ip.u4_enable_recon = 1;
+        } else {
+            s_init_ip.u4_enable_recon = 0;
+        }
+        s_init_ip.e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT;
+        s_init_ip.e_rc_mode = DEFAULT_RC_MODE;
+        s_init_ip.u4_max_framerate = DEFAULT_MAX_FRAMERATE;
+        s_init_ip.u4_max_bitrate = DEFAULT_MAX_BITRATE;
+        s_init_ip.u4_max_num_bframes = DEFAULT_B_FRAMES;
+        s_init_ip.e_content_type = IV_PROGRESSIVE;
+        s_init_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+        s_init_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+        s_init_ip.e_slice_mode = mSliceMode;
+        s_init_ip.u4_slice_param = mSliceParam;
+        s_init_ip.e_arch = mArch;
+        s_init_ip.e_soc = DEFAULT_SOC;
+
+        status = ive_api_function(mCodecCtx, &s_init_ip, &s_init_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Init memory records failed = 0x%x\n",
+                    s_init_op.u4_error_code);
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorUndefined, 0 /* arg2 */, NULL /* data */);
+            return OMX_ErrorUndefined;
+        }
+    }
+
+    /* Get Codec Version */
+    logVersion();
+
+    /* set processor details */
+    setNumCores();
+
+    /* Video control Set Frame dimensions */
+    setDimensions();
+
+    /* Video control Set Frame rates */
+    setFrameRate();
+
+    /* Video control Set IPE Params */
+    setIpeParams();
+
+    /* Video control Set Bitrate */
+    setBitRate();
+
+    /* Video control Set QP */
+    setQp();
+
+    /* Video control Set AIR params */
+    setAirParams();
+
+    /* Video control Set VBV params */
+    setVbvParams();
+
+    /* Video control Set Motion estimation params */
+    setMeParams();
+
+    /* Video control Set GOP params */
+    setGopParams();
+
+    /* Video control Set Deblock params */
+    setDeblockParams();
+
+    /* Video control Set Profile params */
+    setProfileParams();
+
+    /* Video control Set in Encode header mode */
+    setEncMode(IVE_ENC_MODE_HEADER);
+
+    ALOGV("init_codec successfull");
+
+    mSpsPpsHeaderReceived = false;
+    mStarted = true;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::releaseEncoder() {
+    IV_STATUS_T status = IV_SUCCESS;
+    iv_retrieve_mem_rec_ip_t s_retrieve_mem_ip;
+    iv_retrieve_mem_rec_op_t s_retrieve_mem_op;
+    iv_mem_rec_t *ps_mem_rec;
+    UWORD32 i;
+
+    if (!mStarted) {
+        return OMX_ErrorNone;
+    }
+
+    s_retrieve_mem_ip.u4_size = sizeof(iv_retrieve_mem_rec_ip_t);
+    s_retrieve_mem_op.u4_size = sizeof(iv_retrieve_mem_rec_op_t);
+    s_retrieve_mem_ip.e_cmd = IV_CMD_RETRIEVE_MEMREC;
+    s_retrieve_mem_ip.ps_mem_rec = mMemRecords;
+
+    status = ive_api_function(mCodecCtx, &s_retrieve_mem_ip, &s_retrieve_mem_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to retrieve memory records = 0x%x\n",
+                s_retrieve_mem_op.u4_error_code);
+        return OMX_ErrorUndefined;
+    }
+
+    /* Free memory records */
+    ps_mem_rec = mMemRecords;
+    for (i = 0; i < s_retrieve_mem_op.u4_num_mem_rec_filled; i++) {
+        ive_aligned_free(ps_mem_rec->pv_base);
+        ps_mem_rec++;
+    }
+
+    free(mMemRecords);
+
+    if (mConversionBuffer != NULL) {
+        free(mConversionBuffer);
+        mConversionBuffer = NULL;
+    }
+
+    mStarted = false;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamVideoBitrate:
+        {
+            OMX_VIDEO_PARAM_BITRATETYPE *bitRate =
+                (OMX_VIDEO_PARAM_BITRATETYPE *)params;
+
+            if (bitRate->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            bitRate->eControlRate = OMX_Video_ControlRateVariable;
+            bitRate->nTargetBitrate = mBitrate;
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoAvc:
+        {
+            OMX_VIDEO_PARAM_AVCTYPE *avcParams = (OMX_VIDEO_PARAM_AVCTYPE *)params;
+
+            if (avcParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            avcParams->eProfile = OMX_VIDEO_AVCProfileBaseline;
+            OMX_VIDEO_AVCLEVELTYPE omxLevel = OMX_VIDEO_AVCLevel31;
+            if (OMX_ErrorNone
+                    != ConvertAvcSpecLevelToOmxAvcLevel(mAVCEncLevel, &omxLevel)) {
+                return OMX_ErrorUndefined;
+            }
+
+            avcParams->eLevel = omxLevel;
+            avcParams->nRefFrames = 1;
+            avcParams->nBFrames = 0;
+            avcParams->bUseHadamard = OMX_TRUE;
+            avcParams->nAllowedPictureTypes = (OMX_VIDEO_PictureTypeI
+                    | OMX_VIDEO_PictureTypeP);
+            avcParams->nRefIdx10ActiveMinus1 = 0;
+            avcParams->nRefIdx11ActiveMinus1 = 0;
+            avcParams->bWeightedPPrediction = OMX_FALSE;
+            avcParams->bEntropyCodingCABAC = OMX_FALSE;
+            avcParams->bconstIpred = OMX_FALSE;
+            avcParams->bDirect8x8Inference = OMX_FALSE;
+            avcParams->bDirectSpatialTemporal = OMX_FALSE;
+            avcParams->nCabacInitIdc = 0;
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SoftVideoEncoderOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
+    int32_t indexFull = index;
+
+    switch (indexFull) {
+        case OMX_IndexParamVideoBitrate:
+        {
+            return internalSetBitrateParams(
+                    (const OMX_VIDEO_PARAM_BITRATETYPE *)params);
+        }
+
+        case OMX_IndexParamVideoAvc:
+        {
+            OMX_VIDEO_PARAM_AVCTYPE *avcType = (OMX_VIDEO_PARAM_AVCTYPE *)params;
+
+            if (avcType->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (avcType->eProfile != OMX_VIDEO_AVCProfileBaseline
+                    || avcType->nRefFrames != 1 || avcType->nBFrames != 0
+                    || avcType->bUseHadamard != OMX_TRUE
+                    || (avcType->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) != 0
+                    || avcType->nRefIdx10ActiveMinus1 != 0
+                    || avcType->nRefIdx11ActiveMinus1 != 0
+                    || avcType->bWeightedPPrediction != OMX_FALSE
+                    || avcType->bEntropyCodingCABAC != OMX_FALSE
+                    || avcType->bconstIpred != OMX_FALSE
+                    || avcType->bDirect8x8Inference != OMX_FALSE
+                    || avcType->bDirectSpatialTemporal != OMX_FALSE
+                    || avcType->nCabacInitIdc != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (OK != ConvertOmxAvcLevelToAvcSpecLevel(avcType->eLevel, &mAVCEncLevel)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SoftVideoEncoderOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAVC::setConfig(
+        OMX_INDEXTYPE index, const OMX_PTR _params) {
+    switch (index) {
+        case OMX_IndexConfigVideoIntraVOPRefresh:
+        {
+            OMX_CONFIG_INTRAREFRESHVOPTYPE *params =
+                (OMX_CONFIG_INTRAREFRESHVOPTYPE *)_params;
+
+            if (params->nPortIndex != kOutputPortIndex) {
+                return OMX_ErrorBadPortIndex;
+            }
+
+            mKeyFrameRequested = params->IntraRefreshVOP;
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexConfigVideoBitrate:
+        {
+            OMX_VIDEO_CONFIG_BITRATETYPE *params =
+                (OMX_VIDEO_CONFIG_BITRATETYPE *)_params;
+
+            if (params->nPortIndex != kOutputPortIndex) {
+                return OMX_ErrorBadPortIndex;
+            }
+
+            if (mBitrate != params->nEncodeBitrate) {
+                mBitrate = params->nEncodeBitrate;
+                mBitrateUpdated = true;
+            }
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::setConfig(index, _params);
+    }
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetBitrateParams(
+        const OMX_VIDEO_PARAM_BITRATETYPE *bitrate) {
+    if (bitrate->nPortIndex != kOutputPortIndex) {
+        return OMX_ErrorUnsupportedIndex;
+    }
+
+    mBitrate = bitrate->nTargetBitrate;
+    mBitrateUpdated = true;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftAVC::setEncodeArgs(
+        ive_video_encode_ip_t *ps_encode_ip,
+        ive_video_encode_op_t *ps_encode_op,
+        OMX_BUFFERHEADERTYPE *inputBufferHeader,
+        OMX_BUFFERHEADERTYPE *outputBufferHeader) {
+    iv_raw_buf_t *ps_inp_raw_buf;
+    const uint8_t *source;
+    UWORD8 *pu1_buf;
+
+    ps_inp_raw_buf = &ps_encode_ip->s_inp_buf;
+    ps_encode_ip->s_out_buf.pv_buf = outputBufferHeader->pBuffer;
+    ps_encode_ip->s_out_buf.u4_bytes = 0;
+    ps_encode_ip->s_out_buf.u4_bufsize = outputBufferHeader->nAllocLen;
+    ps_encode_ip->u4_size = sizeof(ive_video_encode_ip_t);
+    ps_encode_op->u4_size = sizeof(ive_video_encode_op_t);
+
+    ps_encode_ip->e_cmd = IVE_CMD_VIDEO_ENCODE;
+    ps_encode_ip->pv_bufs = NULL;
+    ps_encode_ip->pv_mb_info = NULL;
+    ps_encode_ip->pv_pic_info = NULL;
+    ps_encode_ip->u4_mb_info_type = 0;
+    ps_encode_ip->u4_pic_info_type = 0;
+    ps_encode_op->s_out_buf.pv_buf = NULL;
+
+    /* Initialize color formats */
+    ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat;
+
+    source = NULL;
+    if (inputBufferHeader) {
+        source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset;
+
+        if (mInputDataIsMeta) {
+            source = extractGraphicBuffer(
+                    mConversionBuffer, (mWidth * mHeight * 3 / 2), source,
+                    inputBufferHeader->nFilledLen, mWidth, mHeight);
+
+            if (source == NULL) {
+                ALOGE("Error in extractGraphicBuffer");
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                return OMX_ErrorUndefined;
+            }
+        }
+    }
+
+    pu1_buf = (UWORD8 *)source;
+    switch (mIvVideoColorFormat) {
+        case IV_YUV_420P:
+        {
+            ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+            pu1_buf += (mStride) * mHeight;
+            ps_inp_raw_buf->apv_bufs[1] = pu1_buf;
+            pu1_buf += (mStride / 2) * mHeight / 2;
+            ps_inp_raw_buf->apv_bufs[2] = pu1_buf;
+
+            ps_inp_raw_buf->au4_wd[0] = mWidth;
+            ps_inp_raw_buf->au4_wd[1] = mWidth / 2;
+            ps_inp_raw_buf->au4_wd[2] = mWidth / 2;
+
+            ps_inp_raw_buf->au4_ht[0] = mHeight;
+            ps_inp_raw_buf->au4_ht[1] = mHeight / 2;
+            ps_inp_raw_buf->au4_ht[2] = mHeight / 2;
+
+            ps_inp_raw_buf->au4_strd[0] = mStride;
+            ps_inp_raw_buf->au4_strd[1] = (mStride / 2);
+            ps_inp_raw_buf->au4_strd[2] = (mStride / 2);
+            break;
+        }
+
+        case IV_YUV_422ILE:
+        {
+            ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+            ps_inp_raw_buf->au4_wd[0] = mWidth * 2;
+            ps_inp_raw_buf->au4_ht[0] = mHeight;
+            ps_inp_raw_buf->au4_strd[0] = mStride * 2;
+            break;
+        }
+
+        case IV_YUV_420SP_UV:
+        case IV_YUV_420SP_VU:
+        default:
+        {
+            ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+            pu1_buf += (mStride) * mHeight;
+            ps_inp_raw_buf->apv_bufs[1] = pu1_buf;
+
+            ps_inp_raw_buf->au4_wd[0] = mWidth;
+            ps_inp_raw_buf->au4_wd[1] = mWidth;
+
+            ps_inp_raw_buf->au4_ht[0] = mHeight;
+            ps_inp_raw_buf->au4_ht[1] = mHeight / 2;
+
+            ps_inp_raw_buf->au4_strd[0] = mStride;
+            ps_inp_raw_buf->au4_strd[1] = mStride;
+            break;
+        }
+    }
+
+    ps_encode_ip->u4_is_last = 0;
+
+    if (inputBufferHeader) {
+        ps_encode_ip->u4_timestamp_high = (inputBufferHeader->nTimeStamp) >> 32;
+        ps_encode_ip->u4_timestamp_low = (inputBufferHeader->nTimeStamp) & 0xFFFFFFFF;
+    }
+
+    return OMX_ErrorNone;
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+    IV_STATUS_T status;
+    WORD32 timeDelay, timeTaken;
+
+    UNUSED(portIndex);
+
+    // Initialize encoder if not already initialized
+    if (mCodecCtx == NULL) {
+        if (OMX_ErrorNone != initEncoder()) {
+            ALOGE("Failed to initialize encoder");
+            notify(OMX_EventError, OMX_ErrorUndefined, 0 /* arg2 */, NULL /* data */);
+            return;
+        }
+    }
+    if (mSignalledError || mSawInputEOS) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!mSawInputEOS && !inQueue.empty() && !outQueue.empty()) {
+        OMX_ERRORTYPE error;
+        ive_video_encode_ip_t s_encode_ip;
+        ive_video_encode_op_t s_encode_op;
+
+        BufferInfo *inputBufferInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader;
+
+        BufferInfo *outputBufferInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader;
+
+        if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inputBufferInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inputBufferHeader);
+
+            outputBufferHeader->nFilledLen = 0;
+            outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outputBufferInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outputBufferHeader);
+            return;
+        }
+
+        outputBufferHeader->nTimeStamp = 0;
+        outputBufferHeader->nFlags = 0;
+        outputBufferHeader->nOffset = 0;
+        outputBufferHeader->nFilledLen = 0;
+        outputBufferHeader->nOffset = 0;
+
+        uint8_t *outPtr = (uint8_t *)outputBufferHeader->pBuffer;
+
+        if (!mSpsPpsHeaderReceived) {
+            error = setEncodeArgs(&s_encode_ip, &s_encode_op, NULL, outputBufferHeader);
+            if (error != OMX_ErrorNone) {
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                return;
+            }
+            status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+            if (IV_SUCCESS != status) {
+                ALOGE("Encode Frame failed = 0x%x\n",
+                        s_encode_op.u4_error_code);
+            } else {
+                ALOGV("Bytes Generated in header %d\n",
+                        s_encode_op.s_out_buf.u4_bytes);
+            }
+
+            mSpsPpsHeaderReceived = true;
+
+            outputBufferHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG;
+            outputBufferHeader->nFilledLen = s_encode_op.s_out_buf.u4_bytes;
+            outputBufferHeader->nTimeStamp = inputBufferHeader->nTimeStamp;
+
+            outQueue.erase(outQueue.begin());
+            outputBufferInfo->mOwnedByUs = false;
+            DUMP_TO_FILE(
+                    mOutFile, outputBufferHeader->pBuffer,
+                    outputBufferHeader->nFilledLen);
+            notifyFillBufferDone(outputBufferHeader);
+
+            setEncMode(IVE_ENC_MODE_PICTURE);
+            return;
+        }
+
+        if (mBitrateUpdated) {
+            setBitRate();
+        }
+
+        if (mKeyFrameRequested) {
+            setFrameType(IV_IDR_FRAME);
+        }
+
+        mPrevTimestampUs = inputBufferHeader->nTimeStamp;
+
+        if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            mSawInputEOS = true;
+        }
+
+        error = setEncodeArgs(
+                &s_encode_ip, &s_encode_op, inputBufferHeader, outputBufferHeader);
+        if (error != OMX_ErrorNone) {
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+            return;
+        }
+
+        DUMP_TO_FILE(
+                mInFile, s_encode_ip.s_inp_buf.apv_bufs[0],
+                (mHeight * mStride * 3 / 2));
+        //DUMP_TO_FILE(mInFile, inputBufferHeader->pBuffer + inputBufferHeader->nOffset,
+        //    inputBufferHeader->nFilledLen);
+
+        GETTIME(&mTimeStart, NULL);
+        /* Compute time elapsed between end of previous decode()
+         * to start of current decode() */
+        TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+        status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        if (IV_SUCCESS != status) {
+            ALOGE("Encode Frame failed = 0x%x\n",
+                    s_encode_op.u4_error_code);
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+            return;
+        }
+
+        GETTIME(&mTimeEnd, NULL);
+        /* Compute time taken for decode() */
+        TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+        ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+                s_encode_op.s_out_buf.u4_bytes);
+
+
+        outputBufferHeader->nFlags = inputBufferHeader->nFlags;
+        outputBufferHeader->nFilledLen = s_encode_op.s_out_buf.u4_bytes;
+        outputBufferHeader->nTimeStamp = inputBufferHeader->nTimeStamp;
+
+        if (IV_IDR_FRAME
+                == s_encode_op.u4_encoded_frame_type) {
+            outputBufferHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME;
+        }
+
+        inQueue.erase(inQueue.begin());
+        inputBufferInfo->mOwnedByUs = false;
+
+        notifyEmptyBufferDone(inputBufferHeader);
+
+        if (mSawInputEOS) {
+            outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+        }
+
+        outputBufferInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+
+        DUMP_TO_FILE(
+                mOutFile, outputBufferHeader->pBuffer,
+                outputBufferHeader->nFilledLen);
+        notifyFillBufferDone(outputBufferHeader);
+
+    }
+    return;
+}
+
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
new file mode 100644
index 0000000..2b35d45
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOFT_AVC_ENC_H__
+#define __SOFT_AVC_ENC_H__
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Vector.h>
+
+#include "SoftVideoEncoderOMXComponent.h"
+
+namespace android {
+
+class MediaBuffer;
+
+#define CODEC_MAX_CORES          4
+#define LEN_STATUS_BUFFER        (10  * 1024)
+#define MAX_VBV_BUFF_SIZE        (120 * 16384)
+#define MAX_NUM_IO_BUFS           3
+
+#define DEFAULT_MAX_REF_FRM         1
+#define DEFAULT_MAX_REORDER_FRM     0
+#define DEFAULT_QP_MIN              10
+#define DEFAULT_QP_MAX              40
+#define DEFAULT_MAX_BITRATE         20000000
+#define DEFAULT_MAX_SRCH_RANGE_X    256
+#define DEFAULT_MAX_SRCH_RANGE_Y    256
+#define DEFAULT_MAX_FRAMERATE       120000
+#define DEFAULT_NUM_CORES           1
+#define DEFAULT_NUM_CORES_PRE_ENC   0
+#define DEFAULT_FPS                 30
+#define DEFAULT_ENC_SPEED           IVE_NORMAL
+
+#define DEFAULT_MEM_REC_CNT         0
+#define DEFAULT_RECON_ENABLE        0
+#define DEFAULT_CHKSUM_ENABLE       0
+#define DEFAULT_START_FRM           0
+#define DEFAULT_NUM_FRMS            0xFFFFFFFF
+#define DEFAULT_INP_COLOR_FORMAT       IV_YUV_420SP_VU
+#define DEFAULT_RECON_COLOR_FORMAT     IV_YUV_420P
+#define DEFAULT_LOOPBACK            0
+#define DEFAULT_SRC_FRAME_RATE      30
+#define DEFAULT_TGT_FRAME_RATE      30
+#define DEFAULT_MAX_WD              1920
+#define DEFAULT_MAX_HT              1920
+#define DEFAULT_MAX_LEVEL           40
+#define DEFAULT_STRIDE              0
+#define DEFAULT_WD                  1280
+#define DEFAULT_HT                  720
+#define DEFAULT_PSNR_ENABLE         0
+#define DEFAULT_ME_SPEED            100
+#define DEFAULT_ENABLE_FAST_SAD     0
+#define DEFAULT_ENABLE_ALT_REF      0
+#define DEFAULT_RC_MODE             IVE_RC_STORAGE
+#define DEFAULT_BITRATE             6000000
+#define DEFAULT_I_QP                22
+#define DEFAULT_I_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_I_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_P_QP                28
+#define DEFAULT_P_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_P_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_B_QP                22
+#define DEFAULT_B_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_B_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_AIR                 IVE_AIR_MODE_NONE
+#define DEFAULT_AIR_REFRESH_PERIOD  30
+#define DEFAULT_SRCH_RNG_X          64
+#define DEFAULT_SRCH_RNG_Y          48
+#define DEFAULT_I_INTERVAL          30
+#define DEFAULT_IDR_INTERVAL        1000
+#define DEFAULT_B_FRAMES            0
+#define DEFAULT_DISABLE_DEBLK_LEVEL 0
+#define DEFAULT_HPEL                1
+#define DEFAULT_QPEL                1
+#define DEFAULT_I4                  1
+#define DEFAULT_EPROFILE            IV_PROFILE_BASE
+#define DEFAULT_SLICE_MODE          IVE_SLICE_MODE_NONE
+#define DEFAULT_SLICE_PARAM         256
+#define DEFAULT_ARCH                ARCH_ARM_A9Q
+#define DEFAULT_SOC                 SOC_GENERIC
+#define DEFAULT_INTRA4x4            0
+#define STRLENGTH                   500
+
+
+
+#define MIN(a, b) ((a) < (b))? (a) : (b)
+#define MAX(a, b) ((a) > (b))? (a) : (b)
+#define ALIGN16(x) ((((x) + 15) >> 4) << 4)
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN4096(x) ((((x) + 4095) >> 12) << 12)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+    diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+            (end.tv_usec - start.tv_usec);
+
+#define ive_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ive_aligned_free(buf) free(buf)
+
+struct SoftAVC : public SoftVideoEncoderOMXComponent {
+    SoftAVC(
+            const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+    // Override SimpleSoftOMXComponent methods
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+
+protected:
+    virtual ~SoftAVC();
+
+private:
+    enum {
+        kNumBuffers = 2,
+    };
+
+    // OMX input buffer's timestamp and flags
+    typedef struct {
+        int64_t mTimeUs;
+        int32_t mFlags;
+    } InputBufferInfo;
+
+    int32_t  mStride;
+
+    uint32_t mFrameRate;
+
+    struct timeval mTimeStart;   // Time at the start of decode()
+    struct timeval mTimeEnd;     // Time at the end of decode()
+
+
+    // If a request for a change it bitrate has been received.
+    bool mBitrateUpdated;
+
+    bool mKeyFrameRequested;
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+    char mOutFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    IV_COLOR_FORMAT_T mIvVideoColorFormat;
+
+    int32_t  mIDRFrameRefreshIntervalInSec;
+    IV_PROFILE_T mAVCEncProfile;
+    WORD32   mAVCEncLevel;
+    int64_t  mNumInputFrames;
+    int64_t  mPrevTimestampUs;
+    bool     mStarted;
+    bool     mSpsPpsHeaderReceived;
+
+    bool     mSawInputEOS;
+    bool     mSignalledError;
+    bool     mIntra4x4;
+    bool     mEnableFastSad;
+    bool     mEnableAltRef;
+    bool    mReconEnable;
+    bool    mPSNREnable;
+    IVE_SPEED_CONFIG     mEncSpeed;
+
+    uint8_t *mConversionBuffer;
+
+    iv_obj_t *mCodecCtx;         // Codec context
+    iv_mem_rec_t *mMemRecords;   // Memory records requested by the codec
+    size_t mNumMemRecords;       // Number of memory records requested by codec
+    size_t mNumCores;            // Number of cores used by the codec
+
+    UWORD32 mHeaderGenerated;
+
+    IV_ARCH_T mArch;
+    IVE_SLICE_MODE_T mSliceMode;
+    UWORD32 mSliceParam;
+    bool mHalfPelEnable;
+    UWORD32 mIInterval;
+    UWORD32 mIDRInterval;
+    UWORD32 mDisableDeblkLevel;
+    IVE_AIR_MODE_T mAIRMode;
+    UWORD32 mAIRRefreshPeriod;
+
+    OMX_ERRORTYPE initEncParams();
+    OMX_ERRORTYPE initEncoder();
+    OMX_ERRORTYPE releaseEncoder();
+
+    // Verifies the component role tried to be set to this OMX component is
+    // strictly video_encoder.avc
+    OMX_ERRORTYPE internalSetRoleParams(
+        const OMX_PARAM_COMPONENTROLETYPE *role);
+
+    // Updates bitrate to reflect port settings.
+    OMX_ERRORTYPE internalSetBitrateParams(
+        const OMX_VIDEO_PARAM_BITRATETYPE *bitrate);
+
+    OMX_ERRORTYPE setConfig(
+        OMX_INDEXTYPE index, const OMX_PTR _params);
+
+    // Handles port definition changes.
+    OMX_ERRORTYPE internalSetPortParams(
+        const OMX_PARAM_PORTDEFINITIONTYPE *port);
+
+    OMX_ERRORTYPE internalSetFormatParams(
+        const OMX_VIDEO_PARAM_PORTFORMATTYPE *format);
+
+    OMX_ERRORTYPE setFrameType(IV_PICTURE_CODING_TYPE_T  e_frame_type);
+    OMX_ERRORTYPE setQp();
+    OMX_ERRORTYPE setEncMode(IVE_ENC_MODE_T e_enc_mode);
+    OMX_ERRORTYPE setDimensions();
+    OMX_ERRORTYPE setNumCores();
+    OMX_ERRORTYPE setFrameRate();
+    OMX_ERRORTYPE setIpeParams();
+    OMX_ERRORTYPE setBitRate();
+    OMX_ERRORTYPE setAirParams();
+    OMX_ERRORTYPE setMeParams();
+    OMX_ERRORTYPE setGopParams();
+    OMX_ERRORTYPE setProfileParams();
+    OMX_ERRORTYPE setDeblockParams();
+    OMX_ERRORTYPE setVbvParams();
+    void logVersion();
+    OMX_ERRORTYPE setEncodeArgs(
+        ive_video_encode_ip_t *ps_encode_ip,
+        ive_video_encode_op_t *ps_encode_op,
+        OMX_BUFFERHEADERTYPE *inputBufferHeader,
+        OMX_BUFFERHEADERTYPE *outputBufferHeader);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH     "/sdcard/media/avce_input"
+#define INPUT_DUMP_EXT      "yuv"
+#define OUTPUT_DUMP_PATH    "/sdcard/media/avce_output"
+#define OUTPUT_DUMP_EXT     "h264"
+
+#define GENERATE_FILE_NAMES() {                         \
+    GETTIME(&mTimeStart, NULL);                         \
+    strcpy(mInFile, "");                                \
+    sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            INPUT_DUMP_EXT);                            \
+    strcpy(mOutFile, "");                               \
+    sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,\
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            OUTPUT_DUMP_EXT);                           \
+}
+
+#define CREATE_DUMP_FILE(m_filename) {                  \
+    FILE *fp = fopen(m_filename, "wb");                 \
+    if (fp != NULL) {                                   \
+        ALOGD("Opened file %s", m_filename);            \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not open file %s", m_filename);    \
+    }                                                   \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+{                                                       \
+    FILE *fp = fopen(m_filename, "ab");                 \
+    if (fp != NULL && m_buf != NULL) {                  \
+        int i;                                          \
+        i = fwrite(m_buf, 1, m_size, fp);               \
+        ALOGD("fwrite ret %d to write %d", i, m_size);  \
+        if (i != (int)m_size) {                         \
+            ALOGD("Error in fwrite, returned %d", i);   \
+            perror("Error in write to file");           \
+        }                                               \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not write to file %s", m_filename);\
+    }                                                   \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+}  // namespace android
+
+#endif  // __SOFT_AVC_ENC_H__
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
index cddd176..5c05a0e 100644
--- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
@@ -143,7 +143,7 @@
     s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
     s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
 
-    ALOGV("Set the run-time (dynamic) parameters stride = %u", stride);
+    ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
     status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
             (void *)&s_ctl_op);
 
@@ -408,7 +408,7 @@
     uint32_t bufferSize = displaySizeY * 3 / 2;
     mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
     if (NULL == mFlushOutBuffer) {
-        ALOGE("Could not allocate flushOutputBuffer of size %zu", bufferSize);
+        ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
         return NO_MEMORY;
     }
 
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.mk b/media/libstagefright/codecs/mpeg2dec/Android.mk
new file mode 100644
index 0000000..23b126d
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/Android.mk
@@ -0,0 +1,27 @@
+ifeq ($(if $(wildcard external/libmpeg2),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE            := libstagefright_soft_mpeg2dec
+LOCAL_MODULE_TAGS       := optional
+
+LOCAL_STATIC_LIBRARIES  := libmpeg2dec
+LOCAL_SRC_FILES         := SoftMPEG2.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libmpeg2/decoder
+LOCAL_C_INCLUDES += $(TOP)/external/libmpeg2/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES  := libstagefright
+LOCAL_SHARED_LIBRARIES  += libstagefright_omx
+LOCAL_SHARED_LIBRARIES  += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES  += libutils
+LOCAL_SHARED_LIBRARIES  += liblog
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
new file mode 100644
index 0000000..78b3ab4
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -0,0 +1,771 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftMPEG2"
+#include <utils/Log.h>
+
+#include "iv_datatypedef.h"
+#include "iv.h"
+#include "ivd.h"
+#include "ithread.h"
+#include "impeg2d.h"
+#include "SoftMPEG2.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <OMX_VideoExt.h>
+
+namespace android {
+
+#define componentName                   "video_decoder.mpeg2"
+#define codingType                      OMX_VIDEO_CodingMPEG2
+#define CODEC_MIME_TYPE                 MEDIA_MIMETYPE_VIDEO_MPEG2
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function              impeg2d_api_function
+#define ivdext_init_ip_t                impeg2d_init_ip_t
+#define ivdext_init_op_t                impeg2d_init_op_t
+#define ivdext_fill_mem_rec_ip_t        impeg2d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t        impeg2d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t   impeg2d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   impeg2d_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
+
+static const CodecProfileLevel kProfileLevels[] = {
+    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelLL  },
+    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelML  },
+    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelH14 },
+    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelHL  },
+
+    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelLL  },
+    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelML  },
+    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelH14 },
+    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelHL  },
+};
+
+SoftMPEG2::SoftMPEG2(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SoftVideoDecoderOMXComponent(
+            name, componentName, codingType,
+            kProfileLevels, ARRAY_SIZE(kProfileLevels),
+            320 /* width */, 240 /* height */, callbacks,
+            appData, component),
+      mMemRecords(NULL),
+      mFlushOutBuffer(NULL),
+      mOmxColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mIvColorFormat(IV_YUV_420P),
+      mNewWidth(mWidth),
+      mNewHeight(mHeight),
+      mChangingResolution(false) {
+    initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
+
+    // If input dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftMPEG2::~SoftMPEG2() {
+    CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+
+static size_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
+    OMX_S64 minTimeStamp = LLONG_MAX;
+    int idx = -1;
+    for (size_t i = 0; i < MAX_TIME_STAMPS; i++) {
+        if (pIsTimeStampValid[i]) {
+            if (pNTimeStamp[i] < minTimeStamp) {
+                minTimeStamp = pNTimeStamp[i];
+                idx = i;
+            }
+        }
+    }
+    return idx;
+}
+
+static size_t GetCPUCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+void SoftMPEG2::logVersion() {
+    ivd_ctl_getversioninfo_ip_t s_ctl_ip;
+    ivd_ctl_getversioninfo_op_t s_ctl_op;
+    UWORD8 au1_buf[512];
+    IV_API_CALL_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+    s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+    s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+    s_ctl_ip.pv_version_buffer = au1_buf;
+    s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);
+
+    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in getting version number: 0x%x",
+                s_ctl_op.u4_error_code);
+    } else {
+        ALOGV("Ittiam decoder version number: %s",
+                (char *)s_ctl_ip.pv_version_buffer);
+    }
+    return;
+}
+
+status_t SoftMPEG2::setParams(size_t stride) {
+    ivd_ctl_set_config_ip_t s_ctl_ip;
+    ivd_ctl_set_config_op_t s_ctl_op;
+    IV_API_CALL_STATUS_T status;
+    s_ctl_ip.u4_disp_wd = (UWORD32)stride;
+    s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+
+    s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+    s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+
+    ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
+    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in setting the run-time parameters: 0x%x",
+                s_ctl_op.u4_error_code);
+
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+status_t SoftMPEG2::resetPlugin() {
+    mIsInFlush = false;
+    mReceivedEOS = false;
+    memset(mTimeStamps, 0, sizeof(mTimeStamps));
+    memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+    /* Initialize both start and end times */
+    gettimeofday(&mTimeStart, NULL);
+    gettimeofday(&mTimeEnd, NULL);
+
+    return OK;
+}
+
+status_t SoftMPEG2::resetDecoder() {
+    ivd_ctl_reset_ip_t s_ctl_ip;
+    ivd_ctl_reset_op_t s_ctl_op;
+    IV_API_CALL_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+
+    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    /* Set the run-time (dynamic) parameters */
+    setParams(outputBufferWidth());
+
+    /* Set number of cores/threads to be used by the codec */
+    setNumCores();
+
+    return OK;
+}
+
+status_t SoftMPEG2::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_cores_ip;
+    ivdext_ctl_set_num_cores_op_t s_set_cores_op;
+    IV_API_CALL_STATUS_T status;
+    s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES);
+    s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+
+    status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in setting number of cores: 0x%x",
+                s_set_cores_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+status_t SoftMPEG2::setFlushMode() {
+    IV_API_CALL_STATUS_T status;
+    ivd_ctl_flush_ip_t s_video_flush_ip;
+    ivd_ctl_flush_op_t s_video_flush_op;
+
+    s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+    s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+    s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+
+    /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+    status = ivdec_api_function(
+            mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
+                s_video_flush_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    mWaitForI = true;
+    mIsInFlush = true;
+    return OK;
+}
+
+status_t SoftMPEG2::initDecoder() {
+    IV_API_CALL_STATUS_T status;
+
+    UWORD32 u4_num_reorder_frames;
+    UWORD32 u4_num_ref_frames;
+    UWORD32 u4_share_disp_buf;
+
+    mNumCores = GetCPUCoreCount();
+    mWaitForI = true;
+
+    /* Initialize number of ref and reorder modes (for MPEG2) */
+    u4_num_reorder_frames = 16;
+    u4_num_ref_frames = 16;
+    u4_share_disp_buf = 0;
+
+    uint32_t displayStride = outputBufferWidth();
+    uint32_t displayHeight = outputBufferHeight();
+    uint32_t displaySizeY = displayStride * displayHeight;
+
+    {
+        iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+        iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+        s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+        s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+        s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+        status = ivdec_api_function(
+                mCodecCtx, (void *)&s_num_mem_rec_ip, (void *)&s_num_mem_rec_op);
+        if (IV_SUCCESS != status) {
+            ALOGE("Error in getting mem records: 0x%x",
+                    s_num_mem_rec_op.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+
+        mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+    }
+
+    mMemRecords = (iv_mem_rec_t *)ivd_aligned_malloc(
+            128, mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (mMemRecords == NULL) {
+        ALOGE("Allocation failure");
+        return NO_MEMORY;
+    }
+
+    memset(mMemRecords, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+
+    {
+        size_t i;
+        ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+        ivdext_fill_mem_rec_op_t s_fill_mem_op;
+        iv_mem_rec_t *ps_mem_rec;
+
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size =
+            sizeof(ivdext_fill_mem_rec_ip_t);
+
+        s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf;
+        s_fill_mem_ip.e_output_format = mIvColorFormat;
+
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = displayStride;
+        s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = displayHeight;
+        s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size =
+            sizeof(ivdext_fill_mem_rec_op_t);
+
+        ps_mem_rec = mMemRecords;
+        for (i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+        }
+
+        status = ivdec_api_function(
+                mCodecCtx, (void *)&s_fill_mem_ip, (void *)&s_fill_mem_op);
+
+        if (IV_SUCCESS != status) {
+            ALOGE("Error in filling mem records: 0x%x",
+                    s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+        mNumMemRecords =
+            s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled;
+
+        ps_mem_rec = mMemRecords;
+
+        for (i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->pv_base = ivd_aligned_malloc(
+                    ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+            if (ps_mem_rec->pv_base == NULL) {
+                ALOGE("Allocation failure for memory record #%zu of size %u",
+                        i, ps_mem_rec->u4_mem_size);
+                status = IV_FAIL;
+                return NO_MEMORY;
+            }
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Initialize the decoder */
+    {
+        ivdext_init_ip_t s_init_ip;
+        ivdext_init_op_t s_init_op;
+
+        void *dec_fxns = (void *)ivdec_api_function;
+
+        s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+        s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+        s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+        s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = displayStride;
+        s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = displayHeight;
+
+        s_init_ip.u4_share_disp_buf = u4_share_disp_buf;
+
+        s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op);
+
+        s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+        s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat;
+
+        mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+        mCodecCtx->pv_fxns = dec_fxns;
+        mCodecCtx->u4_size = sizeof(iv_obj_t);
+
+        status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op);
+        if (status != IV_SUCCESS) {
+            ALOGE("Error in init: 0x%x",
+                    s_init_op.s_ivd_init_op_t.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    /* Reset the plugin state */
+    resetPlugin();
+
+    /* Set the run time (dynamic) parameters */
+    setParams(displayStride);
+
+    /* Set number of cores/threads to be used by the codec */
+    setNumCores();
+
+    /* Get codec version */
+    logVersion();
+
+    /* Allocate internal picture buffer */
+    uint32_t bufferSize = displaySizeY * 3 / 2;
+    mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+    if (NULL == mFlushOutBuffer) {
+        ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
+        return NO_MEMORY;
+    }
+
+    mInitNeeded = false;
+    mFlushNeeded = false;
+    return OK;
+}
+
+status_t SoftMPEG2::deInitDecoder() {
+    size_t i;
+
+    if (mMemRecords) {
+        iv_mem_rec_t *ps_mem_rec;
+
+        ps_mem_rec = mMemRecords;
+        for (i = 0; i < mNumMemRecords; i++) {
+            if (ps_mem_rec->pv_base) {
+                ivd_aligned_free(ps_mem_rec->pv_base);
+            }
+            ps_mem_rec++;
+        }
+        ivd_aligned_free(mMemRecords);
+        mMemRecords = NULL;
+    }
+
+    if (mFlushOutBuffer) {
+        ivd_aligned_free(mFlushOutBuffer);
+        mFlushOutBuffer = NULL;
+    }
+
+    mInitNeeded = true;
+    mChangingResolution = false;
+
+    return OK;
+}
+
+status_t SoftMPEG2::reInitDecoder() {
+    status_t ret;
+
+    deInitDecoder();
+
+    ret = initDecoder();
+    if (OK != ret) {
+        ALOGE("Create failure");
+        deInitDecoder();
+        return NO_MEMORY;
+    }
+    return OK;
+}
+
+void SoftMPEG2::onReset() {
+    SoftVideoDecoderOMXComponent::onReset();
+
+    mWaitForI = true;
+
+    resetDecoder();
+    resetPlugin();
+}
+
+OMX_ERRORTYPE SoftMPEG2::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) {
+    const uint32_t oldWidth = mWidth;
+    const uint32_t oldHeight = mHeight;
+    OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params);
+    if (mWidth != oldWidth || mHeight != oldHeight) {
+        reInitDecoder();
+    }
+    return ret;
+}
+
+void SoftMPEG2::setDecodeArgs(
+        ivd_video_decode_ip_t *ps_dec_ip,
+        ivd_video_decode_op_t *ps_dec_op,
+        OMX_BUFFERHEADERTYPE *inHeader,
+        OMX_BUFFERHEADERTYPE *outHeader,
+        size_t timeStampIx) {
+    size_t sizeY = outputBufferWidth() * outputBufferHeight();
+    size_t sizeUV;
+    uint8_t *pBuf;
+
+    ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+    ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+    ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+
+    /* When in flush and after EOS with zero byte input,
+     * inHeader is set to zero. Hence check for non-null */
+    if (inHeader) {
+        ps_dec_ip->u4_ts = timeStampIx;
+        ps_dec_ip->pv_stream_buffer = inHeader->pBuffer
+                + inHeader->nOffset;
+        ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen;
+    } else {
+        ps_dec_ip->u4_ts = 0;
+        ps_dec_ip->pv_stream_buffer = NULL;
+        ps_dec_ip->u4_num_Bytes = 0;
+    }
+
+    if (outHeader) {
+        pBuf = outHeader->pBuffer;
+    } else {
+        pBuf = mFlushOutBuffer;
+    }
+
+    sizeUV = sizeY / 4;
+    ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+    ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+    ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+    ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf;
+    ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY;
+    ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV;
+    ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
+    return;
+}
+void SoftMPEG2::onPortFlushCompleted(OMX_U32 portIndex) {
+    /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
+    if (kOutputPortIndex == portIndex) {
+        setFlushMode();
+
+        while (true) {
+            ivd_video_decode_ip_t s_dec_ip;
+            ivd_video_decode_op_t s_dec_op;
+            IV_API_CALL_STATUS_T status;
+            size_t sizeY, sizeUV;
+
+            setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0);
+
+            status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+            if (0 == s_dec_op.u4_output_present) {
+                resetPlugin();
+                break;
+            }
+        }
+    }
+}
+
+void SoftMPEG2::onQueueFilled(OMX_U32 portIndex) {
+    UNUSED(portIndex);
+
+    if (mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
+    List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
+
+    /* If input EOS is seen and decoder is not in flush mode,
+     * set the decoder in flush mode.
+     * There can be a case where EOS is sent along with last picture data
+     * In that case, only after decoding that input data, decoder has to be
+     * put in flush. This case is handled here  */
+
+    if (mReceivedEOS && !mIsInFlush) {
+        setFlushMode();
+    }
+
+    while (!outQueue.empty()) {
+        BufferInfo *inInfo;
+        OMX_BUFFERHEADERTYPE *inHeader;
+
+        BufferInfo *outInfo;
+        OMX_BUFFERHEADERTYPE *outHeader;
+        size_t timeStampIx;
+
+        inInfo = NULL;
+        inHeader = NULL;
+
+        if (!mIsInFlush) {
+            if (!inQueue.empty()) {
+                inInfo = *inQueue.begin();
+                inHeader = inInfo->mHeader;
+            } else {
+                break;
+            }
+        }
+
+        outInfo = *outQueue.begin();
+        outHeader = outInfo->mHeader;
+        outHeader->nFlags = 0;
+        outHeader->nTimeStamp = 0;
+        outHeader->nOffset = 0;
+
+        if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+            mReceivedEOS = true;
+            if (inHeader->nFilledLen == 0) {
+                inQueue.erase(inQueue.begin());
+                inInfo->mOwnedByUs = false;
+                notifyEmptyBufferDone(inHeader);
+                inHeader = NULL;
+                setFlushMode();
+            }
+        }
+
+        // When there is an init required and the decoder is not in flush mode,
+        // update output port's definition and reinitialize decoder.
+        if (mInitNeeded && !mIsInFlush) {
+            bool portWillReset = false;
+            handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight);
+
+            CHECK_EQ(reInitDecoder(), (status_t)OK);
+            return;
+        }
+
+        /* Get a free slot in timestamp array to hold input timestamp */
+        {
+            size_t i;
+            timeStampIx = 0;
+            for (i = 0; i < MAX_TIME_STAMPS; i++) {
+                if (!mTimeStampsValid[i]) {
+                    timeStampIx = i;
+                    break;
+                }
+            }
+            if (inHeader != NULL) {
+                mTimeStampsValid[timeStampIx] = true;
+                mTimeStamps[timeStampIx] = inHeader->nTimeStamp;
+            }
+        }
+
+        {
+            ivd_video_decode_ip_t s_dec_ip;
+            ivd_video_decode_op_t s_dec_op;
+            WORD32 timeDelay, timeTaken;
+            size_t sizeY, sizeUV;
+
+            setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+            // If input dump is enabled, then write to file
+            DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes);
+
+            if (s_dec_ip.u4_num_Bytes > 0) {
+                char *ptr = (char *)s_dec_ip.pv_stream_buffer;
+            }
+
+            GETTIME(&mTimeStart, NULL);
+            /* Compute time elapsed between end of previous decode()
+             * to start of current decode() */
+            TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+            IV_API_CALL_STATUS_T status;
+            status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+
+            bool unsupportedDimensions = (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code);
+            bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+
+            GETTIME(&mTimeEnd, NULL);
+            /* Compute time taken for decode() */
+            TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+            ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+                   s_dec_op.u4_num_bytes_consumed);
+            if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
+                mFlushNeeded = true;
+            }
+
+            if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) {
+                /* If the input did not contain picture data, then ignore
+                 * the associated timestamp */
+                mTimeStampsValid[timeStampIx] = false;
+            }
+
+            // This is needed to handle CTS DecoderTest testCodecResetsMPEG2WithoutSurface,
+            // which is not sending SPS/PPS after port reconfiguration and flush to the codec.
+            if (unsupportedDimensions && !mFlushNeeded) {
+                bool portWillReset = false;
+                handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht);
+
+                CHECK_EQ(reInitDecoder(), (status_t)OK);
+
+                setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+
+                ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+                return;
+            }
+
+            // If the decoder is in the changing resolution mode and there is no output present,
+            // that means the switching is done and it's ready to reset the decoder and the plugin.
+            if (mChangingResolution && !s_dec_op.u4_output_present) {
+                mChangingResolution = false;
+                resetDecoder();
+                resetPlugin();
+                continue;
+            }
+
+            if (unsupportedDimensions || resChanged) {
+                mChangingResolution = true;
+                if (mFlushNeeded) {
+                    setFlushMode();
+                }
+
+                if (unsupportedDimensions) {
+                    mNewWidth = s_dec_op.u4_pic_wd;
+                    mNewHeight = s_dec_op.u4_pic_ht;
+                    mInitNeeded = true;
+                }
+                continue;
+            }
+
+            if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
+                uint32_t width = s_dec_op.u4_pic_wd;
+                uint32_t height = s_dec_op.u4_pic_ht;
+                bool portWillReset = false;
+                handlePortSettingsChange(&portWillReset, width, height);
+
+                if (portWillReset) {
+                    resetDecoder();
+                    return;
+                }
+            }
+
+            if (s_dec_op.u4_output_present) {
+                size_t timeStampIdx;
+                outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+
+                timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid);
+                outHeader->nTimeStamp = mTimeStamps[timeStampIdx];
+                mTimeStampsValid[timeStampIdx] = false;
+
+                /* mWaitForI waits for the first I picture. Once made FALSE, it
+                   has to remain false till explicitly set to TRUE. */
+                mWaitForI = mWaitForI && !(IV_I_FRAME == s_dec_op.e_pic_type);
+
+                if (mWaitForI) {
+                    s_dec_op.u4_output_present = false;
+                } else {
+                    ALOGV("Output timestamp: %lld, res: %ux%u",
+                            (long long)outHeader->nTimeStamp, mWidth, mHeight);
+                    DUMP_TO_FILE(mOutFile, outHeader->pBuffer, outHeader->nFilledLen);
+                    outInfo->mOwnedByUs = false;
+                    outQueue.erase(outQueue.begin());
+                    outInfo = NULL;
+                    notifyFillBufferDone(outHeader);
+                    outHeader = NULL;
+                }
+            } else {
+                /* If in flush mode and no output is returned by the codec,
+                 * then come out of flush mode */
+                mIsInFlush = false;
+
+                /* If EOS was recieved on input port and there is no output
+                 * from the codec, then signal EOS on output port */
+                if (mReceivedEOS) {
+                    outHeader->nFilledLen = 0;
+                    outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+                    outInfo->mOwnedByUs = false;
+                    outQueue.erase(outQueue.begin());
+                    outInfo = NULL;
+                    notifyFillBufferDone(outHeader);
+                    outHeader = NULL;
+                    resetPlugin();
+                }
+            }
+        }
+
+        // TODO: Handle more than one picture data
+        if (inHeader != NULL) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
+        OMX_COMPONENTTYPE **component) {
+    return new android::SoftMPEG2(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
new file mode 100644
index 0000000..a625e08
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_MPEG2_H_
+
+#define SOFT_MPEG2_H_
+
+#include "SoftVideoDecoderOMXComponent.h"
+#include <sys/time.h>
+
+namespace android {
+
+#define ivd_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ivd_aligned_free(buf) free(buf)
+
+/** Number of entries in the time-stamp array */
+#define MAX_TIME_STAMPS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH     1920
+
+#define CODEC_MAX_HEIGHT    1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+    diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+            (end.tv_usec - start.tv_usec);
+
+struct SoftMPEG2 : public SoftVideoDecoderOMXComponent {
+    SoftMPEG2(
+            const char *name, const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData, OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftMPEG2();
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onReset();
+    virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params);
+private:
+    // Number of input and output buffers
+    enum {
+        kNumBuffers = 8
+    };
+
+    iv_obj_t *mCodecCtx;         // Codec context
+    iv_mem_rec_t *mMemRecords;   // Memory records requested by the codec
+    size_t mNumMemRecords;       // Number of memory records requested by the codec
+
+    size_t mNumCores;            // Number of cores to be uesd by the codec
+
+    struct timeval mTimeStart;   // Time at the start of decode()
+    struct timeval mTimeEnd;     // Time at the end of decode()
+
+    // Internal buffer to be used to flush out the buffers from decoder
+    uint8_t *mFlushOutBuffer;
+
+    // Status of entries in the timestamp array
+    bool mTimeStampsValid[MAX_TIME_STAMPS];
+
+    // Timestamp array - Since codec does not take 64 bit timestamps,
+    // they are maintained in the plugin
+    OMX_S64 mTimeStamps[MAX_TIME_STAMPS];
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    OMX_COLOR_FORMATTYPE mOmxColorFormat;    // OMX Color format
+    IV_COLOR_FORMAT_T mIvColorFormat;        // Ittiam Color format
+
+    bool mIsInFlush;        // codec is flush mode
+    bool mReceivedEOS;      // EOS is receieved on input port
+    bool mInitNeeded;
+    uint32_t mNewWidth;
+    uint32_t mNewHeight;
+    // The input stream has changed to a different resolution, which is still supported by the
+    // codec. So the codec is switching to decode the new resolution.
+    bool mChangingResolution;
+    bool mFlushNeeded;
+    bool mWaitForI;
+
+    status_t initDecoder();
+    status_t deInitDecoder();
+    status_t setFlushMode();
+    status_t setParams(size_t stride);
+    void logVersion();
+    status_t setNumCores();
+    status_t resetDecoder();
+    status_t resetPlugin();
+    status_t reInitDecoder();
+
+    void setDecodeArgs(
+            ivd_video_decode_ip_t *ps_dec_ip,
+            ivd_video_decode_op_t *ps_dec_op,
+            OMX_BUFFERHEADERTYPE *inHeader,
+            OMX_BUFFERHEADERTYPE *outHeader,
+            size_t timeStampIx);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG2);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH     "/sdcard/media/mpeg2d_input"
+#define INPUT_DUMP_EXT      "m2v"
+
+#define GENERATE_FILE_NAMES() {                         \
+    GETTIME(&mTimeStart, NULL);                         \
+    strcpy(mInFile, "");                                \
+    sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            INPUT_DUMP_EXT);                            \
+}
+
+#define CREATE_DUMP_FILE(m_filename) {                  \
+    FILE *fp = fopen(m_filename, "wb");                 \
+    if (fp != NULL) {                                   \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not open file %s", m_filename);    \
+    }                                                   \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+{                                                       \
+    FILE *fp = fopen(m_filename, "ab");                 \
+    if (fp != NULL && m_buf != NULL) {                  \
+        int i;                                          \
+        i = fwrite(m_buf, 1, m_size, fp);               \
+        ALOGD("fwrite ret %d to write %d", i, m_size);  \
+        if (i != (int)m_size) {                         \
+            ALOGD("Error in fwrite, returned %d", i);   \
+            perror("Error in write to file");           \
+        }                                               \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not write to file %s", m_filename);\
+    }                                                   \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+} // namespace android
+
+#endif  // SOFT_MPEG2_H_
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 6e6a78a..a35909e 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -139,7 +139,7 @@
         uint32_t height = mImg->d_h;
         outInfo = *outQueue.begin();
         outHeader = outInfo->mHeader;
-        CHECK_EQ(mImg->fmt, IMG_FMT_I420);
+        CHECK_EQ(mImg->fmt, VPX_IMG_FMT_I420);
         handlePortSettingsChange(portWillReset, width, height);
         if (*portWillReset) {
             return true;
@@ -151,12 +151,12 @@
         outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv;
 
         uint8_t *dst = outHeader->pBuffer;
-        const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y];
-        const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U];
-        const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V];
-        size_t srcYStride = mImg->stride[PLANE_Y];
-        size_t srcUStride = mImg->stride[PLANE_U];
-        size_t srcVStride = mImg->stride[PLANE_V];
+        const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
+        const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
+        const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V];
+        size_t srcYStride = mImg->stride[VPX_PLANE_Y];
+        size_t srcUStride = mImg->stride[VPX_PLANE_U];
+        size_t srcVStride = mImg->stride[VPX_PLANE_V];
         copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
 
         mImg = NULL;
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index 970acf3..e654843 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -661,7 +661,8 @@
         BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin();
         OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader;
 
-        if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+        if ((inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) &&
+                inputBufferHeader->nFilledLen == 0) {
             inputBufferInfoQueue.erase(inputBufferInfoQueue.begin());
             inputBufferInfo->mOwnedByUs = false;
             notifyEmptyBufferDone(inputBufferHeader);
@@ -762,6 +763,9 @@
                        encoded_packet->data.frame.sz);
                 outputBufferInfo->mOwnedByUs = false;
                 outputBufferInfoQueue.erase(outputBufferInfoQueue.begin());
+                if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+                    outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+                }
                 notifyFillBufferDone(outputBufferHeader);
             }
         }
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
index 8f356b6..c559682 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -364,7 +364,7 @@
         } else {
             numFrames = vorbis_dsp_pcmout(
                     mState, (int16_t *)outHeader->pBuffer,
-                    kMaxNumSamplesPerBuffer);
+                    (kMaxNumSamplesPerBuffer / mVi->channels));
 
             if (numFrames < 0) {
                 ALOGE("vorbis_dsp_pcmout returned %d", numFrames);
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 59a64ba..4f7c48f 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -9,6 +9,9 @@
         $(TOP)/frameworks/native/include/media/openmax \
         $(TOP)/hardware/msm7k
 
+LOCAL_CFLAGS += -Werror
+LOCAL_CLANG := true
+
 LOCAL_MODULE:= libstagefright_color_conversion
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml
old mode 100644
new mode 100755
index 7e9fa18..740f96b
--- a/media/libstagefright/data/media_codecs_google_video.xml
+++ b/media/libstagefright/data/media_codecs_google_video.xml
@@ -16,6 +16,15 @@
 
 <Included>
     <Decoders>
+        <MediaCodec name="OMX.google.mpeg2.decoder" type="video/mpeg2">
+            <!-- profiles and levels:  ProfileMain : LevelHL -->
+            <Limit name="size" min="16x16" max="1920x1088" />
+            <Limit name="alignment" value="2x2" />
+            <Limit name="block-size" value="16x16" />
+            <Limit name="blocks-per-second" range="1-244800" />
+            <Limit name="bitrate" range="1-20000000" />
+            <Feature name="adaptive-playback" />
+        </MediaCodec>
         <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es">
             <!-- profiles and levels:  ProfileSimple : Level3 -->
             <Limit name="size" min="2x2" max="352x288" />
@@ -34,12 +43,12 @@
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="OMX.google.h264.decoder" type="video/avc">
-            <!-- profiles and levels:  ProfileBaseline : Level51 -->
-            <Limit name="size" min="2x2" max="2048x2048" />
+            <!-- profiles and levels:  ProfileHigh : Level41 -->
+            <Limit name="size" min="16x16" max="1920x1088" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
-            <Limit name="blocks-per-second" range="1-983040" />
-            <Limit name="bitrate" range="1-40000000" />
+            <Limit name="blocks-per-second" range="1-244800" />
+            <Limit name="bitrate" range="1-12000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
         <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc">
@@ -78,12 +87,12 @@
             <Limit name="bitrate" range="1-128000" />
         </MediaCodec>
         <MediaCodec name="OMX.google.h264.encoder" type="video/avc">
-            <!-- profiles and levels:  ProfileBaseline : Level2 -->
-            <Limit name="size" min="16x16" max="896x896" />
-            <Limit name="alignment" value="16x16" />
+            <!-- profiles and levels:  ProfileBaseline : Level41 -->
+            <Limit name="size" min="16x16" max="1920x1088" />
+            <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
-            <Limit name="blocks-per-second" range="1-11880" />
-            <Limit name="bitrate" range="1-2000000" />
+            <Limit name="blocks-per-second" range="1-244800" />
+            <Limit name="bitrate" range="1-12000000" />
         </MediaCodec>
         <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es">
             <!-- profiles and levels:  ProfileCore : Level2 -->
diff --git a/media/libstagefright/filters/Android.mk b/media/libstagefright/filters/Android.mk
index 36ab444..179f054 100644
--- a/media/libstagefright/filters/Android.mk
+++ b/media/libstagefright/filters/Android.mk
@@ -20,7 +20,8 @@
 intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
 LOCAL_C_INCLUDES += $(intermediates)
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE:= libstagefright_mediafilter
 
diff --git a/media/libstagefright/filters/ColorConvert.cpp b/media/libstagefright/filters/ColorConvert.cpp
index a5039f9..a8d5dd2 100644
--- a/media/libstagefright/filters/ColorConvert.cpp
+++ b/media/libstagefright/filters/ColorConvert.cpp
@@ -93,8 +93,8 @@
 void convertRGBAToARGB(
         uint8_t *src, int32_t width, int32_t height, uint32_t stride,
         uint8_t *dest) {
-    for (size_t i = 0; i < height; ++i) {
-        for (size_t j = 0; j < width; ++j) {
+    for (int32_t i = 0; i < height; ++i) {
+        for (int32_t j = 0; j < width; ++j) {
             uint8_t r = *src++;
             uint8_t g = *src++;
             uint8_t b = *src++;
diff --git a/media/libstagefright/filters/GraphicBufferListener.cpp b/media/libstagefright/filters/GraphicBufferListener.cpp
index 66374ba..a606315 100644
--- a/media/libstagefright/filters/GraphicBufferListener.cpp
+++ b/media/libstagefright/filters/GraphicBufferListener.cpp
@@ -40,7 +40,7 @@
 
     status_t err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
     if (err != NO_ERROR) {
-        ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+        ALOGE("Unable to set BQ max acquired buffer count to %zu: %d",
                 bufferCount, err);
         return err;
     }
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index 0a09575..0cf6b06 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -76,6 +76,11 @@
     (new AMessage(kWhatCreateInputSurface, this))->post();
 }
 
+void MediaFilter::initiateSetInputSurface(
+        const sp<PersistentSurface> & /* surface */) {
+    ALOGW("initiateSetInputSurface() unsupported");
+}
+
 void MediaFilter::initiateStart() {
     (new AMessage(kWhatStart, this))->post();
 }
@@ -804,7 +809,7 @@
         eosBuf->mGeneration = mGeneration;
         eosBuf->mData->setRange(0, 0);
         postDrainThisBuffer(eosBuf);
-        ALOGV("Posted EOS on output buffer %zu", eosBuf->mBufferID);
+        ALOGV("Posted EOS on output buffer %u", eosBuf->mBufferID);
     }
 
     mPortEOS[kPortIndexOutput] = true;
diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp
index ec4a960..0d1cea4 100644
--- a/media/libstagefright/foundation/ADebug.cpp
+++ b/media/libstagefright/foundation/ADebug.cpp
@@ -19,6 +19,7 @@
 #include <ctype.h>
 
 #define LOG_TAG "ADebug"
+#include <cutils/atomic.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
 
@@ -113,5 +114,43 @@
     return debugName;
 }
 
+//static
+bool ADebug::getExperimentFlag(
+        bool allow, const char *name, uint64_t modulo,
+        uint64_t limit, uint64_t plus, uint64_t timeDivisor) {
+    static volatile int32_t haveSerial = 0;
+    static uint64_t serialNum;
+    if (!android_atomic_acquire_load(&haveSerial)) {
+        // calculate initial counter value based on serial number
+        static char serial[PROPERTY_VALUE_MAX];
+        property_get("ro.serialno", serial, "0");
+        uint64_t num = 0; // it is okay for this number to overflow
+        for (size_t i = 0; i < NELEM(serial) && serial[i] != '\0'; ++i) {
+            const char &c = serial[i];
+            // try to use most letters of serialno
+            if (isdigit(c)) {
+                num = num * 10 + (c - '0');
+            } else if (islower(c)) {
+                num = num * 26 + (c - 'a');
+            } else if (isupper(c)) {
+                num = num * 26 + (c - 'A');
+            } else {
+                num = num * 256 + c;
+            }
+        }
+        ALOGI("got serial");
+        serialNum = num;
+        android_atomic_release_store(1, &haveSerial);
+    }
+    ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL));
+    // MINOR: use modulo for counter and time, so that their sum does not
+    // roll over, and mess up the correlation between related experiments.
+    // e.g. keep (a mod 2N) = 0 impl (a mod N) = 0
+    time_t counter = (time(NULL) / timeDivisor) % modulo + plus + serialNum % modulo;
+    bool enable = allow && (counter % modulo < limit);
+    ALOGI("experiment '%s': %s", name, enable ? "ENABLED" : "disabled");
+    return enable;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 08355c7..c68264c 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -29,7 +29,8 @@
         liblog            \
         libpowermanager
 
-LOCAL_CFLAGS += -Wno-multichar -Werror
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE:= libstagefright_foundation
 
diff --git a/media/libstagefright/http/Android.mk b/media/libstagefright/http/Android.mk
index 7f3307d..5fb51c1 100644
--- a/media/libstagefright/http/Android.mk
+++ b/media/libstagefright/http/Android.mk
@@ -21,7 +21,8 @@
 
 LOCAL_CFLAGS += -Wno-multichar
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/media/libstagefright/http/MediaHTTP.cpp b/media/libstagefright/http/MediaHTTP.cpp
index bb89567..2d9b3d4 100644
--- a/media/libstagefright/http/MediaHTTP.cpp
+++ b/media/libstagefright/http/MediaHTTP.cpp
@@ -30,12 +30,11 @@
 namespace android {
 
 MediaHTTP::MediaHTTP(const sp<IMediaHTTPConnection> &conn)
-    : mInitCheck(NO_INIT),
+    : mInitCheck((conn != NULL) ? OK : NO_INIT),
       mHTTPConnection(conn),
       mCachedSizeValid(false),
       mCachedSize(0ll),
       mDrmManagerClient(NULL) {
-    mInitCheck = OK;
 }
 
 MediaHTTP::~MediaHTTP() {
@@ -171,6 +170,10 @@
 }
 
 String8 MediaHTTP::getUri() {
+    if (mInitCheck != OK) {
+        return String8::empty();
+    }
+
     String8 uri;
     if (OK == mHTTPConnection->getUri(&uri)) {
         return uri;
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index 93b7935..fc85835 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -3,6 +3,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=               \
+        HTTPDownloader.cpp      \
         LiveDataSource.cpp      \
         LiveSession.cpp         \
         M3UParser.cpp           \
@@ -12,7 +13,8 @@
 	$(TOP)/frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder \
diff --git a/media/libstagefright/httplive/HTTPDownloader.cpp b/media/libstagefright/httplive/HTTPDownloader.cpp
new file mode 100644
index 0000000..3b44bae
--- /dev/null
+++ b/media/libstagefright/httplive/HTTPDownloader.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HTTPDownloader"
+#include <utils/Log.h>
+
+#include "HTTPDownloader.h"
+#include "M3UParser.h"
+
+#include <media/IMediaHTTPConnection.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <openssl/aes.h>
+#include <openssl/md5.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+HTTPDownloader::HTTPDownloader(
+        const sp<IMediaHTTPService> &httpService,
+        const KeyedVector<String8, String8> &headers) :
+    mHTTPDataSource(new MediaHTTP(httpService->makeHTTPConnection())),
+    mExtraHeaders(headers),
+    mDisconnecting(false) {
+}
+
+void HTTPDownloader::reconnect() {
+    AutoMutex _l(mLock);
+    mDisconnecting = false;
+}
+
+void HTTPDownloader::disconnect() {
+    {
+        AutoMutex _l(mLock);
+        mDisconnecting = true;
+    }
+    mHTTPDataSource->disconnect();
+}
+
+bool HTTPDownloader::isDisconnecting() {
+    AutoMutex _l(mLock);
+    return mDisconnecting;
+}
+
+/*
+ * Illustration of parameters:
+ *
+ * 0      `range_offset`
+ * +------------+-------------------------------------------------------+--+--+
+ * |            |                                 | next block to fetch |  |  |
+ * |            | `source` handle => `out` buffer |                     |  |  |
+ * | `url` file |<--------- buffer size --------->|<--- `block_size` -->|  |  |
+ * |            |<----------- `range_length` / buffer capacity ----------->|  |
+ * |<------------------------------ file_size ------------------------------->|
+ *
+ * Special parameter values:
+ * - range_length == -1 means entire file
+ * - block_size == 0 means entire range
+ *
+ */
+ssize_t HTTPDownloader::fetchBlock(
+        const char *url, sp<ABuffer> *out,
+        int64_t range_offset, int64_t range_length,
+        uint32_t block_size, /* download block size */
+        String8 *actualUrl,
+        bool reconnect /* force connect HTTP when resuing source */) {
+    if (isDisconnecting()) {
+        return ERROR_NOT_CONNECTED;
+    }
+
+    off64_t size;
+
+    if (reconnect) {
+        if (!strncasecmp(url, "file://", 7)) {
+            mDataSource = new FileSource(url + 7);
+        } else if (strncasecmp(url, "http://", 7)
+                && strncasecmp(url, "https://", 8)) {
+            return ERROR_UNSUPPORTED;
+        } else {
+            KeyedVector<String8, String8> headers = mExtraHeaders;
+            if (range_offset > 0 || range_length >= 0) {
+                headers.add(
+                        String8("Range"),
+                        String8(
+                            AStringPrintf(
+                                "bytes=%lld-%s",
+                                range_offset,
+                                range_length < 0
+                                    ? "" : AStringPrintf("%lld",
+                                            range_offset + range_length - 1).c_str()).c_str()));
+            }
+
+            status_t err = mHTTPDataSource->connect(url, &headers);
+
+            if (isDisconnecting()) {
+                return ERROR_NOT_CONNECTED;
+            }
+
+            if (err != OK) {
+                return err;
+            }
+
+            mDataSource = mHTTPDataSource;
+        }
+    }
+
+    status_t getSizeErr = mDataSource->getSize(&size);
+
+    if (isDisconnecting()) {
+        return ERROR_NOT_CONNECTED;
+    }
+
+    if (getSizeErr != OK) {
+        size = 65536;
+    }
+
+    sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
+    if (*out == NULL) {
+        buffer->setRange(0, 0);
+    }
+
+    ssize_t bytesRead = 0;
+    // adjust range_length if only reading partial block
+    if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {
+        range_length = buffer->size() + block_size;
+    }
+    for (;;) {
+        // Only resize when we don't know the size.
+        size_t bufferRemaining = buffer->capacity() - buffer->size();
+        if (bufferRemaining == 0 && getSizeErr != OK) {
+            size_t bufferIncrement = buffer->size() / 2;
+            if (bufferIncrement < 32768) {
+                bufferIncrement = 32768;
+            }
+            bufferRemaining = bufferIncrement;
+
+            ALOGV("increasing download buffer to %zu bytes",
+                 buffer->size() + bufferRemaining);
+
+            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+            memcpy(copy->data(), buffer->data(), buffer->size());
+            copy->setRange(0, buffer->size());
+
+            buffer = copy;
+        }
+
+        size_t maxBytesToRead = bufferRemaining;
+        if (range_length >= 0) {
+            int64_t bytesLeftInRange = range_length - buffer->size();
+            if (bytesLeftInRange < (int64_t)maxBytesToRead) {
+                maxBytesToRead = bytesLeftInRange;
+
+                if (bytesLeftInRange == 0) {
+                    break;
+                }
+            }
+        }
+
+        // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
+        // to help us break out of the loop.
+        ssize_t n = mDataSource->readAt(
+                buffer->size(), buffer->data() + buffer->size(),
+                maxBytesToRead);
+
+        if (isDisconnecting()) {
+            return ERROR_NOT_CONNECTED;
+        }
+
+        if (n < 0) {
+            return n;
+        }
+
+        if (n == 0) {
+            break;
+        }
+
+        buffer->setRange(0, buffer->size() + (size_t)n);
+        bytesRead += n;
+    }
+
+    *out = buffer;
+    if (actualUrl != NULL) {
+        *actualUrl = mDataSource->getUri();
+        if (actualUrl->isEmpty()) {
+            *actualUrl = url;
+        }
+    }
+
+    return bytesRead;
+}
+
+ssize_t HTTPDownloader::fetchFile(
+        const char *url, sp<ABuffer> *out, String8 *actualUrl) {
+    ssize_t err = fetchBlock(url, out, 0, -1, 0, actualUrl, true /* reconnect */);
+
+    // close off the connection after use
+    mHTTPDataSource->disconnect();
+
+    return err;
+}
+
+sp<M3UParser> HTTPDownloader::fetchPlaylist(
+        const char *url, uint8_t *curPlaylistHash, bool *unchanged) {
+    ALOGV("fetchPlaylist '%s'", url);
+
+    *unchanged = false;
+
+    sp<ABuffer> buffer;
+    String8 actualUrl;
+    ssize_t err = fetchFile(url, &buffer, &actualUrl);
+
+    // close off the connection after use
+    mHTTPDataSource->disconnect();
+
+    if (err <= 0) {
+        return NULL;
+    }
+
+    // MD5 functionality is not available on the simulator, treat all
+    // playlists as changed.
+
+#if defined(HAVE_ANDROID_OS)
+    uint8_t hash[16];
+
+    MD5_CTX m;
+    MD5_Init(&m);
+    MD5_Update(&m, buffer->data(), buffer->size());
+
+    MD5_Final(hash, &m);
+
+    if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {
+        // playlist unchanged
+        *unchanged = true;
+
+        return NULL;
+    }
+
+    if (curPlaylistHash != NULL) {
+        memcpy(curPlaylistHash, hash, sizeof(hash));
+    }
+#endif
+
+    sp<M3UParser> playlist =
+        new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
+
+    if (playlist->initCheck() != OK) {
+        ALOGE("failed to parse .m3u8 playlist");
+
+        return NULL;
+    }
+
+    return playlist;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/httplive/HTTPDownloader.h b/media/libstagefright/httplive/HTTPDownloader.h
new file mode 100644
index 0000000..1db4a48
--- /dev/null
+++ b/media/libstagefright/httplive/HTTPDownloader.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HTTP_DOWNLOADER_H_
+
+#define HTTP_DOWNLOADER_H_
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+class DataSource;
+struct HTTPBase;
+struct IMediaHTTPService;
+struct M3UParser;
+
+struct HTTPDownloader : public RefBase {
+    HTTPDownloader(
+            const sp<IMediaHTTPService> &httpService,
+            const KeyedVector<String8, String8> &headers);
+
+    void reconnect();
+    void disconnect();
+    bool isDisconnecting();
+    // If given a non-zero block_size (default 0), it is used to cap the number of
+    // bytes read in from the DataSource. If given a non-NULL buffer, new content
+    // is read into the end.
+    //
+    // The DataSource we read from is responsible for signaling error or EOF to help us
+    // break out of the read loop. The DataSource can be returned to the caller, so
+    // that the caller can reuse it for subsequent fetches (within the initially
+    // requested range).
+    //
+    // For reused HTTP sources, the caller must download a file sequentially without
+    // any overlaps or gaps to prevent reconnection.
+    ssize_t fetchBlock(
+            const char *url,
+            sp<ABuffer> *out,
+            int64_t range_offset, /* open file at range_offset */
+            int64_t range_length, /* open file for range_length (-1: entire file) */
+            uint32_t block_size,  /* download block size (0: entire range) */
+            String8 *actualUrl,   /* returns actual URL */
+            bool reconnect        /* force connect http */
+            );
+
+    // simplified version to fetch a single file
+    ssize_t fetchFile(
+            const char *url,
+            sp<ABuffer> *out,
+            String8 *actualUrl = NULL);
+
+    // fetch a playlist file
+    sp<M3UParser> fetchPlaylist(
+            const char *url, uint8_t *curPlaylistHash, bool *unchanged);
+
+private:
+    sp<HTTPBase> mHTTPDataSource;
+    sp<DataSource> mDataSource;
+    KeyedVector<String8, String8> mExtraHeaders;
+
+    Mutex mLock;
+    bool mDisconnecting;
+
+    DISALLOW_EVIL_CONSTRUCTORS(HTTPDownloader);
+};
+
+}  // namespace android
+
+#endif  // HTTP_DOWNLOADER_H_
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 2d93152..64a8532 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -19,25 +19,19 @@
 #include <utils/Log.h>
 
 #include "LiveSession.h"
-
+#include "HTTPDownloader.h"
 #include "M3UParser.h"
 #include "PlaylistFetcher.h"
 
-#include "include/HTTPBase.h"
 #include "mpeg2ts/AnotherPacketSource.h"
 
 #include <cutils/properties.h>
-#include <media/IMediaHTTPConnection.h>
 #include <media/IMediaHTTPService.h>
-#include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
 
@@ -45,8 +39,6 @@
 
 #include <ctype.h>
 #include <inttypes.h>
-#include <openssl/aes.h>
-#include <openssl/md5.h>
 
 namespace android {
 
@@ -66,12 +58,13 @@
     BandwidthEstimator();
 
     void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
-    bool estimateBandwidth(int32_t *bandwidth);
+    bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL);
 
 private:
     // Bandwidth estimation parameters
-    static const int32_t kMaxBandwidthHistoryItems = 20;
-    static const int64_t kMaxBandwidthHistoryWindowUs = 5000000ll; // 5 sec
+    static const int32_t kMinBandwidthHistoryItems = 20;
+    static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec
+    static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec
 
     struct BandwidthEntry {
         int64_t mDelayUs;
@@ -80,6 +73,9 @@
 
     Mutex mLock;
     List<BandwidthEntry> mBandwidthHistory;
+    List<int32_t> mPrevEstimates;
+    bool mHasNewSample;
+    bool mIsStable;
     int64_t mTotalTransferTimeUs;
     size_t mTotalTransferBytes;
 
@@ -87,6 +83,8 @@
 };
 
 LiveSession::BandwidthEstimator::BandwidthEstimator() :
+    mHasNewSample(false),
+    mIsStable(true),
     mTotalTransferTimeUs(0),
     mTotalTransferBytes(0) {
 }
@@ -101,12 +99,23 @@
     mTotalTransferTimeUs += delayUs;
     mTotalTransferBytes += numBytes;
     mBandwidthHistory.push_back(entry);
+    mHasNewSample = true;
 
+    // Remove no more than 10% of total transfer time at a time
+    // to avoid sudden jump on bandwidth estimation. There might
+    // be long blocking reads that takes up signification time,
+    // we have to keep a longer window in that case.
+    int64_t bandwidthHistoryWindowUs = mTotalTransferTimeUs * 9 / 10;
+    if (bandwidthHistoryWindowUs < kMinBandwidthHistoryWindowUs) {
+        bandwidthHistoryWindowUs = kMinBandwidthHistoryWindowUs;
+    } else if (bandwidthHistoryWindowUs > kMaxBandwidthHistoryWindowUs) {
+        bandwidthHistoryWindowUs = kMaxBandwidthHistoryWindowUs;
+    }
     // trim old samples, keeping at least kMaxBandwidthHistoryItems samples,
     // and total transfer time at least kMaxBandwidthHistoryWindowUs.
-    while (mBandwidthHistory.size() > kMaxBandwidthHistoryItems) {
+    while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) {
         List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
-        if (mTotalTransferTimeUs - it->mDelayUs < kMaxBandwidthHistoryWindowUs) {
+        if (mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) {
             break;
         }
         mTotalTransferTimeUs -= it->mDelayUs;
@@ -115,14 +124,67 @@
     }
 }
 
-bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) {
+bool LiveSession::BandwidthEstimator::estimateBandwidth(
+        int32_t *bandwidthBps, bool *isStable) {
     AutoMutex autoLock(mLock);
 
     if (mBandwidthHistory.size() < 2) {
         return false;
     }
 
+    if (!mHasNewSample) {
+        *bandwidthBps = *(--mPrevEstimates.end());
+        if (isStable) {
+            *isStable = mIsStable;
+        }
+        return true;
+    }
+
     *bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+    mPrevEstimates.push_back(*bandwidthBps);
+    while (mPrevEstimates.size() > 3) {
+        mPrevEstimates.erase(mPrevEstimates.begin());
+    }
+    mHasNewSample = false;
+
+    int32_t minEstimate = -1, maxEstimate = -1;
+    List<int32_t>::iterator it;
+    for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) {
+        int32_t estimate = *it;
+        if (minEstimate < 0 || minEstimate > estimate) {
+            minEstimate = estimate;
+        }
+        if (maxEstimate < 0 || maxEstimate < estimate) {
+            maxEstimate = estimate;
+        }
+    }
+    mIsStable = (maxEstimate <= minEstimate * 4 / 3);
+    if (isStable) {
+       *isStable = mIsStable;
+    }
+#if 0
+    {
+        char dumpStr[1024] = {0};
+        size_t itemIdx = 0;
+        size_t histSize = mBandwidthHistory.size();
+        sprintf(dumpStr, "estimate bps=%d stable=%d history (n=%d): {",
+            *bandwidthBps, mIsStable, histSize);
+        List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
+        for (; it != mBandwidthHistory.end(); ++it) {
+            if (itemIdx > 50) {
+                sprintf(dumpStr + strlen(dumpStr),
+                        "...(%zd more items)... }", histSize - itemIdx);
+                break;
+            }
+            sprintf(dumpStr + strlen(dumpStr), "%dk/%.3fs%s",
+                it->mNumBytes / 1024,
+                (double)it->mDelayUs * 1.0e-6,
+                (it == (--mBandwidthHistory.end())) ? "}" : ", ");
+            itemIdx++;
+        }
+        ALOGE(dumpStr);
+    }
+#endif
     return true;
 }
 
@@ -135,12 +197,47 @@
             return "timeUsAudio";
         case STREAMTYPE_SUBTITLES:
             return "timeUsSubtitle";
+        case STREAMTYPE_METADATA:
+            return "timeUsMetadata"; // unused
         default:
             TRESPASS();
     }
     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";
+        case STREAMTYPE_METADATA:
+            return "metadata";
+        default:
+            break;
+    }
+    return "unknown";
+}
+
+//static
+ATSParser::SourceType LiveSession::getSourceTypeForStream(StreamType type) {
+    switch (type) {
+        case STREAMTYPE_VIDEO:
+            return ATSParser::VIDEO;
+        case STREAMTYPE_AUDIO:
+            return ATSParser::AUDIO;
+        case STREAMTYPE_METADATA:
+            return ATSParser::META;
+        case STREAMTYPE_SUBTITLES:
+        default:
+            TRESPASS();
+    }
+    return ATSParser::NUM_SOURCE_TYPES; // should not reach here
+}
+
 LiveSession::LiveSession(
         const sp<AMessage> &notify, uint32_t flags,
         const sp<IMediaHTTPService> &httpService)
@@ -151,11 +248,12 @@
       mInPreparationPhase(true),
       mPollBufferingGeneration(0),
       mPrevBufferPercentage(-1),
-      mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
       mCurBandwidthIndex(-1),
       mOrigBandwidthIndex(-1),
       mLastBandwidthBps(-1ll),
       mBandwidthEstimator(new BandwidthEstimator()),
+      mMaxWidth(720),
+      mMaxHeight(480),
       mStreamMask(0),
       mNewStreamMask(0),
       mSwapMask(0),
@@ -170,12 +268,13 @@
       mUpSwitchMargin(kUpSwitchMarginUs),
       mFirstTimeUsValid(false),
       mFirstTimeUs(0),
-      mLastSeekTimeUs(0) {
+      mLastSeekTimeUs(0),
+      mHasMetadata(false) {
     mStreams[kAudioIndex] = StreamItem("audio");
     mStreams[kVideoIndex] = StreamItem("video");
     mStreams[kSubtitleIndex] = StreamItem("subtitles");
 
-    for (size_t i = 0; i < kMaxStreams; ++i) {
+    for (size_t i = 0; i < kNumSources; ++i) {
         mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
         mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
     }
@@ -187,12 +286,30 @@
     }
 }
 
+int64_t LiveSession::calculateMediaTimeUs(
+        int64_t firstTimeUs, int64_t timeUs, int32_t discontinuitySeq) {
+    if (timeUs >= firstTimeUs) {
+        timeUs -= firstTimeUs;
+    } else {
+        timeUs = 0;
+    }
+    timeUs += mLastSeekTimeUs;
+    if (mDiscontinuityOffsetTimesUs.indexOfKey(discontinuitySeq) >= 0) {
+        timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
+    }
+    return timeUs;
+}
+
 status_t LiveSession::dequeueAccessUnit(
         StreamType stream, sp<ABuffer> *accessUnit) {
     status_t finalResult = OK;
     sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
 
-    ssize_t idx = typeToIndex(stream);
+    ssize_t streamIdx = typeToIndex(stream);
+    if (streamIdx < 0) {
+        return BAD_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
@@ -200,6 +317,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 {
@@ -212,26 +332,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
         int32_t type;
@@ -249,9 +349,11 @@
     } else if (err == OK) {
 
         if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
-            int64_t timeUs;
+            int64_t timeUs, originalTimeUs;
             int32_t discontinuitySeq = 0;
+            StreamItem& strm = mStreams[streamIdx];
             CHECK((*accessUnit)->meta()->findInt64("timeUs",  &timeUs));
+            originalTimeUs = timeUs;
             (*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq);
             if (discontinuitySeq > (int32_t) strm.mCurDiscontinuitySeq) {
                 int64_t offsetTimeUs;
@@ -261,7 +363,8 @@
                     offsetTimeUs = 0;
                 }
 
-                if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
+                if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0
+                        && strm.mLastDequeuedTimeUs >= 0) {
                     int64_t firstTimeUs;
                     firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
                     offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs;
@@ -293,17 +396,10 @@
             }
 
             strm.mLastDequeuedTimeUs = timeUs;
-            if (timeUs >= firstTimeUs) {
-                timeUs -= firstTimeUs;
-            } else {
-                timeUs = 0;
-            }
-            timeUs += mLastSeekTimeUs;
-            if (mDiscontinuityOffsetTimesUs.indexOfKey(discontinuitySeq) >= 0) {
-                timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
-            }
+            timeUs = calculateMediaTimeUs(firstTimeUs, timeUs, 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;
@@ -316,6 +412,17 @@
             (*accessUnit)->meta()->setInt32(
                     "trackIndex", mPlaylist->getSelectedIndex());
             (*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
+        } else if (stream == STREAMTYPE_METADATA) {
+            HLSTime mdTime((*accessUnit)->meta());
+            if (mDiscontinuityAbsStartTimesUs.indexOfKey(mdTime.mSeq) < 0) {
+                packetSource->requeueAccessUnit((*accessUnit));
+                return -EAGAIN;
+            } else {
+                int64_t firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(mdTime.mSeq);
+                int64_t timeUs = calculateMediaTimeUs(firstTimeUs, mdTime.mTimeUs, mdTime.mSeq);
+                (*accessUnit)->meta()->setInt64("timeUs",  timeUs);
+                (*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
+            }
         }
     } else {
         ALOGI("[%s] encountered error %d", streamStr, err);
@@ -340,13 +447,16 @@
     if (stream == STREAMTYPE_AUDIO) {
         // set AAC input buffer size to 32K bytes (256kbps x 1sec)
         meta->setInt32(kKeyMaxInputSize, 32 * 1024);
+    } else if (stream == STREAMTYPE_VIDEO) {
+        meta->setInt32(kKeyMaxWidth, mMaxWidth);
+        meta->setInt32(kKeyMaxHeight, mMaxHeight);
     }
 
     return convertMetaDataToMessage(meta, format);
 }
 
-sp<HTTPBase> LiveSession::getHTTPDataSource() {
-    return new MediaHTTP(mHTTPService->makeHTTPConnection());
+sp<HTTPDownloader> LiveSession::getHTTPDownloader() {
+    return new HTTPDownloader(mHTTPService, mExtraHeaders);
 }
 
 void LiveSession::connectAsync(
@@ -409,7 +519,7 @@
             if (lastDequeueMeta == NULL) {
                 // this means we don't have enough cushion, try again later
                 ALOGV("[%s] up switching failed due to insufficient buffer",
-                        stream == STREAMTYPE_AUDIO ? "audio" : "video");
+                        getNameForStream(stream));
                 return false;
             }
         } else {
@@ -428,7 +538,7 @@
         if (firstNewMeta[i] == NULL) {
             HLSTime dequeueTime(lastDequeueMeta);
             ALOGV("[%s] dequeue time (%d, %lld) past start time",
-                    stream == STREAMTYPE_AUDIO ? "audio" : "video",
+                    getNameForStream(stream),
                     dequeueTime.mSeq, (long long) dequeueTime.mTimeUs);
             return false;
         }
@@ -493,16 +603,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;
         }
 
@@ -525,6 +634,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());
@@ -544,6 +658,7 @@
                         if (--mContinuationCounter == 0) {
                             mContinuation->post();
                         }
+                        ALOGV("%zu fetcher(s) left", mContinuationCounter);
                     }
                     break;
                 }
@@ -636,6 +751,9 @@
                     int32_t switchGeneration;
                     CHECK(msg->findInt32("switchGeneration", &switchGeneration));
 
+                    ALOGV("kWhatStartedAt: switchGen=%d, mSwitchGen=%d",
+                            switchGeneration, mSwitchGeneration);
+
                     if (switchGeneration != mSwitchGeneration) {
                         break;
                     }
@@ -667,6 +785,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) {
@@ -682,6 +801,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) {
@@ -693,8 +813,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 */);
                         }
                     }
@@ -706,6 +828,23 @@
                     break;
                 }
 
+                case PlaylistFetcher::kWhatPlaylistFetched:
+                {
+                    onMasterPlaylistFetched(msg);
+                    break;
+                }
+
+                case PlaylistFetcher::kWhatMetadataDetected:
+                {
+                    if (!mHasMetadata) {
+                        mHasMetadata = true;
+                        sp<AMessage> notify = mNotify->dup();
+                        notify->setInt32("what", kWhatMetadataDetected);
+                        notify->post();
+                    }
+                    break;
+                }
+
                 default:
                     TRESPASS();
             }
@@ -731,12 +870,6 @@
             break;
         }
 
-        case kWhatFinishDisconnect2:
-        {
-            onFinishDisconnect2();
-            break;
-        }
-
         case kWhatPollBuffering:
         {
             int32_t generation;
@@ -766,7 +899,7 @@
 
 // static
 LiveSession::StreamType LiveSession::indexToType(int idx) {
-    CHECK(idx >= 0 && idx < kMaxStreams);
+    CHECK(idx >= 0 && idx < kNumSources);
     return (StreamType)(1 << idx);
 }
 
@@ -779,6 +912,8 @@
             return 1;
         case STREAMTYPE_SUBTITLES:
             return 2;
+        case STREAMTYPE_METADATA:
+            return 3;
         default:
             return -1;
     };
@@ -786,8 +921,10 @@
 }
 
 void LiveSession::onConnect(const sp<AMessage> &msg) {
-    AString url;
-    CHECK(msg->findString("url", &url));
+    CHECK(msg->findString("url", &mMasterURL));
+
+    // TODO currently we don't know if we are coming here from incognito mode
+    ALOGI("onConnect %s", uriDebugString(mMasterURL).c_str());
 
     KeyedVector<String8, String8> *headers = NULL;
     if (!msg->findPointer("headers", (void **)&headers)) {
@@ -799,21 +936,6 @@
         headers = NULL;
     }
 
-    // TODO currently we don't know if we are coming here from incognito mode
-    ALOGI("onConnect %s", uriDebugString(url).c_str());
-
-    mMasterURL = url;
-
-    bool dummy;
-    mPlaylist = fetchPlaylist(url.c_str(), NULL /* curPlaylistHash */, &dummy);
-
-    if (mPlaylist == NULL) {
-        ALOGE("unable to fetch master playlist %s.", uriDebugString(url).c_str());
-
-        postPrepared(ERROR_IO);
-        return;
-    }
-
     // create looper for fetchers
     if (mFetcherLooper == NULL) {
         mFetcherLooper = new ALooper();
@@ -822,6 +944,31 @@
         mFetcherLooper->start(false, false);
     }
 
+    // create fetcher to fetch the master playlist
+    addFetcher(mMasterURL.c_str())->fetchPlaylistAsync();
+}
+
+void LiveSession::onMasterPlaylistFetched(const sp<AMessage> &msg) {
+    AString uri;
+    CHECK(msg->findString("uri", &uri));
+    ssize_t index = mFetcherInfos.indexOfKey(uri);
+    if (index < 0) {
+        ALOGW("fetcher for master playlist is gone.");
+        return;
+    }
+
+    // no longer useful, remove
+    mFetcherLooper->unregisterHandler(mFetcherInfos[index].mFetcher->id());
+    mFetcherInfos.removeItemsAt(index);
+
+    CHECK(msg->findObject("playlist", (sp<RefBase> *)&mPlaylist));
+    if (mPlaylist == NULL) {
+        ALOGE("unable to fetch master playlist %s.",
+                uriDebugString(mMasterURL).c_str());
+
+        postPrepared(ERROR_IO);
+        return;
+    }
     // We trust the content provider to make a reasonable choice of preferred
     // initial bandwidth by listing it first in the variant playlist.
     // At startup we really don't have a good estimate on the available
@@ -830,6 +977,9 @@
     size_t initialBandwidth = 0;
     size_t initialBandwidthIndex = 0;
 
+    int32_t maxWidth = 0;
+    int32_t maxHeight = 0;
+
     if (mPlaylist->isVariantPlaylist()) {
         Vector<BandwidthItem> itemsWithVideo;
         for (size_t i = 0; i < mPlaylist->size(); ++i) {
@@ -843,6 +993,14 @@
 
             CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
 
+            int32_t width, height;
+            if (meta->findInt32("width", &width)) {
+                maxWidth = max(maxWidth, width);
+            }
+            if (meta->findInt32("height", &height)) {
+                maxHeight = max(maxHeight, height);
+            }
+
             mBandwidthItems.push(item);
             if (mPlaylist->hasType(i, "video")) {
                 itemsWithVideo.push(item);
@@ -876,6 +1034,9 @@
         mBandwidthItems.push(item);
     }
 
+    mMaxWidth = maxWidth > 0 ? maxWidth : mMaxWidth;
+    mMaxHeight = maxHeight > 0 ? maxHeight : mMaxHeight;
+
     mPlaylist->pickRandomMediaItems();
     changeConfiguration(
             0ll /* timeUs */, initialBandwidthIndex, false /* pickTrack */);
@@ -891,22 +1052,26 @@
     // cancel buffer polling
     cancelPollBuffering();
 
+    // TRICKY: don't wait for all fetcher to be stopped when disconnecting
+    //
+    // Some fetchers might be stuck in connect/getSize at this point. These
+    // operations will eventually timeout (as we have a timeout set in
+    // MediaHTTPConnection), but we don't want to block the main UI thread
+    // until then. Here we just need to make sure we clear all references
+    // to the fetchers, so that when they finally exit from the blocking
+    // operation, they can be destructed.
+    //
+    // There is one very tricky point though. For this scheme to work, the
+    // fecther must hold a reference to LiveSession, so that LiveSession is
+    // destroyed after fetcher. Otherwise LiveSession would get stuck in its
+    // own destructor when it waits for mFetcherLooper to stop, which still
+    // blocks main UI thread.
     for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
         mFetcherInfos.valueAt(i).mFetcher->stopAsync();
+        mFetcherLooper->unregisterHandler(
+                mFetcherInfos.valueAt(i).mFetcher->id());
     }
-
-    sp<AMessage> msg = new AMessage(kWhatFinishDisconnect2, this);
-
-    mContinuationCounter = mFetcherInfos.size();
-    mContinuation = msg;
-
-    if (mContinuationCounter == 0) {
-        msg->post();
-    }
-}
-
-void LiveSession::onFinishDisconnect2() {
-    mContinuation.clear();
+    mFetcherInfos.clear();
 
     mPacketSources.valueFor(STREAMTYPE_AUDIO)->signalEOS(ERROR_END_OF_STREAM);
     mPacketSources.valueFor(STREAMTYPE_VIDEO)->signalEOS(ERROR_END_OF_STREAM);
@@ -933,7 +1098,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;
@@ -944,204 +1110,51 @@
     return info.mFetcher;
 }
 
-/*
- * Illustration of parameters:
- *
- * 0      `range_offset`
- * +------------+-------------------------------------------------------+--+--+
- * |            |                                 | next block to fetch |  |  |
- * |            | `source` handle => `out` buffer |                     |  |  |
- * | `url` file |<--------- buffer size --------->|<--- `block_size` -->|  |  |
- * |            |<----------- `range_length` / buffer capacity ----------->|  |
- * |<------------------------------ file_size ------------------------------->|
- *
- * Special parameter values:
- * - range_length == -1 means entire file
- * - block_size == 0 means entire range
- *
- */
-ssize_t LiveSession::fetchFile(
-        const char *url, sp<ABuffer> *out,
-        int64_t range_offset, int64_t range_length,
-        uint32_t block_size, /* download block size */
-        sp<DataSource> *source, /* to return and reuse source */
-        String8 *actualUrl,
-        bool forceConnectHTTP /* force connect HTTP when resuing source */) {
-    off64_t size;
-    sp<DataSource> temp_source;
-    if (source == NULL) {
-        source = &temp_source;
-    }
-
-    if (*source == NULL || forceConnectHTTP) {
-        if (!strncasecmp(url, "file://", 7)) {
-            *source = new FileSource(url + 7);
-        } else if (strncasecmp(url, "http://", 7)
-                && strncasecmp(url, "https://", 8)) {
-            return ERROR_UNSUPPORTED;
-        } else {
-            KeyedVector<String8, String8> headers = mExtraHeaders;
-            if (range_offset > 0 || range_length >= 0) {
-                headers.add(
-                        String8("Range"),
-                        String8(
-                            AStringPrintf(
-                                "bytes=%lld-%s",
-                                range_offset,
-                                range_length < 0
-                                    ? "" : AStringPrintf("%lld",
-                                            range_offset + range_length - 1).c_str()).c_str()));
-            }
-
-            HTTPBase* httpDataSource =
-                    (*source == NULL) ? mHTTPDataSource.get() : (HTTPBase*)source->get();
-            status_t err = httpDataSource->connect(url, &headers);
-
-            if (err != OK) {
-                return err;
-            }
-
-            if (*source == NULL) {
-                *source = mHTTPDataSource;
-            }
-        }
-    }
-
-    status_t getSizeErr = (*source)->getSize(&size);
-    if (getSizeErr != OK) {
-        size = 65536;
-    }
-
-    sp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);
-    if (*out == NULL) {
-        buffer->setRange(0, 0);
-    }
-
-    ssize_t bytesRead = 0;
-    // adjust range_length if only reading partial block
-    if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {
-        range_length = buffer->size() + block_size;
-    }
-    for (;;) {
-        // Only resize when we don't know the size.
-        size_t bufferRemaining = buffer->capacity() - buffer->size();
-        if (bufferRemaining == 0 && getSizeErr != OK) {
-            size_t bufferIncrement = buffer->size() / 2;
-            if (bufferIncrement < 32768) {
-                bufferIncrement = 32768;
-            }
-            bufferRemaining = bufferIncrement;
-
-            ALOGV("increasing download buffer to %zu bytes",
-                 buffer->size() + bufferRemaining);
-
-            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
-            memcpy(copy->data(), buffer->data(), buffer->size());
-            copy->setRange(0, buffer->size());
-
-            buffer = copy;
-        }
-
-        size_t maxBytesToRead = bufferRemaining;
-        if (range_length >= 0) {
-            int64_t bytesLeftInRange = range_length - buffer->size();
-            if (bytesLeftInRange < (int64_t)maxBytesToRead) {
-                maxBytesToRead = bytesLeftInRange;
-
-                if (bytesLeftInRange == 0) {
-                    break;
-                }
-            }
-        }
-
-        // The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)
-        // to help us break out of the loop.
-        ssize_t n = (*source)->readAt(
-                buffer->size(), buffer->data() + buffer->size(),
-                maxBytesToRead);
-
-        if (n < 0) {
-            return n;
-        }
-
-        if (n == 0) {
-            break;
-        }
-
-        buffer->setRange(0, buffer->size() + (size_t)n);
-        bytesRead += n;
-    }
-
-    *out = buffer;
-    if (actualUrl != NULL) {
-        *actualUrl = (*source)->getUri();
-        if (actualUrl->isEmpty()) {
-            *actualUrl = url;
-        }
-    }
-
-    return bytesRead;
-}
-
-sp<M3UParser> LiveSession::fetchPlaylist(
-        const char *url, uint8_t *curPlaylistHash, bool *unchanged) {
-    ALOGV("fetchPlaylist '%s'", url);
-
-    *unchanged = false;
-
-    sp<ABuffer> buffer;
-    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;
-    }
-
-    // MD5 functionality is not available on the simulator, treat all
-    // playlists as changed.
-
-#if defined(HAVE_ANDROID_OS)
-    uint8_t hash[16];
-
-    MD5_CTX m;
-    MD5_Init(&m);
-    MD5_Update(&m, buffer->data(), buffer->size());
-
-    MD5_Final(hash, &m);
-
-    if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {
-        // playlist unchanged
-        *unchanged = true;
-
-        return NULL;
-    }
-
-    if (curPlaylistHash != NULL) {
-        memcpy(curPlaylistHash, hash, sizeof(hash));
-    }
-#endif
-
-    sp<M3UParser> playlist =
-        new M3UParser(actualUrl.string(), buffer->data(), buffer->size());
-
-    if (playlist->initCheck() != OK) {
-        ALOGE("failed to parse .m3u8 playlist");
-
-        return NULL;
-    }
-
-    return playlist;
-}
-
 #if 0
 static double uniformRand() {
     return (double)rand() / RAND_MAX;
 }
 #endif
 
+bool LiveSession::UriIsSameAsIndex(const AString &uri, int32_t i, bool newUri) {
+    ALOGI("[timed_id3] i %d UriIsSameAsIndex newUri %s, %s", i,
+            newUri ? "true" : "false",
+            newUri ? mStreams[i].mNewUri.c_str() : mStreams[i].mUri.c_str());
+    return i >= 0
+            && ((!newUri && uri == mStreams[i].mUri)
+            || (newUri && uri == mStreams[i].mNewUri));
+}
+
+sp<AnotherPacketSource> LiveSession::getPacketSourceForStreamIndex(
+        size_t trackIndex, bool newUri) {
+    StreamType type = indexToType(trackIndex);
+    sp<AnotherPacketSource> source = NULL;
+    if (newUri) {
+        source = mPacketSources2.valueFor(type);
+        source->clear();
+    } else {
+        source = mPacketSources.valueFor(type);
+    };
+    return source;
+}
+
+sp<AnotherPacketSource> LiveSession::getMetadataSource(
+        sp<AnotherPacketSource> sources[kNumSources], uint32_t streamMask, bool newUri) {
+    // todo: One case where the following strategy can fail is when audio and video
+    // are in separate playlists, both are transport streams, and the metadata
+    // is actually contained in the audio stream.
+    ALOGV("[timed_id3] getMetadataSourceForUri streamMask %x newUri %s",
+            streamMask, newUri ? "true" : "false");
+
+    if ((sources[kVideoIndex] != NULL) // video fetcher; or ...
+            || (!(streamMask & STREAMTYPE_VIDEO) && sources[kAudioIndex] != NULL)) {
+            // ... audio fetcher for audio only variant
+        return getPacketSourceForStreamIndex(kMetaDataIndex, newUri);
+    }
+
+    return NULL;
+}
+
 bool LiveSession::resumeFetcher(
         const AString &uri, uint32_t streamMask, int64_t timeUs, bool newUri) {
     ssize_t index = mFetcherInfos.indexOfKey(uri);
@@ -1151,28 +1164,26 @@
     }
 
     bool resume = false;
-    sp<AnotherPacketSource> sources[kMaxStreams];
+    sp<AnotherPacketSource> sources[kNumSources];
     for (size_t i = 0; i < kMaxStreams; ++i) {
-        if ((streamMask & indexToType(i))
-            && ((!newUri && uri == mStreams[i].mUri)
-            || (newUri && uri == mStreams[i].mNewUri))) {
+        if ((streamMask & indexToType(i)) && UriIsSameAsIndex(uri, i, newUri)) {
             resume = true;
-            if (newUri) {
-                sources[i] = mPacketSources2.valueFor(indexToType(i));
-                sources[i]->clear();
-            } else {
-                sources[i] = mPacketSources.valueFor(indexToType(i));
-            }
+            sources[i] = getPacketSourceForStreamIndex(i, newUri);
         }
     }
 
     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],
+                getMetadataSource(sources, streamMask, newUri),
                 timeUs, -1, -1, seekMode);
     }
 
@@ -1349,16 +1360,10 @@
     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 {
@@ -1389,7 +1394,7 @@
     if (mPlaylist == NULL) {
         return 0;
     } else {
-        return mPlaylist->getTrackCount();
+        return mPlaylist->getTrackCount() + (mHasMetadata ? 1 : 0);
     }
 }
 
@@ -1397,6 +1402,13 @@
     if (mPlaylist == NULL) {
         return NULL;
     } else {
+        if (trackIndex == mPlaylist->getTrackCount() && mHasMetadata) {
+            sp<AMessage> format = new AMessage();
+            format->setInt32("type", MEDIA_TRACK_TYPE_METADATA);
+            format->setString("language", "und");
+            format->setString("mime", MEDIA_MIMETYPE_DATA_METADATA);
+            return format;
+        }
         return mPlaylist->getTrackInfo(trackIndex);
     }
 }
@@ -1406,6 +1418,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) {
@@ -1426,6 +1441,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);
@@ -1433,6 +1451,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);
@@ -1478,21 +1500,23 @@
         }
 
         if (discardFetcher) {
+            ALOGV("discarding fetcher-%d", fetcher->getFetcherID());
             fetcher->stopAsync();
         } else {
-            float threshold = -1.0f; // always finish fetching by default
+            float threshold = 0.0f; // default to pause after current block (47Kbytes)
+            bool disconnect = false;
             if (timeUs >= 0ll) {
                 // seeking, no need to finish fetching
-                threshold = 0.0f;
+                disconnect = true;
             } else if (delayRemoval) {
                 // adapting, abort if remaining of current segment is over threshold
                 threshold = getAbortThreshold(
                         mOrigBandwidthIndex, mCurBandwidthIndex);
             }
 
-            ALOGV("Pausing with threshold %.3f", threshold);
-
-            fetcher->pauseAsync(threshold);
+            ALOGV("pausing fetcher-%d, threshold=%.2f",
+                    fetcher->getFetcherID(), threshold);
+            fetcher->pauseAsync(threshold, disconnect);
         }
     }
 
@@ -1526,6 +1550,8 @@
 }
 
 void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
+    ALOGV("onChangeConfiguration");
+
     if (!mReconfigurationInProgress) {
         int32_t pickTrack = 0;
         msg->findInt32("pickTrack", &pickTrack);
@@ -1536,6 +1562,8 @@
 }
 
 void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
+    ALOGV("onChangeConfiguration2");
+
     mContinuation.clear();
 
     // All fetchers are either suspended or have been removed now.
@@ -1547,13 +1575,14 @@
 
     if (timeUs >= 0) {
         mLastSeekTimeUs = timeUs;
+        mLastDequeuedTimeUs = timeUs;
 
         for (size_t i = 0; i < mPacketSources.size(); i++) {
             mPacketSources.editValueAt(i)->clear();
         }
 
         for (size_t i = 0; i < kMaxStreams; ++i) {
-            mStreams[i].mCurDiscontinuitySeq = 0;
+            mStreams[i].reset();
         }
 
         mDiscontinuityOffsetTimesUs.clear();
@@ -1599,8 +1628,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
@@ -1660,16 +1691,17 @@
             // and resume audio.
             mSwapMask =  mNewStreamMask & mStreamMask & ~resumeMask;
             switching = (mSwapMask != 0);
-            if (!switching) {
-                ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd",
-                        mOrigBandwidthIndex, mCurBandwidthIndex);
-            }
         }
         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) {
@@ -1687,6 +1719,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;
         }
     }
@@ -1711,7 +1746,7 @@
 
         HLSTime startTime;
         SeekMode seekMode = kSeekModeExactPosition;
-        sp<AnotherPacketSource> sources[kMaxStreams];
+        sp<AnotherPacketSource> sources[kNumSources];
 
         if (i == kSubtitleIndex || (!pickTrack && !switching)) {
             startTime = latestMediaSegmentStartTime();
@@ -1740,8 +1775,8 @@
                         }
                     }
 
-                    if (j != kSubtitleIndex && meta != NULL
-                            && !meta->findInt32("discontinuity", &type)) {
+                    if ((j == kAudioIndex || j == kVideoIndex)
+                            && meta != NULL && !meta->findInt32("discontinuity", &type)) {
                         HLSTime tmpTime(meta);
                         if (startTime < tmpTime) {
                             startTime = tmpTime;
@@ -1776,6 +1811,14 @@
             }
         }
 
+        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(),
+                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
@@ -1786,8 +1829,9 @@
                 sources[kAudioIndex],
                 sources[kVideoIndex],
                 sources[kSubtitleIndex],
+                getMetadataSource(sources, mNewStreamMask, switching),
                 startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs,
-                startTime.getSegmentTimeUs(true /* midpoint */),
+                startTime.getSegmentTimeUs(),
                 startTime.mSeq,
                 seekMode);
     }
@@ -1795,22 +1839,28 @@
     // All fetchers have now been started, the configuration change
     // has completed.
 
-    ALOGV("XXX configuration change completed.");
     mReconfigurationInProgress = false;
     if (switching) {
         mSwitchInProgress = true;
     } 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);
@@ -1858,7 +1908,7 @@
 
     mFetcherInfos.editValueAt(index).mFetcher->stopAsync(false /* clear */);
 
-    ALOGV("tryToFinishBandwidthSwitch: mSwapMask=%x", mSwapMask);
+    ALOGV("tryToFinishBandwidthSwitch: mSwapMask=0x%x", mSwapMask);
     if (mSwapMask != 0) {
         return;
     }
@@ -1925,11 +1975,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();
@@ -1937,8 +1995,7 @@
                 startBufferingIfNecessary();
             }
             switchBandwidthIfNeeded(up, down);
-       }
-
+        }
     }
 
     schedulePollBuffering();
@@ -1983,7 +2040,7 @@
     }
 
     ALOGI("#### Canceled Bandwidth Switch: %zd => %zd",
-            mCurBandwidthIndex, mOrigBandwidthIndex);
+            mOrigBandwidthIndex, mCurBandwidthIndex);
 
     mSwitchGeneration++;
     mSwitchInProgress = false;
@@ -2020,15 +2077,19 @@
             continue;
         }
 
+        status_t finalResult;
         int64_t bufferedDurationUs =
-                mPacketSources[i]->getEstimatedDurationUs();
-        ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
+                mPacketSources[i]->getBufferedDurationUs(&finalResult);
+        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;
@@ -2111,47 +2172,58 @@
     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;
-    if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) {
+    bool isStable;
+    if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) {
         ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
         mLastBandwidthBps = bandwidthBps;
     } else {
         ALOGV("no bandwidth estimate.");
-        return;
+        return false;
     }
 
     int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
     // 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
+    bool canSwitchDown = bufferLow
             && (bandwidthBps < (int32_t)curBandwidth);
     bool canSwitchUp = bufferHigh
             && (bandwidthBps > (int32_t)curBandwidth * 12 / 10);
 
-    if (canSwithDown || canSwitchUp) {
+    if (canSwitchDown || canSwitchUp) {
+        // bandwidth estimating has some delay, if we have to downswitch when
+        // it hasn't stabilized, be very conservative on bandwidth.
+        if (!isStable && canSwitchDown) {
+            bandwidthBps /= 2;
+        }
+
         ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
 
         // 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 (bandwidthIndex == mCurBandwidthIndex
-                || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex)
-                || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) {
-            return;
+        if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+         || (canSwitchDown && 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 b5e31c9..21be413 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -23,18 +23,21 @@
 
 #include <utils/String8.h>
 
+#include "mpeg2ts/ATSParser.h"
+
 namespace android {
 
 struct ABuffer;
 struct AReplyToken;
 struct AnotherPacketSource;
-struct DataSource;
+class DataSource;
 struct HTTPBase;
 struct IMediaHTTPService;
 struct LiveDataSource;
 struct M3UParser;
 struct PlaylistFetcher;
 struct HLSTime;
+struct HTTPDownloader;
 
 struct LiveSession : public AHandler {
     enum Flags {
@@ -47,12 +50,15 @@
         kVideoIndex    = 1,
         kSubtitleIndex = 2,
         kMaxStreams    = 3,
+        kMetaDataIndex = 3,
+        kNumSources    = 4,
     };
 
     enum StreamType {
         STREAMTYPE_AUDIO        = 1 << kAudioIndex,
         STREAMTYPE_VIDEO        = 1 << kVideoIndex,
         STREAMTYPE_SUBTITLES    = 1 << kSubtitleIndex,
+        STREAMTYPE_METADATA     = 1 << kMetaDataIndex,
     };
 
     enum SeekMode {
@@ -66,11 +72,12 @@
             uint32_t flags,
             const sp<IMediaHTTPService> &httpService);
 
+    int64_t calculateMediaTimeUs(int64_t firstTimeUs, int64_t timeUs, int32_t discontinuitySeq);
     status_t dequeueAccessUnit(StreamType stream, sp<ABuffer> *accessUnit);
 
     status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
 
-    sp<HTTPBase> getHTTPDataSource();
+    sp<HTTPDownloader> getHTTPDownloader();
 
     void connectAsync(
             const char *url,
@@ -91,6 +98,8 @@
     bool hasDynamicDuration() const;
 
     static const char *getKeyForStream(StreamType type);
+    static const char *getNameForStream(StreamType type);
+    static ATSParser::SourceType getSourceTypeForStream(StreamType type);
 
     enum {
         kWhatStreamsChanged,
@@ -100,6 +109,7 @@
         kWhatBufferingStart,
         kWhatBufferingEnd,
         kWhatBufferingUpdate,
+        kWhatMetadataDetected,
     };
 
 protected:
@@ -118,7 +128,6 @@
         kWhatChangeConfiguration        = 'chC0',
         kWhatChangeConfiguration2       = 'chC2',
         kWhatChangeConfiguration3       = 'chC3',
-        kWhatFinishDisconnect2          = 'fin2',
         kWhatPollBuffering              = 'poll',
     };
 
@@ -157,10 +166,14 @@
             : StreamItem("") {}
         StreamItem(const char *type)
             : mType(type),
-              mSeekMode(kSeekModeExactPosition),
-              mCurDiscontinuitySeq(0),
-              mLastDequeuedTimeUs(0),
-              mLastSampleDurationUs(0) {}
+              mSeekMode(kSeekModeExactPosition) {
+                  reset();
+              }
+        void reset() {
+            mCurDiscontinuitySeq = 0;
+            mLastDequeuedTimeUs = -1ll;
+            mLastSampleDurationUs = 0ll;
+        }
         AString uriKey() {
             AString key(mType);
             key.append("URI");
@@ -178,7 +191,6 @@
     int32_t mPollBufferingGeneration;
     int32_t mPrevBufferPercentage;
 
-    sp<HTTPBase> mHTTPDataSource;
     KeyedVector<String8, String8> mExtraHeaders;
 
     AString mMasterURL;
@@ -190,6 +202,8 @@
     sp<BandwidthEstimator> mBandwidthEstimator;
 
     sp<M3UParser> mPlaylist;
+    int32_t mMaxWidth;
+    int32_t mMaxHeight;
 
     sp<ALooper> mFetcherLooper;
     KeyedVector<AString, FetcherInfo> mFetcherInfos;
@@ -230,40 +244,21 @@
     bool mFirstTimeUsValid;
     int64_t mFirstTimeUs;
     int64_t mLastSeekTimeUs;
+    bool mHasMetadata;
+
     KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs;
     KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs;
 
     sp<PlaylistFetcher> addFetcher(const char *uri);
 
     void onConnect(const sp<AMessage> &msg);
-    status_t onSeek(const sp<AMessage> &msg);
-    void onFinishDisconnect2();
+    void onMasterPlaylistFetched(const sp<AMessage> &msg);
+    void onSeek(const sp<AMessage> &msg);
 
-    // If given a non-zero block_size (default 0), it is used to cap the number of
-    // bytes read in from the DataSource. If given a non-NULL buffer, new content
-    // is read into the end.
-    //
-    // The DataSource we read from is responsible for signaling error or EOF to help us
-    // break out of the read loop. The DataSource can be returned to the caller, so
-    // that the caller can reuse it for subsequent fetches (within the initially
-    // requested range).
-    //
-    // For reused HTTP sources, the caller must download a file sequentially without
-    // any overlaps or gaps to prevent reconnection.
-    ssize_t fetchFile(
-            const char *url, sp<ABuffer> *out,
-            /* request/open a file starting at range_offset for range_length bytes */
-            int64_t range_offset = 0, int64_t range_length = -1,
-            /* download block size */
-            uint32_t block_size = 0,
-            /* reuse DataSource if doing partial fetch */
-            sp<DataSource> *source = NULL,
-            String8 *actualUrl = NULL,
-            /* force connect http even when resuing DataSource */
-            bool forceConnectHTTP = false);
-
-    sp<M3UParser> fetchPlaylist(
-            const char *url, uint8_t *curPlaylistHash, bool *unchanged);
+    bool UriIsSameAsIndex( const AString &uri, int32_t index, bool newUri);
+    sp<AnotherPacketSource> getPacketSourceForStreamIndex(size_t trackIndex, bool newUri);
+    sp<AnotherPacketSource> getMetadataSource(
+            sp<AnotherPacketSource> sources[kNumSources], uint32_t streamMask, bool newUri);
 
     bool resumeFetcher(
             const AString &uri, uint32_t streamMask,
@@ -291,7 +286,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 7bb7f2c..ff2bb27 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -250,6 +250,9 @@
       mIsVariantPlaylist(false),
       mIsComplete(false),
       mIsEvent(false),
+      mFirstSeqNumber(-1),
+      mLastSeqNumber(-1),
+      mTargetDurationUs(-1ll),
       mDiscontinuitySeq(0),
       mDiscontinuityCount(0),
       mSelectedIndex(-1) {
@@ -283,6 +286,19 @@
     return mDiscontinuitySeq;
 }
 
+int64_t M3UParser::getTargetDuration() const {
+    return mTargetDurationUs;
+}
+
+int32_t M3UParser::getFirstSeqNumber() const {
+    return mFirstSeqNumber;
+}
+
+void M3UParser::getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const {
+    *firstSeq = mFirstSeqNumber;
+    *lastSeq = mLastSeqNumber;
+}
+
 sp<AMessage> M3UParser::meta() {
     return mMeta;
 }
@@ -664,11 +680,22 @@
     }
 
     // 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;
+    // (currently only checking "target-duration"), and
+    // initialization of playlist properties (eg. mTargetDurationUs)
+    if (!mIsVariantPlaylist) {
+        int32_t targetDurationSecs;
+        if (mMeta == NULL || !mMeta->findInt32(
+                "target-duration", &targetDurationSecs)) {
+            ALOGE("Media playlist missing #EXT-X-TARGETDURATION");
+            return ERROR_MALFORMED;
+        }
+        mTargetDurationUs = targetDurationSecs * 1000000ll;
+
+        mFirstSeqNumber = 0;
+        if (mMeta != NULL) {
+            mMeta->findInt32("media-sequence", &mFirstSeqNumber);
+        }
+        mLastSeqNumber = mFirstSeqNumber + mItems.size() - 1;
     }
 
     return OK;
@@ -808,6 +835,29 @@
                 *meta = new AMessage;
             }
             (*meta)->setString(key.c_str(), codecs.c_str());
+        } else if (!strcasecmp("resolution", key.c_str())) {
+            const char *s = val.c_str();
+            char *end;
+            unsigned long width = strtoul(s, &end, 10);
+
+            if (end == s || *end != 'x') {
+                // malformed
+                continue;
+            }
+
+            s = end + 1;
+            unsigned long height = strtoul(s, &end, 10);
+
+            if (end == s || *end != '\0') {
+                // malformed
+                continue;
+            }
+
+            if (meta->get() == NULL) {
+                *meta = new AMessage;
+            }
+            (*meta)->setInt32("width", width);
+            (*meta)->setInt32("height", height);
         } else if (!strcasecmp("audio", key.c_str())
                 || !strcasecmp("video", key.c_str())
                 || !strcasecmp("subtitles", key.c_str())) {
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index fef361f..fa648ed 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -36,6 +36,9 @@
     bool isComplete() const;
     bool isEvent() const;
     size_t getDiscontinuitySeq() const;
+    int64_t getTargetDuration() const;
+    int32_t getFirstSeqNumber() const;
+    void getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const;
 
     sp<AMessage> meta();
 
@@ -70,6 +73,9 @@
     bool mIsVariantPlaylist;
     bool mIsComplete;
     bool mIsEvent;
+    int32_t mFirstSeqNumber;
+    int32_t mLastSeqNumber;
+    int64_t mTargetDurationUs;
     size_t mDiscontinuitySeq;
     int32_t mDiscontinuityCount;
 
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 368612d..4851528 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -17,25 +17,19 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "PlaylistFetcher"
 #include <utils/Log.h>
+#include <utils/misc.h>
 
 #include "PlaylistFetcher.h"
-
-#include "LiveDataSource.h"
+#include "HTTPDownloader.h"
 #include "LiveSession.h"
 #include "M3UParser.h"
-
 #include "include/avc_utils.h"
-#include "include/HTTPBase.h"
 #include "include/ID3.h"
 #include "mpeg2ts/AnotherPacketSource.h"
 
-#include <media/IStreamSource.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AUtils.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
@@ -43,7 +37,10 @@
 #include <ctype.h>
 #include <inttypes.h>
 #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 {
 
@@ -143,16 +140,19 @@
         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),
       mDiscontinuitySeq(-1ll),
       mStartTimeUsRelative(false),
       mLastPlaylistFetchTimeUs(-1ll),
+      mPlaylistTimeUs(-1ll),
       mSeqNumber(-1),
       mNumRetries(0),
       mStartup(true),
@@ -168,25 +168,25 @@
       mFirstTimeUs(-1ll),
       mVideoBuffer(new AnotherPacketSource(NULL)),
       mThresholdRatio(-1.0f),
-      mDownloadState(new DownloadState()) {
+      mDownloadState(new DownloadState()),
+      mHasMetadata(false) {
     memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
-    mHTTPDataSource = mSession->getHTTPDataSource();
+    mHTTPDownloader = mSession->getHTTPDownloader();
 }
 
 PlaylistFetcher::~PlaylistFetcher() {
 }
 
+int32_t PlaylistFetcher::getFetcherID() const {
+    return mFetcherID;
+}
+
 int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const {
     CHECK(mPlaylist != NULL);
 
-    int32_t firstSeqNumberInPlaylist;
-    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
-                "media-sequence", &firstSeqNumberInPlaylist)) {
-        firstSeqNumberInPlaylist = 0;
-    }
-
-    int32_t lastSeqNumberInPlaylist =
-        firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+    int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+    mPlaylist->getSeqNumberRange(
+            &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
 
     CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
     CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
@@ -210,14 +210,9 @@
 int64_t PlaylistFetcher::getSegmentDurationUs(int32_t seqNumber) const {
     CHECK(mPlaylist != NULL);
 
-    int32_t firstSeqNumberInPlaylist;
-    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
-                "media-sequence", &firstSeqNumberInPlaylist)) {
-        firstSeqNumberInPlaylist = 0;
-    }
-
-    int32_t lastSeqNumberInPlaylist =
-        firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+    int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+    mPlaylist->getSeqNumberRange(
+            &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
 
     CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
     CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
@@ -245,10 +240,7 @@
         return (~0llu >> 1);
     }
 
-    int32_t targetDurationSecs;
-    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-
-    int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+    int64_t targetDurationUs = mPlaylist->getTargetDuration();
 
     int64_t minPlaylistAgeUs;
 
@@ -338,9 +330,11 @@
     if (index >= 0) {
         key = mAESKeyForURI.valueAt(index);
     } else {
-        ssize_t err = mSession->fetchFile(keyURI.c_str(), &key);
+        ssize_t err = mHTTPDownloader->fetchFile(keyURI.c_str(), &key);
 
-        if (err < 0) {
+        if (err == ERROR_NOT_CONNECTED) {
+            return ERROR_NOT_CONNECTED;
+        } else if (err < 0) {
             ALOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
             return ERROR_IO;
         } else if (key->size() != 16) {
@@ -436,7 +430,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);
@@ -448,18 +442,39 @@
     ++mMonitorQueueGeneration;
 }
 
-void PlaylistFetcher::setStoppingThreshold(float thresholdRatio) {
-    AutoMutex _l(mThresholdLock);
-    if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
-        return;
+void PlaylistFetcher::setStoppingThreshold(float thresholdRatio, bool disconnect) {
+    {
+        AutoMutex _l(mThresholdLock);
+        mThresholdRatio = thresholdRatio;
     }
-    mThresholdRatio = thresholdRatio;
+    if (disconnect) {
+        mHTTPDownloader->disconnect();
+    }
+}
+
+void PlaylistFetcher::resetStoppingThreshold(bool disconnect) {
+    {
+        AutoMutex _l(mThresholdLock);
+        mThresholdRatio = -1.0f;
+    }
+    if (disconnect) {
+        mHTTPDownloader->disconnect();
+    } else {
+        // allow reconnect
+        mHTTPDownloader->reconnect();
+    }
+}
+
+float PlaylistFetcher::getStoppingThreshold() {
+    AutoMutex _l(mThresholdLock);
+    return mThresholdRatio;
 }
 
 void PlaylistFetcher::startAsync(
         const sp<AnotherPacketSource> &audioSource,
         const sp<AnotherPacketSource> &videoSource,
         const sp<AnotherPacketSource> &subtitleSource,
+        const sp<AnotherPacketSource> &metadataSource,
         int64_t startTimeUs,
         int64_t segmentStartTimeUs,
         int32_t startDiscontinuitySeq,
@@ -483,6 +498,11 @@
         streamTypeMask |= LiveSession::STREAMTYPE_SUBTITLES;
     }
 
+    if (metadataSource != NULL) {
+        msg->setPointer("metadataSource", metadataSource.get());
+        // metadataSource does not affect streamTypeMask.
+    }
+
     msg->setInt32("streamTypeMask", streamTypeMask);
     msg->setInt64("startTimeUs", startTimeUs);
     msg->setInt64("segmentStartTimeUs", segmentStartTimeUs);
@@ -491,15 +511,15 @@
     msg->post();
 }
 
-void PlaylistFetcher::pauseAsync(float thresholdRatio) {
-    if (thresholdRatio >= 0.0f) {
-        setStoppingThreshold(thresholdRatio);
-    }
+void PlaylistFetcher::pauseAsync(
+        float thresholdRatio, bool disconnect) {
+    setStoppingThreshold(thresholdRatio, disconnect);
+
     (new AMessage(kWhatPause, this))->post();
 }
 
 void PlaylistFetcher::stopAsync(bool clear) {
-    setStoppingThreshold(0.0f);
+    setStoppingThreshold(0.0f, true /* disconncect */);
 
     sp<AMessage> msg = new AMessage(kWhatStop, this);
     msg->setInt32("clear", clear);
@@ -507,11 +527,17 @@
 }
 
 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();
 }
 
+void PlaylistFetcher::fetchPlaylistAsync() {
+    (new AMessage(kWhatFetchPlaylist, this))->post();
+}
+
 void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatStart:
@@ -549,6 +575,19 @@
             break;
         }
 
+        case kWhatFetchPlaylist:
+        {
+            bool unchanged;
+            sp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(
+                    mURI.c_str(), NULL /* curPlaylistHash */, &unchanged);
+
+            sp<AMessage> notify = mNotify->dup();
+            notify->setInt32("what", kWhatPlaylistFetched);
+            notify->setObject("playlist", playlist);
+            notify->post();
+            break;
+        }
+
         case kWhatMonitorQueue:
         case kWhatDownloadNext:
         {
@@ -625,6 +664,15 @@
                 static_cast<AnotherPacketSource *>(ptr));
     }
 
+    void *ptr;
+    // metadataSource is not part of streamTypeMask
+    if ((streamTypeMask & (LiveSession::STREAMTYPE_AUDIO | LiveSession::STREAMTYPE_VIDEO))
+            && msg->findPointer("metadataSource", &ptr)) {
+        mPacketSources.add(
+                LiveSession::STREAMTYPE_METADATA,
+                static_cast<AnotherPacketSource *>(ptr));
+    }
+
     mStreamTypeMask = streamTypeMask;
 
     mSegmentStartTimeUs = segmentStartTimeUs;
@@ -659,7 +707,7 @@
     cancelMonitorQueue();
     mLastDiscontinuitySeq = mDiscontinuitySeq;
 
-    setStoppingThreshold(-1.0f);
+    resetStoppingThreshold(false /* disconnect */);
 }
 
 void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
@@ -674,14 +722,11 @@
         }
     }
 
-    // close off the connection after use
-    mHTTPDataSource->disconnect();
-
     mDownloadState->resetState();
     mPacketSources.clear();
     mStreamTypeMask = 0;
 
-    setStoppingThreshold(-1.0f);
+    resetStoppingThreshold(true /* disconnect */);
 }
 
 // Resume until we have reached the boundary timestamps listed in `msg`; when
@@ -721,24 +766,15 @@
 }
 
 void PlaylistFetcher::onMonitorQueue() {
-    bool downloadMore = false;
-
     // in the middle of an unfinished download, delay
     // playlist refresh as it'll change seq numbers
     if (!mDownloadState->hasSavedState()) {
         refreshPlaylist();
     }
 
-    int32_t targetDurationSecs;
     int64_t targetDurationUs = kMinBufferedDurationUs;
     if (mPlaylist != NULL) {
-        if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
-                "target-duration", &targetDurationSecs)) {
-            ALOGE("Playlist is missing required EXT-X-TARGETDURATION tag");
-            notifyError(ERROR_MALFORMED);
-            return;
-        }
-        targetDurationUs = targetDurationSecs * 1000000ll;
+        targetDurationUs = mPlaylist->getTargetDuration();
     }
 
     int64_t bufferedDurationUs = 0ll;
@@ -763,8 +799,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;
@@ -776,8 +813,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();
@@ -792,8 +830,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);
     }
 }
@@ -801,7 +843,7 @@
 status_t PlaylistFetcher::refreshPlaylist() {
     if (delayUsToRefreshPlaylist() <= 0) {
         bool unchanged;
-        sp<M3UParser> playlist = mSession->fetchPlaylist(
+        sp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(
                 mURI.c_str(), mPlaylistHash, &unchanged);
 
         if (playlist == NULL) {
@@ -830,6 +872,7 @@
             if (!mPlaylist->isComplete()) {
                 updateTargetDuration();
             }
+            mPlaylistTimeUs = ALooper::GetNowUs();
         }
 
         mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
@@ -849,20 +892,12 @@
     }
 
     // Calculate threshold to abort current download
-    int32_t targetDurationSecs;
-    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-    int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-    int64_t thresholdUs = -1;
-    {
-        AutoMutex _l(mThresholdLock);
-        thresholdUs = (mThresholdRatio < 0.0f) ?
-                -1ll : mThresholdRatio * targetDurationUs;
-    }
+    float thresholdRatio = getStoppingThreshold();
 
-    if (thresholdUs < 0) {
+    if (thresholdRatio < 0.0f) {
         // never abort
         return false;
-    } else if (thresholdUs == 0) {
+    } else if (thresholdRatio == 0.0f) {
         // immediately abort
         return true;
     }
@@ -891,6 +926,15 @@
         }
     }
     lastEnqueueUs -= mSegmentFirstPTS;
+
+    int64_t targetDurationUs = mPlaylist->getTargetDuration();
+    int64_t thresholdUs = thresholdRatio * targetDurationUs;
+
+    FLOGV("%spausing now, thresholdUs %lld, remaining %lld",
+            targetDurationUs - lastEnqueueUs > thresholdUs ? "" : "not ",
+            (long long)thresholdUs,
+            (long long)(targetDurationUs - lastEnqueueUs));
+
     if (targetDurationUs - lastEnqueueUs > thresholdUs) {
         return true;
     }
@@ -908,12 +952,8 @@
     bool discontinuity = false;
 
     if (mPlaylist != NULL) {
-        if (mPlaylist->meta() != NULL) {
-            mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist);
-        }
-
-        lastSeqNumberInPlaylist =
-                firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+        mPlaylist->getSeqNumberRange(
+                &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
 
         if (mDiscontinuitySeq < 0) {
             mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
@@ -940,19 +980,26 @@
                 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
             // to media time 0) is used to determine the start segment; mStartTimeUs (absolute
             // timestamps coming from the media container) is used to determine the position
             // inside a segments.
-            mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
             if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES
                     && mSeekMode != LiveSession::kSeekModeNextSample) {
                 // avoid double fetch/decode
-                mSeqNumber += 1;
+                // Use (mSegmentStartTimeUs + 1/2 * targetDurationUs) to search
+                // for the starting segment in new variant.
+                // If the two variants' segments are aligned, this gives the
+                // next segment. If they're not aligned, this gives the segment
+                // that overlaps no more than 1/2 * targetDurationUs.
+                mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs
+                        + mPlaylist->getTargetDuration() / 2);
+            } else {
+                mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
             }
             ssize_t minSeq = getSeqNumberForDiscontinuity(mDiscontinuitySeq);
             if (mSeqNumber < minSeq) {
@@ -966,7 +1013,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);
         }
@@ -986,19 +1033,17 @@
                 // refresh in increasing fraction (1/2, 1/3, ...) of the
                 // playlist's target duration or 3 seconds, whichever is less
                 int64_t delayUs = kMaxMonitorDelayUs;
-                if (mPlaylist != NULL && mPlaylist->meta() != NULL) {
-                    int32_t targetDurationSecs;
-                    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-                    delayUs = mPlaylist->size() * targetDurationSecs *
-                            1000000ll / (1 + mNumRetries);
+                if (mPlaylist != NULL) {
+                    delayUs = mPlaylist->size() * mPlaylist->getTargetDuration()
+                            / (1 + mNumRetries);
                 }
                 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;
             }
@@ -1037,6 +1082,16 @@
                      mSeqNumber, firstSeqNumberInPlaylist,
                       firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1);
 
+                if (mTSParser != NULL) {
+                    mTSParser->signalEOS(ERROR_END_OF_STREAM);
+                    // Use an empty buffer; we don't have any new data, just want to extract
+                    // potential new access units after flush.  Reset mSeqNumber to
+                    // lastSeqNumberInPlaylist such that we set the correct access unit
+                    // properties in extractAndQueueAccessUnitsFromTs.
+                    sp<ABuffer> buffer = new ABuffer(0);
+                    mSeqNumber = lastSeqNumberInPlaylist;
+                    extractAndQueueAccessUnitsFromTs(buffer);
+                }
                 notifyError(ERROR_END_OF_STREAM);
             } else {
                 // It's possible that we were never able to download the playlist.
@@ -1067,9 +1122,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;
@@ -1081,7 +1136,9 @@
         junk->setRange(0, 16);
         status_t err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, junk,
                 true /* first */);
-        if (err != OK) {
+        if (err == ERROR_NOT_CONNECTED) {
+            return false;
+        } else if (err != OK) {
             notifyError(err);
             return false;
         }
@@ -1117,7 +1174,7 @@
         }
 
         queueDiscontinuity(
-                ATSParser::DISCONTINUITY_FORMATCHANGE,
+                ATSParser::DISCONTINUITY_FORMAT_ONLY,
                 NULL /* extra */);
 
         if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
@@ -1131,10 +1188,12 @@
             // set mStartTimeUs=0, and take all samples from now on.
             mStartTimeUs = 0;
             mFirstPTSValid = false;
+            mIDRFound = false;
+            mVideoBuffer->clear();
         }
     }
 
-    ALOGV("fetching segment %d from (%d .. %d)",
+    FLOGV("fetching segment %d from (%d .. %d)",
             mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
     return true;
 }
@@ -1157,7 +1216,7 @@
                 firstSeqNumberInPlaylist,
                 lastSeqNumberInPlaylist);
         connectHTTP = false;
-        ALOGV("resuming: '%s'", uri.c_str());
+        FLOGV("resuming: '%s'", uri.c_str());
     } else {
         if (!initDownloadState(
                 uri,
@@ -1166,7 +1225,7 @@
                 lastSeqNumberInPlaylist)) {
             return;
         }
-        ALOGV("fetching: '%s'", uri.c_str());
+        FLOGV("fetching: '%s'", uri.c_str());
     }
 
     int64_t range_offset, range_length;
@@ -1180,12 +1239,21 @@
     bool shouldPause = false;
     ssize_t bytesRead;
     do {
-        sp<DataSource> source = mHTTPDataSource;
-
         int64_t startUs = ALooper::GetNowUs();
-        bytesRead = mSession->fetchFile(
+        bytesRead = mHTTPDownloader->fetchBlock(
                 uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize,
-                &source, NULL, connectHTTP);
+                NULL /* actualURL */, connectHTTP);
+        int64_t delayUs = ALooper::GetNowUs() - startUs;
+
+        if (bytesRead == ERROR_NOT_CONNECTED) {
+            return;
+        }
+        if (bytesRead < 0) {
+            status_t err = bytesRead;
+            ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
+            notifyError(err);
+            return;
+        }
 
         // add sample for bandwidth estimation, excluding samples from subtitles (as
         // its too small), or during startup/resumeUntil (when we could have more than
@@ -1194,19 +1262,15 @@
                 && (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;
 
-        if (bytesRead < 0) {
-            status_t err = bytesRead;
-            ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
-            notifyError(err);
-            return;
-        }
-
         CHECK(buffer != NULL);
 
         size_t size = buffer->size();
@@ -1286,11 +1350,11 @@
     if (bufferStartsWithTsSyncByte(buffer)) {
         // If we don't see a stream in the program table after fetching a full ts segment
         // mark it as nonexistent.
-        const size_t kNumTypes = ATSParser::NUM_SOURCE_TYPES;
-        ATSParser::SourceType srcTypes[kNumTypes] =
+        ATSParser::SourceType srcTypes[] =
                 { ATSParser::VIDEO, ATSParser::AUDIO };
-        LiveSession::StreamType streamTypes[kNumTypes] =
+        LiveSession::StreamType streamTypes[] =
                 { LiveSession::STREAMTYPE_VIDEO, LiveSession::STREAMTYPE_AUDIO };
+        const size_t kNumTypes = NELEM(srcTypes);
 
         for (size_t i = 0; i < kNumTypes; i++) {
             ATSParser::SourceType srcType = srcTypes[i];
@@ -1362,42 +1426,73 @@
     }
 }
 
-int32_t PlaylistFetcher::getSeqNumberWithAnchorTime(
-        int64_t anchorTimeUs, int64_t targetDiffUs) const {
-    int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
-    if (mPlaylist->meta() == NULL
-            || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
-        firstSeqNumberInPlaylist = 0;
-    }
-    lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1;
+/*
+ * returns true if we need to adjust mSeqNumber
+ */
+bool PlaylistFetcher::adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs) {
+    int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
 
-    int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1;
-    // adjust anchorTimeUs to within targetDiffUs from mStartTimeUs
-    while (index >= 0 && anchorTimeUs - mStartTimeUs > targetDiffUs) {
-        sp<AMessage> itemMeta;
-        CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
-
-        int64_t itemDurationUs;
-        CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
-
-        anchorTimeUs -= itemDurationUs;
-        --index;
-    }
-
-    int32_t newSeqNumber = firstSeqNumberInPlaylist + index + 1;
-    if (newSeqNumber <= lastSeqNumberInPlaylist) {
-        return newSeqNumber;
+    int64_t minDiffUs, maxDiffUs;
+    if (mSeekMode == LiveSession::kSeekModeNextSample) {
+        // if the previous fetcher paused in the middle of a segment, we
+        // want to start at a segment that overlaps the last sample
+        minDiffUs = -mPlaylist->getTargetDuration();
+        maxDiffUs = 0ll;
     } else {
-        return lastSeqNumberInPlaylist;
+        // if the previous fetcher paused at the end of a segment, ideally
+        // we want to start at the segment that's roughly aligned with its
+        // next segment, but if the two variants are not well aligned we
+        // adjust the diff to within (-T/2, T/2)
+        minDiffUs = -mPlaylist->getTargetDuration() / 2;
+        maxDiffUs = mPlaylist->getTargetDuration() / 2;
     }
+
+    int32_t oldSeqNumber = mSeqNumber;
+    ssize_t index = mSeqNumber - firstSeqNumberInPlaylist;
+
+    // adjust anchorTimeUs to within (minDiffUs, maxDiffUs) from mStartTimeUs
+    int64_t diffUs = anchorTimeUs - mStartTimeUs;
+    if (diffUs > maxDiffUs) {
+        while (index > 0 && diffUs > maxDiffUs) {
+            --index;
+
+            sp<AMessage> itemMeta;
+            CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+            int64_t itemDurationUs;
+            CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+            diffUs -= itemDurationUs;
+        }
+    } else if (diffUs < minDiffUs) {
+        while (index + 1 < (ssize_t) mPlaylist->size()
+                && diffUs < minDiffUs) {
+            ++index;
+
+            sp<AMessage> itemMeta;
+            CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+            int64_t itemDurationUs;
+            CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+            diffUs += itemDurationUs;
+        }
+    }
+
+    mSeqNumber = firstSeqNumberInPlaylist + index;
+
+    if (mSeqNumber != oldSeqNumber) {
+        FLOGV("guessed wrong seg number: diff %lld out of [%lld, %lld]",
+                (long long) anchorTimeUs - mStartTimeUs,
+                (long long) minDiffUs,
+                (long long) maxDiffUs);
+        return true;
+    }
+    return false;
 }
 
 int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const {
-    int32_t firstSeqNumberInPlaylist;
-    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
-                "media-sequence", &firstSeqNumberInPlaylist)) {
-        firstSeqNumberInPlaylist = 0;
-    }
+    int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
 
     size_t index = 0;
     while (index < mPlaylist->size()) {
@@ -1419,12 +1514,6 @@
 }
 
 int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const {
-    int32_t firstSeqNumberInPlaylist;
-    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
-                "media-sequence", &firstSeqNumberInPlaylist)) {
-        firstSeqNumberInPlaylist = 0;
-    }
-
     size_t index = 0;
     int64_t segmentStartUs = 0;
     while (index < mPlaylist->size()) {
@@ -1447,7 +1536,7 @@
         index = mPlaylist->size() - 1;
     }
 
-    return firstSeqNumberInPlaylist + index;
+    return mPlaylist->getFirstSeqNumber() + index;
 }
 
 const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties(
@@ -1462,17 +1551,37 @@
         accessUnit->meta()->setInt32("discard", discard);
     }
 
-    int32_t targetDurationSecs;
-    if (mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)) {
-        accessUnit->meta()->setInt32("targetDuration", targetDurationSecs);
-    }
-
     accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
     accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
+    accessUnit->meta()->setInt64("segmentFirstTimeUs", mSegmentFirstPTS);
     accessUnit->meta()->setInt64("segmentDurationUs", getSegmentDurationUs(mSeqNumber));
+    if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) {
+        accessUnit->meta()->setInt64("playlistTimeUs", mPlaylistTimeUs);
+    }
     return accessUnit;
 }
 
+bool PlaylistFetcher::isStartTimeReached(int64_t timeUs) {
+    if (!mFirstPTSValid) {
+        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);
+    }
+    return startTimeReached;
+}
+
 status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer) {
     if (mTSParser == NULL) {
         // Use TS_TIMESTAMPS_ARE_ABSOLUTE so pts carry over between fetchers.
@@ -1510,6 +1619,62 @@
     // setRange to indicate consumed bytes.
     buffer->setRange(buffer->offset() + offset, buffer->size() - offset);
 
+    if (mSegmentFirstPTS < 0ll) {
+        // get the smallest first PTS from all streams present in this parser
+        for (size_t i = mPacketSources.size(); i-- > 0;) {
+            const LiveSession::StreamType stream = mPacketSources.keyAt(i);
+            if (stream == LiveSession::STREAMTYPE_SUBTITLES) {
+                ALOGE("MPEG2 Transport streams do not contain subtitles.");
+                return ERROR_MALFORMED;
+            }
+            if (stream == LiveSession::STREAMTYPE_METADATA) {
+                continue;
+            }
+            ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream);
+            sp<AnotherPacketSource> source =
+                static_cast<AnotherPacketSource *>(
+                        mTSParser->getSource(type).get());
+
+            if (source == NULL) {
+                continue;
+            }
+            sp<AMessage> meta = source->getMetaAfterLastDequeued(0);
+            if (meta != NULL) {
+                int64_t timeUs;
+                CHECK(meta->findInt64("timeUs", &timeUs));
+                if (mSegmentFirstPTS < 0ll || timeUs < mSegmentFirstPTS) {
+                    mSegmentFirstPTS = timeUs;
+                }
+            }
+        }
+        if (mSegmentFirstPTS < 0ll) {
+            // didn't find any TS packet, can return early
+            return OK;
+        }
+        if (!mStartTimeUsRelative) {
+            // mStartup
+            //   mStartup is true until we have queued a packet for all the streams
+            //   we are fetching. We queue packets whose timestamps are greater than
+            //   mStartTimeUs.
+            // mSegmentStartTimeUs >= 0
+            //   mSegmentStartTimeUs is non-negative when adapting or switching tracks
+            // adjustSeqNumberWithAnchorTime(timeUs) == true
+            //   we guessed a seq number that's either too large or too small.
+            // If this happens, we'll adjust mSeqNumber and restart fetching from new
+            // location. Note that we only want to adjust once, so set mSegmentStartTimeUs
+            // to -1 so that we don't enter this chunk next time.
+            if (mStartup && mSegmentStartTimeUs >= 0
+                    && adjustSeqNumberWithAnchorTime(mSegmentFirstPTS)) {
+                mStartTimeUsNotify = mNotify->dup();
+                mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+                mStartTimeUsNotify->setString("uri", mURI);
+                mIDRFound = false;
+                mSegmentStartTimeUs = -1;
+                return -EAGAIN;
+            }
+        }
+    }
+
     status_t err = OK;
     for (size_t i = mPacketSources.size(); i-- > 0;) {
         sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
@@ -1519,10 +1684,9 @@
             ALOGE("MPEG2 Transport streams do not contain subtitles.");
             return ERROR_MALFORMED;
         }
+
         const char *key = LiveSession::getKeyForStream(stream);
-        ATSParser::SourceType type =
-                (stream == LiveSession::STREAMTYPE_AUDIO) ?
-                        ATSParser::AUDIO : ATSParser::VIDEO;
+        ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream);
 
         sp<AnotherPacketSource> source =
             static_cast<AnotherPacketSource *>(
@@ -1545,82 +1709,26 @@
             int64_t timeUs;
             CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
 
-            if (mSegmentFirstPTS < 0ll) {
-                mSegmentFirstPTS = timeUs;
-                if (!mStartTimeUsRelative) {
-                    int32_t firstSeqNumberInPlaylist;
-                    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
-                                "media-sequence", &firstSeqNumberInPlaylist)) {
-                        firstSeqNumberInPlaylist = 0;
-                    }
-
-                    int32_t targetDurationSecs;
-                    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-                    int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-                    // mStartup
-                    //   mStartup is true until we have queued a packet for all the streams
-                    //   we are fetching. We queue packets whose timestamps are greater than
-                    //   mStartTimeUs.
-                    // mSegmentStartTimeUs >= 0
-                    //   mSegmentStartTimeUs is non-negative when adapting or switching tracks
-                    // mSeqNumber > firstSeqNumberInPlaylist
-                    //   don't decrement mSeqNumber if it already points to the 1st segment
-                    // timeUs - mStartTimeUs > targetDurationUs:
-                    //   This and the 2 above conditions should only happen when adapting in a live
-                    //   stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher
-                    //   would start fetching after timeUs, which should be greater than mStartTimeUs;
-                    //   the old fetcher would then continue fetching data until timeUs. We don't want
-                    //   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
-                            ? 0 : targetDurationUs);
-                    if (mStartup && mSegmentStartTimeUs >= 0
-                            && mSeqNumber > firstSeqNumberInPlaylist
-                            && timeUs - mStartTimeUs > targetDiffUs) {
-                        // we just guessed a starting timestamp that is too high when adapting in a
-                        // live stream; re-adjust based on the actual timestamp extracted from the
-                        // media segment; if we didn't move backward after the re-adjustment
-                        // (newSeqNumber), start at least 1 segment prior.
-                        int32_t newSeqNumber = getSeqNumberWithAnchorTime(
-                                timeUs, targetDiffUs);
-                        if (newSeqNumber >= mSeqNumber) {
-                            --mSeqNumber;
-                        } else {
-                            mSeqNumber = newSeqNumber;
-                        }
-                        mStartTimeUsNotify = mNotify->dup();
-                        mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
-                        mStartTimeUsNotify->setString("uri", mURI);
-                        mIDRFound = false;
-                        return -EAGAIN;
-                    }
-                }
-            }
             if (mStartup) {
-                if (!mFirstPTSValid) {
-                    mFirstTimeUs = timeUs;
-                    mFirstPTSValid = true;
-                }
-                bool startTimeReached = true;
-                if (mStartTimeUsRelative) {
-                    timeUs -= mFirstTimeUs;
-                    if (timeUs < 0) {
-                        timeUs = 0;
-                    }
-                    startTimeReached = (timeUs >= mStartTimeUs);
-                }
+                bool startTimeReached = isStartTimeReached(timeUs);
 
                 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)=%lld, mIDRFound=%d",
+                            (long long)timeUs,
+                            (long long)mStartTimeUs,
+                            (long long)timeUs - mStartTimeUs,
+                            mIDRFound);
                     if (isAvc) {
                         if (IsIDR(accessUnit)) {
                             mVideoBuffer->clear();
+                            FSLOGV(stream, "found IDR, clear mVideoBuffer");
                             mIDRFound = true;
                         }
                         if (mIDRFound && mStartTimeUsRelative && !startTimeReached) {
                             mVideoBuffer->queueAccessUnit(accessUnit);
+                            FSLOGV(stream, "saving AVC video AccessUnit");
                         }
                     }
                     if (!startTimeReached || (isAvc && !mIDRFound)) {
@@ -1632,18 +1740,21 @@
             if (mStartTimeUsNotify != NULL) {
                 uint32_t streamMask = 0;
                 mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
-                if (!(streamMask & mPacketSources.keyAt(i))) {
+                if ((mStreamTypeMask & mPacketSources.keyAt(i))
+                        && !(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)
@@ -1651,13 +1762,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;
@@ -1666,11 +1777,21 @@
                     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);
                 }
+            } else if (stream == LiveSession::STREAMTYPE_METADATA && !mHasMetadata) {
+                mHasMetadata = true;
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kWhatMetadataDetected);
+                notify->post();
             }
 
             setAccessUnitProperties(accessUnit, source);
             packetSource->queueAccessUnit(accessUnit);
+            FSLOGV(stream, "queueAccessUnit, timeUs=%lld", (long long)timeUs);
         }
 
         if (err != OK) {
@@ -1688,7 +1809,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;
     }
 
@@ -1739,7 +1860,6 @@
         buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
         buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
         buffer->meta()->setInt32("subtitleGeneration", mSubtitleGeneration);
-
         packetSource->queueAccessUnit(buffer);
         return OK;
     }
@@ -1850,6 +1970,18 @@
         mFirstTimeUs = timeUs;
     }
 
+    if (mSegmentFirstPTS < 0ll) {
+        mSegmentFirstPTS = timeUs;
+        if (!mStartTimeUsRelative) {
+            // Duplicated logic from how we handle .ts playlists.
+            if (mStartup && mSegmentStartTimeUs >= 0
+                    && adjustSeqNumberWithAnchorTime(timeUs)) {
+                mSegmentStartTimeUs = -1;
+                return -EAGAIN;
+            }
+        }
+    }
+
     size_t offset = 0;
     while (offset < buffer->size()) {
         const uint8_t *adtsHeader = buffer->data() + offset;
@@ -1893,32 +2025,12 @@
             }
 
             if (mStartTimeUsNotify != NULL) {
-                int32_t targetDurationSecs;
-                CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-                int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
-                int64_t targetDiffUs =(mSeekMode == LiveSession::kSeekModeNextSample
-                        ? 0 : targetDurationUs);
-                // Duplicated logic from how we handle .ts playlists.
-                if (mStartup && mSegmentStartTimeUs >= 0
-                        && timeUs - mStartTimeUs > targetDiffUs) {
-                    int32_t newSeqNumber = getSeqNumberWithAnchorTime(
-                            timeUs, targetDiffUs);
-                    if (newSeqNumber >= mSeqNumber) {
-                        --mSeqNumber;
-                    } else {
-                        mSeqNumber = newSeqNumber;
-                    }
-                    return -EAGAIN;
-                }
-
                 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)
@@ -1962,13 +2074,9 @@
 }
 
 void PlaylistFetcher::updateTargetDuration() {
-    int32_t targetDurationSecs;
-    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-    int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
     sp<AMessage> msg = mNotify->dup();
     msg->setInt32("what", kWhatTargetDurationUpdate);
-    msg->setInt64("targetDurationUs", targetDurationUs);
+    msg->setInt64("targetDurationUs", mPlaylist->getTargetDuration());
     msg->post();
 }
 
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index dab56df..c8ca457 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -27,7 +27,7 @@
 
 struct ABuffer;
 struct AnotherPacketSource;
-struct DataSource;
+class DataSource;
 struct HTTPBase;
 struct LiveDataSource;
 struct M3UParser;
@@ -49,32 +49,38 @@
         kWhatPreparationFailed,
         kWhatStartedAt,
         kWhatStopReached,
+        kWhatPlaylistFetched,
+        kWhatMetadataDetected,
     };
 
     PlaylistFetcher(
             const sp<AMessage> &notify,
             const sp<LiveSession> &session,
             const char *uri,
+            int32_t id,
             int32_t subtitleGeneration);
 
-    sp<DataSource> getDataSource();
+    int32_t getFetcherID() const;
 
     void startAsync(
             const sp<AnotherPacketSource> &audioSource,
             const sp<AnotherPacketSource> &videoSource,
             const sp<AnotherPacketSource> &subtitleSource,
+            const sp<AnotherPacketSource> &metadataSource,
             int64_t startTimeUs = -1ll,         // starting timestamps
             int64_t segmentStartTimeUs = -1ll, // starting position within playlist
             // startTimeUs!=segmentStartTimeUs only when playlist is live
             int32_t startDiscontinuitySeq = -1,
             LiveSession::SeekMode seekMode = LiveSession::kSeekModeExactPosition);
 
-    void pauseAsync(float thresholdRatio);
+    void pauseAsync(float thresholdRatio, bool disconnect);
 
     void stopAsync(bool clear = true);
 
     void resumeUntilAsync(const sp<AMessage> &params);
 
+    void fetchPlaylistAsync();
+
     uint32_t getStreamTypeMask() const {
         return mStreamTypeMask;
     }
@@ -95,6 +101,7 @@
         kWhatMonitorQueue   = 'moni',
         kWhatResumeUntil    = 'rsme',
         kWhatDownloadNext   = 'dlnx',
+        kWhatFetchPlaylist  = 'flst'
     };
 
     struct DownloadState;
@@ -109,10 +116,12 @@
     sp<AMessage> mNotify;
     sp<AMessage> mStartTimeUsNotify;
 
-    sp<HTTPBase> mHTTPDataSource;
+    sp<HTTPDownloader> mHTTPDownloader;
     sp<LiveSession> mSession;
     AString mURI;
 
+    int32_t mFetcherID;
+
     uint32_t mStreamTypeMask;
     int64_t mStartTimeUs;
 
@@ -131,6 +140,7 @@
     KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
 
     int64_t mLastPlaylistFetchTimeUs;
+    int64_t mPlaylistTimeUs;
     sp<M3UParser> mPlaylist;
     int32_t mSeqNumber;
     int32_t mNumRetries;
@@ -172,6 +182,8 @@
 
     sp<DownloadState> mDownloadState;
 
+    bool mHasMetadata;
+
     // Set first to true if decrypting the first segment of a playlist segment. When
     // first is true, reset the initialization vector based on the available
     // information in the manifest; otherwise, use the initialization vector as
@@ -187,7 +199,9 @@
 
     void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
     void cancelMonitorQueue();
-    void setStoppingThreshold(float thresholdRatio);
+    void setStoppingThreshold(float thresholdRatio, bool disconnect);
+    void resetStoppingThreshold(bool disconnect);
+    float getStoppingThreshold();
     bool shouldPauseDownload();
 
     int64_t delayUsToRefreshPlaylist() const;
@@ -217,6 +231,7 @@
             const sp<ABuffer> &accessUnit,
             const sp<AnotherPacketSource> &source,
             bool discard = false);
+    bool isStartTimeReached(int64_t timeUs);
     status_t extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &buffer);
 
     status_t extractAndQueueAccessUnits(
@@ -228,8 +243,7 @@
     void queueDiscontinuity(
             ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
 
-    int32_t getSeqNumberWithAnchorTime(
-            int64_t anchorTimeUs, int64_t targetDurationUs) const;
+    bool adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs);
     int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const;
     int32_t getSeqNumberForTime(int64_t timeUs) const;
 
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 2194c38..68bd017 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -4,7 +4,8 @@
 LOCAL_SRC_FILES := \
 	ID3.cpp
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE := libstagefright_id3
 
@@ -17,7 +18,8 @@
 LOCAL_SRC_FILES := \
 	testid3.cpp
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_SHARED_LIBRARIES := \
 	libstagefright libutils liblog libbinder libstagefright_foundation
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 77d65e0..758b2c9 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -21,6 +21,7 @@
 #include "HTTPBase.h"
 #include "TimedEventQueue.h"
 
+#include <media/AudioResamplerPublic.h>
 #include <media/MediaPlayerInterface.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/OMXClient.h>
@@ -31,20 +32,20 @@
 
 namespace android {
 
-struct AudioPlayer;
+class AudioPlayer;
 struct ClockEstimator;
-struct DataSource;
-struct MediaBuffer;
+class IDataSource;
+class MediaBuffer;
 struct MediaExtractor;
 struct MediaSource;
 struct NuCachedSource2;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
 
 class DrmManagerClinet;
 class DecryptHandle;
 
 class TimedTextDriver;
-struct WVMExtractor;
+class WVMExtractor;
 
 struct AwesomeRenderer : public RefBase {
     AwesomeRenderer() {}
@@ -93,6 +94,8 @@
 
     status_t setParameter(int key, const Parcel &request);
     status_t getParameter(int key, Parcel *reply);
+    status_t setPlaybackSettings(const AudioPlaybackRate &rate);
+    status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
     status_t invoke(const Parcel &request, Parcel *reply);
     status_t setCacheStatCollectFreq(const Parcel &request);
 
@@ -180,6 +183,7 @@
     sp<MediaSource> mOmxSource;
     sp<MediaSource> mAudioSource;
     AudioPlayer *mAudioPlayer;
+    AudioPlaybackRate mPlaybackSettings;
     int64_t mDurationUs;
 
     int32_t mDisplayWidth;
diff --git a/media/libstagefright/include/CallbackDataSource.h b/media/libstagefright/include/CallbackDataSource.h
new file mode 100644
index 0000000..1a21dd3
--- /dev/null
+++ b/media/libstagefright/include/CallbackDataSource.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CALLBACKDATASOURCE_H
+#define ANDROID_CALLBACKDATASOURCE_H
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+class IDataSource;
+class IMemory;
+
+// A stagefright DataSource that wraps a binder IDataSource. It's a "Callback"
+// DataSource because it calls back to the IDataSource for data.
+class CallbackDataSource : public DataSource {
+public:
+    CallbackDataSource(const sp<IDataSource>& iDataSource);
+    virtual ~CallbackDataSource();
+
+    // DataSource implementation.
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+    virtual status_t getSize(off64_t *size);
+
+private:
+    sp<IDataSource> mIDataSource;
+    sp<IMemory> mMemory;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CallbackDataSource);
+};
+
+
+// A caching DataSource that wraps a CallbackDataSource. For reads smaller
+// than kCacheSize it will read up to kCacheSize ahead and cache it.
+// This reduces the number of binder round trips to the IDataSource and has a significant
+// impact on time taken for filetype sniffing and metadata extraction.
+class TinyCacheSource : public DataSource {
+public:
+    TinyCacheSource(const sp<DataSource>& source);
+
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off64_t offset, void* data, size_t size);
+    virtual status_t getSize(off64_t* size);
+    virtual uint32_t flags();
+
+private:
+    // 2kb comes from experimenting with the time-to-first-frame from a MediaPlayer
+    // with an in-memory MediaDataSource source on a Nexus 5. Beyond 2kb there was
+    // no improvement.
+    enum {
+        kCacheSize = 2048,
+    };
+
+    sp<DataSource> mSource;
+    uint8_t mCache[kCacheSize];
+    off64_t mCachedOffset;
+    size_t mCachedSize;
+
+    DISALLOW_EVIL_CONSTRUCTORS(TinyCacheSource);
+};
+
+}; // namespace android
+
+#endif // ANDROID_CALLBACKDATASOURCE_H
diff --git a/media/libstagefright/include/MPEG2PSExtractor.h b/media/libstagefright/include/MPEG2PSExtractor.h
index fb76564..22cb02d 100644
--- a/media/libstagefright/include/MPEG2PSExtractor.h
+++ b/media/libstagefright/include/MPEG2PSExtractor.h
@@ -28,7 +28,7 @@
 struct ABuffer;
 struct AMessage;
 struct Track;
-struct String8;
+class String8;
 
 struct MPEG2PSExtractor : public MediaExtractor {
     MPEG2PSExtractor(const sp<DataSource> &source);
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index db1187d..4dd340c 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -30,7 +30,7 @@
 struct ATSParser;
 class DataSource;
 struct MPEG2TSSource;
-struct String8;
+class String8;
 
 struct MPEG2TSExtractor : public MediaExtractor {
     MPEG2TSExtractor(const sp<DataSource> &source);
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 8c16251..3067c3d 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -104,11 +104,15 @@
     String8 mLastCommentName;
     String8 mLastCommentData;
 
+    KeyedVector<uint32_t, AString> mMetaKeyMap;
+
     status_t readMetaData();
     status_t parseChunk(off64_t *offset, int depth);
     status_t parseITunesMetaData(off64_t offset, size_t size);
     status_t parse3GPPMetaData(off64_t offset, size_t size, int depth);
     void parseID3v2MetaData(off64_t offset);
+    status_t parseQTMetaKey(off64_t data_offset, size_t data_size);
+    status_t parseQTMetaVal(int32_t keyId, off64_t data_offset, size_t data_size);
 
     status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
             const void *esds_data, size_t esds_size);
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index e8c4970..c183208 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -24,7 +24,7 @@
 namespace android {
 
 struct OMXMaster;
-class OMXNodeInstance;
+struct OMXNodeInstance;
 
 class OMX : public BnOMX,
             public IBinder::DeathRecipient {
@@ -95,6 +95,14 @@
             node_id node, OMX_U32 port_index,
             sp<IGraphicBufferProducer> *bufferProducer);
 
+    virtual status_t createPersistentInputSurface(
+            sp<IGraphicBufferProducer> *bufferProducer,
+            sp<IGraphicBufferConsumer> *bufferConsumer);
+
+    virtual status_t setInputSurface(
+            node_id node, OMX_U32 port_index,
+            const sp<IGraphicBufferConsumer> &bufferConsumer);
+
     virtual status_t signalEndOfInputStream(node_id node);
 
     virtual status_t allocateBuffer(
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 104dcfc..ad1e181 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -27,7 +27,9 @@
 
 class IOMXObserver;
 struct OMXMaster;
-struct GraphicBufferSource;
+class GraphicBufferSource;
+
+status_t StatusFromOMXError(OMX_ERRORTYPE err);
 
 struct OMXNodeInstance {
     OMXNodeInstance(
@@ -81,6 +83,13 @@
     status_t createInputSurface(
             OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer);
 
+    static status_t createPersistentInputSurface(
+            sp<IGraphicBufferProducer> *bufferProducer,
+            sp<IGraphicBufferConsumer> *bufferConsumer);
+
+    status_t setInputSurface(
+            OMX_U32 portIndex, const sp<IGraphicBufferConsumer> &bufferConsumer);
+
     status_t signalEndOfInputStream();
 
     status_t allocateBuffer(
@@ -202,6 +211,8 @@
             OMX_BUFFERHEADERTYPE *header,
             OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr);
 
+    status_t createGraphicBufferSource(
+            OMX_U32 portIndex, sp<IGraphicBufferConsumer> consumer = NULL);
     sp<GraphicBufferSource> getGraphicBufferSource();
     void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource);
 
diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
index 60c9e7e..7053247 100644
--- a/media/libstagefright/include/SampleIterator.h
+++ b/media/libstagefright/include/SampleIterator.h
@@ -18,7 +18,7 @@
 
 namespace android {
 
-struct SampleTable;
+class SampleTable;
 
 struct SampleIterator {
     SampleIterator(SampleTable *table);
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index 6632c27..fd739d0 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -25,7 +25,7 @@
 
 namespace android {
 
-struct DataSource;
+class DataSource;
 class MediaExtractor;
 
 struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface {
@@ -38,6 +38,7 @@
             const KeyedVector<String8, String8> *headers);
 
     virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+    virtual status_t setDataSource(const sp<DataSource>& source);
 
     virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option);
     virtual MediaAlbumArt *extractAlbumArt();
@@ -53,6 +54,8 @@
     MediaAlbumArt *mAlbumArt;
 
     void parseMetaData();
+    // Delete album art and clear metadata.
+    void clearMetadata();
 
     StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
 
diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
index 2963150..890f7e8 100644
--- a/media/libstagefright/include/TimedEventQueue.h
+++ b/media/libstagefright/include/TimedEventQueue.h
@@ -46,7 +46,7 @@
         virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
 
     private:
-        friend class TimedEventQueue;
+        friend struct TimedEventQueue;
 
         event_id mEventID;
 
diff --git a/media/libstagefright/include/VBRISeeker.h b/media/libstagefright/include/VBRISeeker.h
index 1a2bf9f..c57d571 100644
--- a/media/libstagefright/include/VBRISeeker.h
+++ b/media/libstagefright/include/VBRISeeker.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-struct DataSource;
+class DataSource;
 
 struct VBRISeeker : public MP3Seeker {
     static sp<VBRISeeker> CreateFromSource(
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
index c408576..cce04f0 100644
--- a/media/libstagefright/include/XINGSeeker.h
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-struct DataSource;
+class DataSource;
 
 struct XINGSeeker : public MP3Seeker {
     static sp<XINGSeeker> CreateFromSource(
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 446ff8c..1e8c2b2 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -8,7 +8,8 @@
         $(TOP)/external/libvpx/libwebm \
         $(TOP)/frameworks/native/include/media/openmax \
 
-LOCAL_CFLAGS += -Wno-multichar -Werror
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE:= libstagefright_matroska
 
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 0712bf0..70d2c69 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -925,6 +925,11 @@
         ALOGV("codec id = %s", codecID);
         ALOGV("codec name = %s", track->GetCodecNameAsUTF8());
 
+        if (codecID == NULL) {
+            ALOGW("unknown codecID is not supported.");
+            continue;
+        }
+
         size_t codecPrivateSize;
         const unsigned char *codecPrivate =
             track->GetCodecPrivate(codecPrivateSize);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 0a868bc..e8b2219 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -132,6 +132,7 @@
 
     bool isAudio() const;
     bool isVideo() const;
+    bool isMeta() const;
 
 protected:
     virtual ~Stream();
@@ -146,6 +147,7 @@
     sp<ABuffer> mBuffer;
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
+    bool mEOSReached;
 
     uint64_t mPrevPTS;
 
@@ -300,9 +302,13 @@
                 // The two checks below shouldn't happen,
                 // we already checked above the stream count matches
                 ssize_t index = newType2PIDs.indexOfKey(temp[i]->type());
-                CHECK(index >= 0);
+                if (index < 0) {
+                    return false;
+                }
                 Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index);
-                CHECK(newPIDs.size() > 0);
+                if (newPIDs.isEmpty()) {
+                    return false;
+                }
 
                 // get the next PID for temp[i]->type() in the new PID map
                 Vector<int32_t>::iterator it = newPIDs.begin();
@@ -333,13 +339,11 @@
         return ERROR_MALFORMED;
     }
 
-    CHECK_EQ(br->getBits(1), 0u);
+    br->skipBits(1);  // '0'
     MY_LOGV("  reserved = %u", br->getBits(2));
 
     unsigned section_length = br->getBits(12);
     ALOGV("  section_length = %u", section_length);
-    CHECK_EQ(section_length & 0xc00, 0u);
-    CHECK_LE(section_length, 1021u);
 
     MY_LOGV("  program_number = %u", br->getBits(16));
     MY_LOGV("  reserved = %u", br->getBits(2));
@@ -356,7 +360,6 @@
 
     unsigned program_info_length = br->getBits(12);
     ALOGV("  program_info_length = %u", program_info_length);
-    CHECK_EQ(program_info_length & 0xc00, 0u);
 
     br->skipBits(program_info_length * 8);  // skip descriptors
 
@@ -367,8 +370,7 @@
     // final CRC.
     size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
 
-    while (infoBytesRemaining > 0) {
-        CHECK_GE(infoBytesRemaining, 5u);
+    while (infoBytesRemaining >= 5) {
 
         unsigned streamType = br->getBits(8);
         ALOGV("    stream_type = 0x%02x", streamType);
@@ -382,9 +384,6 @@
 
         unsigned ES_info_length = br->getBits(12);
         ALOGV("    ES_info_length = %u", ES_info_length);
-        CHECK_EQ(ES_info_length & 0xc00, 0u);
-
-        CHECK_GE(infoBytesRemaining - 5, ES_info_length);
 
 #if 0
         br->skipBits(ES_info_length * 8);  // skip descriptors
@@ -396,13 +395,13 @@
             unsigned descLength = br->getBits(8);
             ALOGV("      len = %u", descLength);
 
-            CHECK_GE(info_bytes_remaining, 2 + descLength);
-
+            if (info_bytes_remaining < descLength) {
+                return ERROR_MALFORMED;
+            }
             br->skipBits(descLength * 8);
 
             info_bytes_remaining -= descLength + 2;
         }
-        CHECK_EQ(info_bytes_remaining, 0u);
 #endif
 
         StreamInfo info;
@@ -413,7 +412,9 @@
         infoBytesRemaining -= 5 + ES_info_length;
     }
 
-    CHECK_EQ(infoBytesRemaining, 0u);
+    if (infoBytesRemaining != 0) {
+        ALOGW("Section data remains unconsumed");
+    }
     MY_LOGV("  CRC = 0x%08x", br->getBits(32));
 
     bool PIDsChanged = false;
@@ -567,6 +568,7 @@
       mPCR_PID(PCR_PID),
       mExpectedContinuityCounter(-1),
       mPayloadStarted(false),
+      mEOSReached(false),
       mPrevPTS(0),
       mQueue(NULL) {
     switch (mStreamType) {
@@ -602,6 +604,11 @@
                     ElementaryStreamQueue::AC3);
             break;
 
+        case STREAMTYPE_METADATA:
+            mQueue = new ElementaryStreamQueue(
+                    ElementaryStreamQueue::METADATA);
+            break;
+
         default:
             break;
     }
@@ -672,7 +679,10 @@
     }
 
     size_t payloadSizeBits = br->numBitsLeft();
-    CHECK_EQ(payloadSizeBits % 8, 0u);
+    if (payloadSizeBits % 8 != 0u) {
+        ALOGE("Wrong value");
+        return BAD_VALUE;
+    }
 
     size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
     if (mBuffer->capacity() < neededSize) {
@@ -720,6 +730,13 @@
     }
 }
 
+bool ATSParser::Stream::isMeta() const {
+    if (mStreamType == STREAMTYPE_METADATA) {
+        return true;
+    }
+    return false;
+}
+
 void ATSParser::Stream::signalDiscontinuity(
         DiscontinuityType type, const sp<AMessage> &extra) {
     mExpectedContinuityCounter = -1;
@@ -729,6 +746,7 @@
     }
 
     mPayloadStarted = false;
+    mEOSReached = false;
     mBuffer->setRange(0, 0);
 
     bool clearFormat = false;
@@ -766,6 +784,8 @@
     if (mSource != NULL) {
         mSource->signalEOS(finalResult);
     }
+    mEOSReached = true;
+    flush();
 }
 
 status_t ATSParser::Stream::parsePES(ABitReader *br) {
@@ -780,8 +800,6 @@
         return ERROR_MALFORMED;
     }
 
-    CHECK_EQ(packet_startcode_prefix, 0x000001u);
-
     unsigned stream_id = br->getBits(8);
     ALOGV("stream_id = 0x%02x", stream_id);
 
@@ -796,7 +814,9 @@
             && stream_id != 0xff  // program_stream_directory
             && stream_id != 0xf2  // DSMCC
             && stream_id != 0xf8) {  // H.222.1 type E
-        CHECK_EQ(br->getBits(2), 2u);
+        if (br->getBits(2) != 2u) {
+            return ERROR_MALFORMED;
+        }
 
         MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
         MY_LOGV("PES_priority = %u", br->getBits(1));
@@ -830,34 +850,51 @@
         uint64_t PTS = 0, DTS = 0;
 
         if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
-            CHECK_GE(optional_bytes_remaining, 5u);
+            if (optional_bytes_remaining < 5u) {
+                return ERROR_MALFORMED;
+            }
 
             if (br->getBits(4) != PTS_DTS_flags) {
-                ALOGE("PES data Error!");
                 return ERROR_MALFORMED;
             }
             PTS = ((uint64_t)br->getBits(3)) << 30;
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             PTS |= ((uint64_t)br->getBits(15)) << 15;
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             PTS |= br->getBits(15);
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             ALOGV("PTS = 0x%016" PRIx64 " (%.2f)", PTS, PTS / 90000.0);
 
             optional_bytes_remaining -= 5;
 
             if (PTS_DTS_flags == 3) {
-                CHECK_GE(optional_bytes_remaining, 5u);
+                if (optional_bytes_remaining < 5u) {
+                    return ERROR_MALFORMED;
+                }
 
-                CHECK_EQ(br->getBits(4), 1u);
+                if (br->getBits(4) != 1u) {
+                    return ERROR_MALFORMED;
+                }
 
                 DTS = ((uint64_t)br->getBits(3)) << 30;
-                CHECK_EQ(br->getBits(1), 1u);
+                if (br->getBits(1) != 1u) {
+                    return ERROR_MALFORMED;
+                }
                 DTS |= ((uint64_t)br->getBits(15)) << 15;
-                CHECK_EQ(br->getBits(1), 1u);
+                if (br->getBits(1) != 1u) {
+                    return ERROR_MALFORMED;
+                }
                 DTS |= br->getBits(15);
-                CHECK_EQ(br->getBits(1), 1u);
+                if (br->getBits(1) != 1u) {
+                    return ERROR_MALFORMED;
+                }
 
                 ALOGV("DTS = %" PRIu64, DTS);
 
@@ -866,31 +903,47 @@
         }
 
         if (ESCR_flag) {
-            CHECK_GE(optional_bytes_remaining, 6u);
+            if (optional_bytes_remaining < 6u) {
+                return ERROR_MALFORMED;
+            }
 
             br->getBits(2);
 
             uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30;
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             ESCR |= ((uint64_t)br->getBits(15)) << 15;
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             ESCR |= br->getBits(15);
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             ALOGV("ESCR = %" PRIu64, ESCR);
             MY_LOGV("ESCR_extension = %u", br->getBits(9));
 
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             optional_bytes_remaining -= 6;
         }
 
         if (ES_rate_flag) {
-            CHECK_GE(optional_bytes_remaining, 3u);
+            if (optional_bytes_remaining < 3u) {
+                return ERROR_MALFORMED;
+            }
 
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             MY_LOGV("ES_rate = %u", br->getBits(22));
-            CHECK_EQ(br->getBits(1), 1u);
+            if (br->getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             optional_bytes_remaining -= 3;
         }
@@ -900,7 +953,9 @@
         // ES data follows.
 
         if (PES_packet_length != 0) {
-            CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+            if (PES_packet_length < PES_header_data_length + 3) {
+                return ERROR_MALFORMED;
+            }
 
             unsigned dataLength =
                 PES_packet_length - 3 - PES_header_data_length;
@@ -913,7 +968,9 @@
                 return ERROR_MALFORMED;
             }
 
-            CHECK_GE(br->numBitsLeft(), dataLength * 8);
+            if (br->numBitsLeft() < dataLength * 8) {
+                return ERROR_MALFORMED;
+            }
 
             onPayloadData(
                     PTS_DTS_flags, PTS, DTS, br->data(), dataLength);
@@ -925,15 +982,21 @@
                     br->data(), br->numBitsLeft() / 8);
 
             size_t payloadSizeBits = br->numBitsLeft();
-            CHECK_EQ(payloadSizeBits % 8, 0u);
+            if (payloadSizeBits % 8 != 0u) {
+                return ERROR_MALFORMED;
+            }
 
             ALOGV("There's %zu bytes of payload.", payloadSizeBits / 8);
         }
     } else if (stream_id == 0xbe) {  // padding_stream
-        CHECK_NE(PES_packet_length, 0u);
+        if (PES_packet_length == 0u) {
+            return ERROR_MALFORMED;
+        }
         br->skipBits(PES_packet_length * 8);
     } else {
-        CHECK_NE(PES_packet_length, 0u);
+        if (PES_packet_length == 0u) {
+            return ERROR_MALFORMED;
+        }
         br->skipBits(PES_packet_length * 8);
     }
 
@@ -976,6 +1039,10 @@
 
     status_t err = mQueue->appendData(data, size, timeUs);
 
+    if (mEOSReached) {
+        mQueue->signalEOS();
+    }
+
     if (err != OK) {
         return;
     }
@@ -1029,6 +1096,14 @@
             break;
         }
 
+        case META:
+        {
+            if (isMeta()) {
+                return mSource;
+            }
+            break;
+        }
+
         default:
             break;
     }
@@ -1053,7 +1128,10 @@
 }
 
 status_t ATSParser::feedTSPacket(const void *data, size_t size) {
-    CHECK_EQ(size, kTSPacketSize);
+    if (size != kTSPacketSize) {
+        ALOGE("Wrong TS packet size");
+        return BAD_VALUE;
+    }
 
     ABitReader br((const uint8_t *)data, kTSPacketSize);
     return parseTS(&br);
@@ -1079,14 +1157,23 @@
         }
     } else if (type == DISCONTINUITY_ABSOLUTE_TIME) {
         int64_t timeUs;
-        CHECK(extra->findInt64("timeUs", &timeUs));
+        if (!extra->findInt64("timeUs", &timeUs)) {
+            ALOGE("timeUs not found");
+            return;
+        }
 
-        CHECK(mPrograms.empty());
+        if (!mPrograms.empty()) {
+            ALOGE("mPrograms is not empty");
+            return;
+        }
         mAbsoluteTimeAnchorUs = timeUs;
         return;
     } else if (type == DISCONTINUITY_TIME_OFFSET) {
         int64_t offset;
-        CHECK(extra->findInt64("offset", &offset));
+        if (!extra->findInt64("offset", &offset)) {
+            ALOGE("offset not found");
+            return;
+        }
 
         mTimeOffsetValid = true;
         mTimeOffsetUs = offset;
@@ -1099,7 +1186,10 @@
 }
 
 void ATSParser::signalEOS(status_t finalResult) {
-    CHECK_NE(finalResult, (status_t)OK);
+    if (finalResult == (status_t) OK) {
+        ALOGE("finalResult not OK");
+        return;
+    }
 
     for (size_t i = 0; i < mPrograms.size(); ++i) {
         mPrograms.editItemAt(i)->signalEOS(finalResult);
@@ -1115,14 +1205,12 @@
     }
     unsigned section_syntax_indictor = br->getBits(1);
     ALOGV("  section_syntax_indictor = %u", section_syntax_indictor);
-    CHECK_EQ(section_syntax_indictor, 1u);
 
-    CHECK_EQ(br->getBits(1), 0u);
+    br->skipBits(1);  // '0'
     MY_LOGV("  reserved = %u", br->getBits(2));
 
     unsigned section_length = br->getBits(12);
     ALOGV("  section_length = %u", section_length);
-    CHECK_EQ(section_length & 0xc00, 0u);
 
     MY_LOGV("  transport_stream_id = %u", br->getBits(16));
     MY_LOGV("  reserved = %u", br->getBits(2));
@@ -1132,7 +1220,6 @@
     MY_LOGV("  last_section_number = %u", br->getBits(8));
 
     size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
-    CHECK_EQ((numProgramBytes % 4), 0u);
 
     for (size_t i = 0; i < numProgramBytes / 4; ++i) {
         unsigned program_number = br->getBits(16);
@@ -1192,7 +1279,9 @@
             br->skipBits(skip * 8);
         }
 
-        CHECK((br->numBitsLeft() % 8) == 0);
+        if (br->numBitsLeft() % 8 != 0) {
+            return ERROR_MALFORMED;
+        }
         status_t err = section->append(br->data(), br->numBitsLeft() / 8);
 
         if (err != OK) {
@@ -1262,7 +1351,7 @@
     return OK;
 }
 
-void ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
+status_t ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
     unsigned adaptation_field_length = br->getBits(8);
 
     if (adaptation_field_length > 0) {
@@ -1278,6 +1367,9 @@
         size_t numBitsRead = 4;
 
         if (PCR_flag) {
+            if (adaptation_field_length * 8 < 52) {
+                return ERROR_MALFORMED;
+            }
             br->skipBits(4);
             uint64_t PCR_base = br->getBits(32);
             PCR_base = (PCR_base << 1) | br->getBits(1);
@@ -1308,10 +1400,9 @@
             numBitsRead += 52;
         }
 
-        CHECK_GE(adaptation_field_length * 8, numBitsRead);
-
         br->skipBits(adaptation_field_length * 8 - numBitsRead);
     }
+    return OK;
 }
 
 status_t ATSParser::parseTS(ABitReader *br) {
@@ -1346,15 +1437,16 @@
 
     // ALOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);
 
-    if (adaptation_field_control == 2 || adaptation_field_control == 3) {
-        parseAdaptationField(br, PID);
-    }
-
     status_t err = OK;
 
-    if (adaptation_field_control == 1 || adaptation_field_control == 3) {
-        err = parsePID(
-                br, PID, continuity_counter, payload_unit_start_indicator);
+    if (adaptation_field_control == 2 || adaptation_field_control == 3) {
+        err = parseAdaptationField(br, PID);
+    }
+    if (err == OK) {
+        if (adaptation_field_control == 1 || adaptation_field_control == 3) {
+            err = parsePID(
+                    br, PID, continuity_counter, payload_unit_start_indicator);
+        }
     }
 
     ++mNumTSPacketsParsed;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index a1405bd..4def333 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -74,7 +74,8 @@
     enum SourceType {
         VIDEO = 0,
         AUDIO = 1,
-        NUM_SOURCE_TYPES = 2
+        META  = 2,
+        NUM_SOURCE_TYPES = 3
     };
     sp<MediaSource> getSource(SourceType type);
     bool hasSource(SourceType type) const;
@@ -90,6 +91,7 @@
         STREAMTYPE_MPEG2_AUDIO          = 0x04,
         STREAMTYPE_MPEG2_AUDIO_ADTS     = 0x0f,
         STREAMTYPE_MPEG4_VIDEO          = 0x10,
+        STREAMTYPE_METADATA             = 0x15,
         STREAMTYPE_H264                 = 0x1b,
 
         // From ATSC A/53 Part 3:2009, 6.7.1
@@ -131,7 +133,7 @@
         unsigned continuity_counter,
         unsigned payload_unit_start_indicator);
 
-    void parseAdaptationField(ABitReader *br, unsigned PID);
+    status_t parseAdaptationField(ABitReader *br, unsigned PID);
     status_t parseTS(ABitReader *br);
 
     void updatePCR(unsigned PID, uint64_t PCR, size_t byteOffsetFromStart);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index c17a0b7..16b0160 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -13,7 +13,8 @@
 	$(TOP)/frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE:= libstagefright_mpeg2ts
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index c5bb41b..0878a1b 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -46,9 +46,10 @@
       mLastQueuedTimeUs(0),
       mEOSResult(OK),
       mLatestEnqueuedMeta(NULL),
-      mLatestDequeuedMeta(NULL),
-      mQueuedDiscontinuityCount(0) {
+      mLatestDequeuedMeta(NULL) {
     setFormat(meta);
+
+    mDiscontinuitySegments.push_back(DiscontinuitySegment());
 }
 
 void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
@@ -73,7 +74,7 @@
     } else  if (!strncasecmp("video/", mime, 6)) {
         mIsVideo = true;
     } else {
-        CHECK(!strncasecmp("text/", mime, 5));
+        CHECK(!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12));
     }
 }
 
@@ -129,11 +130,20 @@
                 mFormat.clear();
             }
 
-            --mQueuedDiscontinuityCount;
+            mDiscontinuitySegments.erase(mDiscontinuitySegments.begin());
+            // CHECK(!mDiscontinuitySegments.empty());
             return INFO_DISCONTINUITY;
         }
 
+        // CHECK(!mDiscontinuitySegments.empty());
+        DiscontinuitySegment &seg = *mDiscontinuitySegments.begin();
+
+        int64_t timeUs;
         mLatestDequeuedMeta = (*buffer)->meta()->dup();
+        CHECK(mLatestDequeuedMeta->findInt64("timeUs", &timeUs));
+        if (timeUs > seg.mMaxDequeTimeUs) {
+            seg.mMaxDequeTimeUs = timeUs;
+        }
 
         sp<RefBase> object;
         if ((*buffer)->meta()->findObject("format", &object)) {
@@ -146,6 +156,12 @@
     return mEOSResult;
 }
 
+void AnotherPacketSource::requeueAccessUnit(const sp<ABuffer> &buffer) {
+    // TODO: update corresponding book keeping info.
+    Mutex::Autolock autoLock(mLock);
+    mBuffers.push_front(buffer);
+}
+
 status_t AnotherPacketSource::read(
         MediaBuffer **out, const ReadOptions *) {
     *out = NULL;
@@ -166,6 +182,8 @@
                 mFormat.clear();
             }
 
+            mDiscontinuitySegments.erase(mDiscontinuitySegments.begin());
+            // CHECK(!mDiscontinuitySegments.empty());
             return INFO_DISCONTINUITY;
         }
 
@@ -178,11 +196,26 @@
 
         int64_t timeUs;
         CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+        // CHECK(!mDiscontinuitySegments.empty());
+        DiscontinuitySegment &seg = *mDiscontinuitySegments.begin();
+        if (timeUs > seg.mMaxDequeTimeUs) {
+            seg.mMaxDequeTimeUs = timeUs;
+        }
 
         MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
 
         mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
 
+        int32_t isSync;
+        if (buffer->meta()->findInt32("isSync", &isSync)) {
+            mediaBuffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+        }
+
+        sp<ABuffer> sei;
+        if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) {
+            mediaBuffer->meta_data()->setData(kKeySEI, 0, sei->data(), sei->size());
+        }
+
         *out = mediaBuffer;
         return OK;
     }
@@ -215,12 +248,14 @@
     mCondition.signal();
 
     int32_t discontinuity;
-    if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
-        // discontinuity handling needs to be consistent with queueDiscontinuity()
-        ++mQueuedDiscontinuityCount;
+    if (buffer->meta()->findInt32("discontinuity", &discontinuity)){
+        ALOGV("queueing a discontinuity with queueAccessUnit");
+
         mLastQueuedTimeUs = 0ll;
         mEOSResult = OK;
         mLatestEnqueuedMeta = NULL;
+
+        mDiscontinuitySegments.push_back(DiscontinuitySegment());
         return;
     }
 
@@ -230,6 +265,15 @@
     ALOGV("queueAccessUnit timeUs=%" PRIi64 " us (%.2f secs)",
             mLastQueuedTimeUs, mLastQueuedTimeUs / 1E6);
 
+    // CHECK(!mDiscontinuitySegments.empty());
+    DiscontinuitySegment &tailSeg = *(--mDiscontinuitySegments.end());
+    if (lastQueuedTimeUs > tailSeg.mMaxEnqueTimeUs) {
+        tailSeg.mMaxEnqueTimeUs = lastQueuedTimeUs;
+    }
+    if (tailSeg.mMaxDequeTimeUs == -1) {
+        tailSeg.mMaxDequeTimeUs = lastQueuedTimeUs;
+    }
+
     if (mLatestEnqueuedMeta == NULL) {
         mLatestEnqueuedMeta = buffer->meta()->dup();
     } else {
@@ -253,7 +297,9 @@
 
     mBuffers.clear();
     mEOSResult = OK;
-    mQueuedDiscontinuityCount = 0;
+
+    mDiscontinuitySegments.clear();
+    mDiscontinuitySegments.push_back(DiscontinuitySegment());
 
     mFormat = NULL;
     mLatestEnqueuedMeta = NULL;
@@ -280,6 +326,14 @@
 
             ++it;
         }
+
+        for (List<DiscontinuitySegment>::iterator it2 = mDiscontinuitySegments.begin();
+                it2 != mDiscontinuitySegments.end();
+                ++it2) {
+            DiscontinuitySegment &seg = *it2;
+            seg.clear();
+        }
+
     }
 
     mEOSResult = OK;
@@ -290,7 +344,8 @@
         return;
     }
 
-    ++mQueuedDiscontinuityCount;
+    mDiscontinuitySegments.push_back(DiscontinuitySegment());
+
     sp<ABuffer> buffer = new ABuffer(0);
     buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
     buffer->meta()->setMessage("extra", extra);
@@ -341,84 +396,19 @@
 
 int64_t AnotherPacketSource::getBufferedDurationUs(status_t *finalResult) {
     Mutex::Autolock autoLock(mLock);
-    return getBufferedDurationUs_l(finalResult);
-}
-
-int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) {
     *finalResult = mEOSResult;
 
-    if (mBuffers.empty()) {
-        return 0;
-    }
-
-    int64_t time1 = -1;
-    int64_t time2 = -1;
     int64_t durationUs = 0;
-
-    List<sp<ABuffer> >::iterator it = mBuffers.begin();
-    while (it != mBuffers.end()) {
-        const sp<ABuffer> &buffer = *it;
-
-        int64_t timeUs;
-        if (buffer->meta()->findInt64("timeUs", &timeUs)) {
-            if (time1 < 0 || timeUs < time1) {
-                time1 = timeUs;
-            }
-
-            if (time2 < 0 || timeUs > time2) {
-                time2 = timeUs;
-            }
-        } else {
-            // This is a discontinuity, reset everything.
-            durationUs += time2 - time1;
-            time1 = time2 = -1;
-        }
-
-        ++it;
+    for (List<DiscontinuitySegment>::iterator it = mDiscontinuitySegments.begin();
+            it != mDiscontinuitySegments.end();
+            ++it) {
+        const DiscontinuitySegment &seg = *it;
+        // dequeued access units should be a subset of enqueued access units
+        // CHECK(seg.maxEnqueTimeUs >= seg.mMaxDequeTimeUs);
+        durationUs += (seg.mMaxEnqueTimeUs - seg.mMaxDequeTimeUs);
     }
 
-    return durationUs + (time2 - time1);
-}
-
-// A cheaper but less precise version of getBufferedDurationUs that we would like to use in
-// LiveSession::dequeueAccessUnit to trigger downwards adaptation.
-int64_t AnotherPacketSource::getEstimatedDurationUs() {
-    Mutex::Autolock autoLock(mLock);
-    if (mBuffers.empty()) {
-        return 0;
-    }
-
-    if (mQueuedDiscontinuityCount > 0) {
-        status_t finalResult;
-        return getBufferedDurationUs_l(&finalResult);
-    }
-
-    List<sp<ABuffer> >::iterator it = mBuffers.begin();
-    sp<ABuffer> buffer = *it;
-
-    int64_t startTimeUs;
-    buffer->meta()->findInt64("timeUs", &startTimeUs);
-    if (startTimeUs < 0) {
-        return 0;
-    }
-
-    it = mBuffers.end();
-    --it;
-    buffer = *it;
-
-    int64_t endTimeUs;
-    buffer->meta()->findInt64("timeUs", &endTimeUs);
-    if (endTimeUs < 0) {
-        return 0;
-    }
-
-    int64_t diffUs;
-    if (endTimeUs > startTimeUs) {
-        diffUs = endTimeUs - startTimeUs;
-    } else {
-        diffUs = startTimeUs - endTimeUs;
-    }
-    return diffUs;
+    return durationUs;
 }
 
 status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
@@ -514,18 +504,19 @@
     }
 
     HLSTime stopTime(meta);
-    ALOGV("trimBuffersAfterMeta: discontinuitySeq %zu, timeUs %lld",
+    ALOGV("trimBuffersAfterMeta: discontinuitySeq %d, timeUs %lld",
             stopTime.mSeq, (long long)stopTime.mTimeUs);
 
     List<sp<ABuffer> >::iterator it;
+    List<DiscontinuitySegment >::iterator it2;
     sp<AMessage> newLatestEnqueuedMeta = NULL;
     int64_t newLastQueuedTimeUs = 0;
-    size_t newDiscontinuityCount = 0;
-    for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+    for (it = mBuffers.begin(), it2 = mDiscontinuitySegments.begin(); it != mBuffers.end(); ++it) {
         const sp<ABuffer> &buffer = *it;
         int32_t discontinuity;
         if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
-            newDiscontinuityCount++;
+            // CHECK(it2 != mDiscontinuitySegments.end());
+            ++it2;
             continue;
         }
 
@@ -538,10 +529,18 @@
         newLatestEnqueuedMeta = buffer->meta();
         newLastQueuedTimeUs = curTime.mTimeUs;
     }
+
     mBuffers.erase(it, mBuffers.end());
     mLatestEnqueuedMeta = newLatestEnqueuedMeta;
     mLastQueuedTimeUs = newLastQueuedTimeUs;
-    mQueuedDiscontinuityCount = newDiscontinuityCount;
+
+    DiscontinuitySegment &seg = *it2;
+    if (newLatestEnqueuedMeta != NULL) {
+        seg.mMaxEnqueTimeUs = newLastQueuedTimeUs;
+    } else {
+        seg.clear();
+    }
+    mDiscontinuitySegments.erase(++it2, mDiscontinuitySegments.end());
 }
 
 /*
@@ -554,10 +553,11 @@
 sp<AMessage> AnotherPacketSource::trimBuffersBeforeMeta(
         const sp<AMessage> &meta) {
     HLSTime startTime(meta);
-    ALOGV("trimBuffersBeforeMeta: discontinuitySeq %zu, timeUs %lld",
+    ALOGV("trimBuffersBeforeMeta: discontinuitySeq %d, timeUs %lld",
             startTime.mSeq, (long long)startTime.mTimeUs);
 
     sp<AMessage> firstMeta;
+    int64_t firstTimeUs = -1;
     Mutex::Autolock autoLock(mLock);
     if (mBuffers.empty()) {
         return NULL;
@@ -567,14 +567,14 @@
     bool isAvc = false;
 
     List<sp<ABuffer> >::iterator it;
-    size_t discontinuityCount = 0;
     for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
         const sp<ABuffer> &buffer = *it;
         int32_t discontinuity;
         if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
+            mDiscontinuitySegments.erase(mDiscontinuitySegments.begin());
+            // CHECK(!mDiscontinuitySegments.empty());
             format = NULL;
             isAvc = false;
-            discontinuityCount++;
             continue;
         }
         if (format == NULL) {
@@ -596,12 +596,21 @@
             ALOGV("trimming from beginning to %lld (not inclusive)",
                     (long long)curTime.mTimeUs);
             firstMeta = buffer->meta();
+            firstTimeUs = curTime.mTimeUs;
             break;
         }
     }
     mBuffers.erase(mBuffers.begin(), it);
-    mQueuedDiscontinuityCount -= discontinuityCount;
     mLatestDequeuedMeta = NULL;
+
+    // CHECK(!mDiscontinuitySegments.empty());
+    DiscontinuitySegment &seg = *mDiscontinuitySegments.begin();
+    if (firstTimeUs >= 0) {
+        seg.mMaxDequeTimeUs = firstTimeUs;
+    } else {
+        seg.clear();
+    }
+
     return firstMeta;
 }
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index fa7dd6a..eb9dc9b 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -53,8 +53,6 @@
     // presentation timestamps since the last discontinuity (if any).
     int64_t getBufferedDurationUs(status_t *finalResult);
 
-    int64_t getEstimatedDurationUs();
-
     status_t nextBufferTime(int64_t *timeUs);
 
     void queueAccessUnit(const sp<ABuffer> &buffer);
@@ -67,6 +65,7 @@
     void signalEOS(status_t result);
 
     status_t dequeueAccessUnit(sp<ABuffer> *buffer);
+    void requeueAccessUnit(const sp<ABuffer> &buffer);
 
     bool isFinished(int64_t duration) const;
 
@@ -83,6 +82,25 @@
     virtual ~AnotherPacketSource();
 
 private:
+
+    struct DiscontinuitySegment {
+        int64_t mMaxDequeTimeUs, mMaxEnqueTimeUs;
+        DiscontinuitySegment()
+            : mMaxDequeTimeUs(-1),
+              mMaxEnqueTimeUs(-1) {
+        };
+
+        void clear() {
+            mMaxDequeTimeUs = mMaxEnqueTimeUs = -1;
+        }
+    };
+
+    // Discontinuity segments are consecutive access units between
+    // discontinuity markers. There should always be at least _ONE_
+    // discontinuity segment, hence the various CHECKs in
+    // AnotherPacketSource.cpp for non-empty()-ness.
+    List<DiscontinuitySegment> mDiscontinuitySegments;
+
     Mutex mLock;
     Condition mCondition;
 
@@ -96,10 +114,7 @@
     sp<AMessage> mLatestEnqueuedMeta;
     sp<AMessage> mLatestDequeuedMeta;
 
-    size_t  mQueuedDiscontinuityCount;
-
     bool wasFormatChange(int32_t discontinuityType) const;
-    int64_t getBufferedDurationUs_l(status_t *finalResult);
 
     DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
 };
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index b17985c..baf3b15 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -38,7 +38,8 @@
 
 ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
     : mMode(mode),
-      mFlags(flags) {
+      mFlags(flags),
+      mEOSReached(false) {
 }
 
 sp<MetaData> ElementaryStreamQueue::getFormat() {
@@ -55,6 +56,8 @@
     if (clearFormat) {
         mFormat.clear();
     }
+
+    mEOSReached = false;
 }
 
 // Parse AC3 header assuming the current ptr is start position of syncframe,
@@ -244,6 +247,11 @@
 
 status_t ElementaryStreamQueue::appendData(
         const void *data, size_t size, int64_t timeUs) {
+
+    if (mEOSReached) {
+        ALOGE("appending data after EOS");
+        return ERROR_MALFORMED;
+    }
     if (mBuffer == NULL || mBuffer->size() == 0) {
         switch (mMode) {
             case H264:
@@ -409,13 +417,14 @@
             }
 
             case PCM_AUDIO:
+            case METADATA:
             {
                 break;
             }
 
             default:
-                TRESPASS();
-                break;
+                ALOGE("Unknown mode: %d", mMode);
+                return ERROR_MALFORMED;
         }
     }
 
@@ -493,8 +502,13 @@
             return dequeueAccessUnitMPEG4Video();
         case PCM_AUDIO:
             return dequeueAccessUnitPCMAudio();
+        case METADATA:
+            return dequeueAccessUnitMetadata();
         default:
-            CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO);
+            if (mMode != MPEG_AUDIO) {
+                ALOGE("Unknown mode");
+                return NULL;
+            }
             return dequeueAccessUnitMPEGAudio();
     }
 }
@@ -531,8 +545,12 @@
     memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
 
     int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
-    CHECK_GE(timeUs, 0ll);
+    if (timeUs < 0ll) {
+        ALOGE("negative timeUs");
+        return NULL;
+    }
     accessUnit->meta()->setInt64("timeUs", timeUs);
+    accessUnit->meta()->setInt32("isSync", 1);
 
     memmove(
             mBuffer->data(),
@@ -550,15 +568,24 @@
     }
 
     ABitReader bits(mBuffer->data(), 4);
-    CHECK_EQ(bits.getBits(8), 0xa0);
+    if (bits.getBits(8) != 0xa0) {
+        ALOGE("Unexpected bit values");
+        return NULL;
+    }
     unsigned numAUs = bits.getBits(8);
     bits.skipBits(8);
     unsigned quantization_word_length __unused = bits.getBits(2);
     unsigned audio_sampling_frequency = bits.getBits(3);
     unsigned num_channels = bits.getBits(3);
 
-    CHECK_EQ(audio_sampling_frequency, 2);  // 48kHz
-    CHECK_EQ(num_channels, 1u);  // stereo!
+    if (audio_sampling_frequency != 2) {
+        ALOGE("Wrong sampling freq");
+        return NULL;
+    }
+    if (num_channels != 1u) {
+        ALOGE("Wrong channel #");
+        return NULL;
+    }
 
     if (mFormat == NULL) {
         mFormat = new MetaData;
@@ -580,8 +607,12 @@
     memcpy(accessUnit->data(), mBuffer->data() + 4, payloadSize);
 
     int64_t timeUs = fetchTimestamp(payloadSize + 4);
-    CHECK_GE(timeUs, 0ll);
+    if (timeUs < 0ll) {
+        ALOGE("Negative timeUs");
+        return NULL;
+    }
     accessUnit->meta()->setInt64("timeUs", timeUs);
+    accessUnit->meta()->setInt32("isSync", 1);
 
     int16_t *ptr = (int16_t *)accessUnit->data();
     for (size_t i = 0; i < payloadSize / sizeof(int16_t); ++i) {
@@ -603,14 +634,19 @@
         return NULL;
     }
 
-    CHECK(!mRangeInfos.empty());
+    if (mRangeInfos.empty()) {
+        return NULL;
+    }
 
     const RangeInfo &info = *mRangeInfos.begin();
     if (mBuffer->size() < info.mLength) {
         return NULL;
     }
 
-    CHECK_GE(info.mTimestampUs, 0ll);
+    if (info.mTimestampUs < 0ll) {
+        ALOGE("Negative info.mTimestampUs");
+        return NULL;
+    }
 
     // The idea here is consume all AAC frames starting at offsets before
     // info.mLength so we can assign a meaningful timestamp without
@@ -627,17 +663,26 @@
 
         // adts_fixed_header
 
-        CHECK_EQ(bits.getBits(12), 0xfffu);
+        if (bits.getBits(12) != 0xfffu) {
+            ALOGE("Wrong atds_fixed_header");
+            return NULL;
+        }
         bits.skipBits(3);  // ID, layer
         bool protection_absent __unused = bits.getBits(1) != 0;
 
         if (mFormat == NULL) {
             unsigned profile = bits.getBits(2);
-            CHECK_NE(profile, 3u);
+            if (profile == 3u) {
+                ALOGE("profile should not be 3");
+                return NULL;
+            }
             unsigned sampling_freq_index = bits.getBits(4);
             bits.getBits(1);  // private_bit
             unsigned channel_configuration = bits.getBits(3);
-            CHECK_NE(channel_configuration, 0u);
+            if (channel_configuration == 0u) {
+                ALOGE("channel_config should not be 0");
+                return NULL;
+            }
             bits.skipBits(2);  // original_copy, home
 
             mFormat = MakeAACCodecSpecificData(
@@ -647,8 +692,14 @@
 
             int32_t sampleRate;
             int32_t numChannels;
-            CHECK(mFormat->findInt32(kKeySampleRate, &sampleRate));
-            CHECK(mFormat->findInt32(kKeyChannelCount, &numChannels));
+            if (!mFormat->findInt32(kKeySampleRate, &sampleRate)) {
+                ALOGE("SampleRate not found");
+                return NULL;
+            }
+            if (!mFormat->findInt32(kKeyChannelCount, &numChannels)) {
+                ALOGE("ChannelCount not found");
+                return NULL;
+            }
 
             ALOGI("found AAC codec config (%d Hz, %d channels)",
                  sampleRate, numChannels);
@@ -671,7 +722,8 @@
 
         if (number_of_raw_data_blocks_in_frame != 0) {
             // To be implemented.
-            TRESPASS();
+            ALOGE("Should not reach here.");
+            return NULL;
         }
 
         if (offset + aac_frame_length > mBuffer->size()) {
@@ -693,6 +745,7 @@
     mBuffer->setRange(0, mBuffer->size() - offset);
 
     accessUnit->meta()->setInt64("timeUs", timeUs);
+    accessUnit->meta()->setInt32("isSync", 1);
 
     return accessUnit;
 }
@@ -702,7 +755,9 @@
     bool first = true;
 
     while (size > 0) {
-        CHECK(!mRangeInfos.empty());
+        if (mRangeInfos.empty()) {
+            return timeUs;
+        }
 
         RangeInfo *info = &*mRangeInfos.begin();
 
@@ -743,6 +798,7 @@
     const uint8_t *nalStart;
     size_t nalSize;
     bool foundSlice = false;
+    bool foundIDR = false;
     while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
         if (nalSize == 0) continue;
 
@@ -750,6 +806,9 @@
         bool flush = false;
 
         if (nalType == 1 || nalType == 5) {
+            if (nalType == 5) {
+                foundIDR = true;
+            }
             if (foundSlice) {
                 ABitReader br(nalStart + 1, nalSize);
                 unsigned first_mb_in_slice = parseUE(&br);
@@ -797,7 +856,10 @@
                 unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f;
 
                 if (nalType == 6 && pos.nalSize > 0) {
-                    CHECK_LT(seiIndex, sei->size() / sizeof(NALPosition));
+                    if (seiIndex >= sei->size() / sizeof(NALPosition)) {
+                        ALOGE("Wrong seiIndex");
+                        return NULL;
+                    }
                     NALPosition &seiPos = ((NALPosition *)sei->data())[seiIndex++];
                     seiPos.nalOffset = dstOffset + 4;
                     seiPos.nalSize = pos.nalSize;
@@ -835,9 +897,15 @@
             mBuffer->setRange(0, mBuffer->size() - nextScan);
 
             int64_t timeUs = fetchTimestamp(nextScan);
-            CHECK_GE(timeUs, 0ll);
+            if (timeUs < 0ll) {
+                ALOGE("Negative timeUs");
+                return NULL;
+            }
 
             accessUnit->meta()->setInt64("timeUs", timeUs);
+            if (foundIDR) {
+                accessUnit->meta()->setInt32("isSync", 1);
+            }
 
             if (mFormat == NULL) {
                 mFormat = MakeAVCCodecSpecificData(accessUnit);
@@ -854,7 +922,10 @@
 
         totalSize += nalSize;
     }
-    CHECK_EQ(err, (status_t)-EAGAIN);
+    if (err != (status_t)-EAGAIN) {
+        ALOGE("Unexpeted err");
+        return NULL;
+    }
 
     return NULL;
 }
@@ -871,9 +942,12 @@
 
     size_t frameSize;
     int samplingRate, numChannels, bitrate, numSamples;
-    CHECK(GetMPEGAudioFrameSize(
+    if (!GetMPEGAudioFrameSize(
                 header, &frameSize, &samplingRate, &numChannels,
-                &bitrate, &numSamples));
+                &bitrate, &numSamples)) {
+        ALOGE("Failed to get audio frame size");
+        return NULL;
+    }
 
     if (size < frameSize) {
         return NULL;
@@ -891,9 +965,13 @@
     mBuffer->setRange(0, mBuffer->size() - frameSize);
 
     int64_t timeUs = fetchTimestamp(frameSize);
-    CHECK_GE(timeUs, 0ll);
+    if (timeUs < 0ll) {
+        ALOGE("Negative timeUs");
+        return NULL;
+    }
 
     accessUnit->meta()->setInt64("timeUs", timeUs);
+    accessUnit->meta()->setInt32("isSync", 1);
 
     if (mFormat == NULL) {
         mFormat = new MetaData;
@@ -912,7 +990,7 @@
                         kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
                 break;
             default:
-                TRESPASS();
+                return NULL;
         }
 
         mFormat->setInt32(kKeySampleRate, samplingRate);
@@ -923,7 +1001,10 @@
 }
 
 static void EncodeSize14(uint8_t **_ptr, size_t size) {
-    CHECK_LE(size, 0x3fff);
+    if (size > 0x3fff) {
+        ALOGE("Wrong size");
+        return;
+    }
 
     uint8_t *ptr = *_ptr;
 
@@ -970,6 +1051,9 @@
     int pprevStartCode = -1;
     int prevStartCode = -1;
     int currentStartCode = -1;
+    bool gopFound = false;
+    bool isClosedGop = false;
+    bool brokenLink = false;
 
     size_t offset = 0;
     while (offset + 3 < size) {
@@ -995,7 +1079,10 @@
             // seqHeader without/with extension
 
             if (mFormat == NULL) {
-                CHECK_GE(size, 7u);
+                if (size < 7u) {
+                    ALOGE("Size too small");
+                    return NULL;
+                }
 
                 unsigned width =
                     (data[4] << 4) | data[5] >> 4;
@@ -1032,6 +1119,13 @@
             }
         }
 
+        if (mFormat != NULL && currentStartCode == 0xb8) {
+            // GOP layer
+            gopFound = true;
+            isClosedGop = (data[offset + 7] & 0x40) != 0;
+            brokenLink = (data[offset + 7] & 0x20) != 0;
+        }
+
         if (mFormat != NULL && currentStartCode == 0x00) {
             // Picture start
 
@@ -1048,11 +1142,17 @@
                 mBuffer->setRange(0, mBuffer->size() - offset);
 
                 int64_t timeUs = fetchTimestamp(offset);
-                CHECK_GE(timeUs, 0ll);
+                if (timeUs < 0ll) {
+                    ALOGE("Negative timeUs");
+                    return NULL;
+                }
 
                 offset = 0;
 
                 accessUnit->meta()->setInt64("timeUs", timeUs);
+                if (gopFound && (!brokenLink || isClosedGop)) {
+                    accessUnit->meta()->setInt32("isSync", 1);
+                }
 
                 ALOGV("returning MPEG video access unit at time %" PRId64 " us",
                       timeUs);
@@ -1078,7 +1178,7 @@
     }
 
     if (memcmp(kStartCode, data, 3)) {
-        TRESPASS();
+        return -EAGAIN;
     }
 
     size_t offset = 3;
@@ -1138,25 +1238,37 @@
 
             case EXPECT_VISUAL_OBJECT_START:
             {
-                CHECK_EQ(chunkType, 0xb5);
+                if (chunkType != 0xb5) {
+                    ALOGE("Unexpected chunkType");
+                    return NULL;
+                }
                 state = EXPECT_VO_START;
                 break;
             }
 
             case EXPECT_VO_START:
             {
-                CHECK_LE(chunkType, 0x1f);
+                if (chunkType > 0x1f) {
+                    ALOGE("Unexpected chunkType");
+                    return NULL;
+                }
                 state = EXPECT_VOL_START;
                 break;
             }
 
             case EXPECT_VOL_START:
             {
-                CHECK((chunkType & 0xf0) == 0x20);
+                if ((chunkType & 0xf0) != 0x20) {
+                    ALOGE("Wrong chunkType");
+                    return NULL;
+                }
 
-                CHECK(ExtractDimensionsFromVOLHeader(
+                if (!ExtractDimensionsFromVOLHeader(
                             &data[offset], chunkSize,
-                            &width, &height));
+                            &width, &height)) {
+                    ALOGE("Failed to get dimension");
+                    return NULL;
+                }
 
                 state = WAIT_FOR_VOP_START;
                 break;
@@ -1197,6 +1309,8 @@
             case SKIP_TO_VOP_START:
             {
                 if (chunkType == 0xb6) {
+                    int vopCodingType = (data[offset + 4] & 0xc0) >> 6;
+
                     offset += chunkSize;
 
                     sp<ABuffer> accessUnit = new ABuffer(offset);
@@ -1207,11 +1321,17 @@
                     mBuffer->setRange(0, size);
 
                     int64_t timeUs = fetchTimestamp(offset);
-                    CHECK_GE(timeUs, 0ll);
+                    if (timeUs < 0ll) {
+                        ALOGE("Negative timeus");
+                        return NULL;
+                    }
 
                     offset = 0;
 
                     accessUnit->meta()->setInt64("timeUs", timeUs);
+                    if (vopCodingType == 0) {  // intra-coded VOP
+                        accessUnit->meta()->setInt32("isSync", 1);
+                    }
 
                     ALOGV("returning MPEG4 video access unit at time %" PRId64 " us",
                          timeUs);
@@ -1228,7 +1348,8 @@
             }
 
             default:
-                TRESPASS();
+                ALOGE("Unknown state: %d", state);
+                return NULL;
         }
 
         if (discard) {
@@ -1245,4 +1366,37 @@
     return NULL;
 }
 
+void ElementaryStreamQueue::signalEOS() {
+    if (!mEOSReached) {
+        if (mMode == MPEG_VIDEO) {
+            const char *theEnd = "\x00\x00\x01\x00";
+            appendData(theEnd, 4, 0);
+        }
+        mEOSReached = true;
+    } else {
+        ALOGW("EOS already signaled");
+    }
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMetadata() {
+    size_t size = mBuffer->size();
+    if (!size) {
+        return NULL;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(size);
+    int64_t timeUs = fetchTimestamp(size);
+    accessUnit->meta()->setInt64("timeUs", timeUs);
+
+    memcpy(accessUnit->data(), mBuffer->data(), size);
+    mBuffer->setRange(0, 0);
+
+    if (mFormat == NULL) {
+        mFormat = new MetaData;
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_DATA_METADATA);
+    }
+
+    return accessUnit;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 45b4624..e9f96b7 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -37,6 +37,7 @@
         MPEG_VIDEO,
         MPEG4_VIDEO,
         PCM_AUDIO,
+        METADATA,
     };
 
     enum Flags {
@@ -46,6 +47,7 @@
     ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
 
     status_t appendData(const void *data, size_t size, int64_t timeUs);
+    void signalEOS();
     void clear(bool clearFormat);
 
     sp<ABuffer> dequeueAccessUnit();
@@ -60,6 +62,7 @@
 
     Mode mMode;
     uint32_t mFlags;
+    bool mEOSReached;
 
     sp<ABuffer> mBuffer;
     List<RangeInfo> mRangeInfos;
@@ -73,6 +76,7 @@
     sp<ABuffer> dequeueAccessUnitMPEGVideo();
     sp<ABuffer> dequeueAccessUnitMPEG4Video();
     sp<ABuffer> dequeueAccessUnitPCMAudio();
+    sp<ABuffer> dequeueAccessUnitMetadata();
 
     // consume a logical (compressed) access unit of size "size",
     // returns its timestamp in us (or -1 if no time information).
diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
index 85859f7..6d9fe9d 100644
--- a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp
@@ -265,7 +265,10 @@
     }
 
     unsigned PES_packet_length = U16_AT(mBuffer->data() + 4);
-    CHECK_NE(PES_packet_length, 0u);
+    if (PES_packet_length == 0u) {
+        ALOGE("PES_packet_length is 0");
+        return -EAGAIN;
+    }
 
     size_t n = PES_packet_length + 6;
 
@@ -286,7 +289,10 @@
         return ERROR_MALFORMED;
     }
 
-    CHECK_EQ(packet_startcode_prefix, 0x000001u);
+    if (packet_startcode_prefix != 0x000001u) {
+        ALOGE("Wrong PES prefix");
+        return ERROR_MALFORMED;
+    }
 
     unsigned stream_id = br.getBits(8);
     ALOGV("stream_id = 0x%02x", stream_id);
@@ -366,8 +372,7 @@
             && stream_id != 0xff  // program_stream_directory
             && stream_id != 0xf2  // DSMCC
             && stream_id != 0xf8) {  // H.222.1 type E
-        CHECK_EQ(br.getBits(2), 2u);
-
+        /* unsigned PES_marker_bits = */br.getBits(2);  // should be 0x2(hex)
         /* unsigned PES_scrambling_control = */br.getBits(2);
         /* unsigned PES_priority = */br.getBits(1);
         /* unsigned data_alignment_indicator = */br.getBits(1);
@@ -400,16 +405,26 @@
         uint64_t PTS = 0, DTS = 0;
 
         if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
-            CHECK_GE(optional_bytes_remaining, 5u);
+            if (optional_bytes_remaining < 5u) {
+                return ERROR_MALFORMED;
+            }
 
-            CHECK_EQ(br.getBits(4), PTS_DTS_flags);
+            if (br.getBits(4) != PTS_DTS_flags) {
+                return ERROR_MALFORMED;
+            }
 
             PTS = ((uint64_t)br.getBits(3)) << 30;
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             PTS |= ((uint64_t)br.getBits(15)) << 15;
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             PTS |= br.getBits(15);
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             ALOGV("PTS = %" PRIu64, PTS);
             // ALOGI("PTS = %.2f secs", PTS / 90000.0f);
@@ -417,16 +432,26 @@
             optional_bytes_remaining -= 5;
 
             if (PTS_DTS_flags == 3) {
-                CHECK_GE(optional_bytes_remaining, 5u);
+                if (optional_bytes_remaining < 5u) {
+                    return ERROR_MALFORMED;
+                }
 
-                CHECK_EQ(br.getBits(4), 1u);
+                if (br.getBits(4) != 1u) {
+                    return ERROR_MALFORMED;
+                }
 
                 DTS = ((uint64_t)br.getBits(3)) << 30;
-                CHECK_EQ(br.getBits(1), 1u);
+                if (br.getBits(1) != 1u) {
+                    return ERROR_MALFORMED;
+                }
                 DTS |= ((uint64_t)br.getBits(15)) << 15;
-                CHECK_EQ(br.getBits(1), 1u);
+                if (br.getBits(1) != 1u) {
+                    return ERROR_MALFORMED;
+                }
                 DTS |= br.getBits(15);
-                CHECK_EQ(br.getBits(1), 1u);
+                if (br.getBits(1) != 1u) {
+                    return ERROR_MALFORMED;
+                }
 
                 ALOGV("DTS = %" PRIu64, DTS);
 
@@ -435,40 +460,62 @@
         }
 
         if (ESCR_flag) {
-            CHECK_GE(optional_bytes_remaining, 6u);
+            if (optional_bytes_remaining < 6u) {
+                return ERROR_MALFORMED;
+            }
 
             br.getBits(2);
 
             uint64_t ESCR = ((uint64_t)br.getBits(3)) << 30;
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             ESCR |= ((uint64_t)br.getBits(15)) << 15;
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             ESCR |= br.getBits(15);
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             ALOGV("ESCR = %" PRIu64, ESCR);
             /* unsigned ESCR_extension = */br.getBits(9);
 
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             optional_bytes_remaining -= 6;
         }
 
         if (ES_rate_flag) {
-            CHECK_GE(optional_bytes_remaining, 3u);
+            if (optional_bytes_remaining < 3u) {
+                return ERROR_MALFORMED;
+            }
 
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
             /* unsigned ES_rate = */br.getBits(22);
-            CHECK_EQ(br.getBits(1), 1u);
+            if (br.getBits(1) != 1u) {
+                return ERROR_MALFORMED;
+            }
 
             optional_bytes_remaining -= 3;
         }
 
+        if (br.numBitsLeft() < optional_bytes_remaining * 8) {
+            return ERROR_MALFORMED;
+        }
+
         br.skipBits(optional_bytes_remaining * 8);
 
         // ES data follows.
 
-        CHECK_GE(PES_packet_length, PES_header_data_length + 3);
+        if (PES_packet_length < PES_header_data_length + 3) {
+            return ERROR_MALFORMED;
+        }
 
         unsigned dataLength =
             PES_packet_length - 3 - PES_header_data_length;
@@ -481,7 +528,9 @@
             return ERROR_MALFORMED;
         }
 
-        CHECK_GE(br.numBitsLeft(), dataLength * 8);
+        if (br.numBitsLeft() < dataLength * 8) {
+            return ERROR_MALFORMED;
+        }
 
         ssize_t index = mTracks.indexOfKey(stream_id);
         if (index < 0 && mScanning) {
@@ -521,10 +570,14 @@
             return err;
         }
     } else if (stream_id == 0xbe) {  // padding_stream
-        CHECK_NE(PES_packet_length, 0u);
+        if (PES_packet_length == 0u) {
+            return ERROR_MALFORMED;
+        }
         br.skipBits(PES_packet_length * 8);
     } else {
-        CHECK_NE(PES_packet_length, 0u);
+        if (PES_packet_length == 0u) {
+            return ERROR_MALFORMED;
+        }
         br.skipBits(PES_packet_length * 8);
     }
 
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 1f43d6d..f5c33cf 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);
 }
 
@@ -126,7 +131,10 @@
 
     bool seekable = true;
     if (mSourceImpls.size() > 1) {
-        CHECK_EQ(mSourceImpls.size(), 2u);
+        if (mSourceImpls.size() != 2u) {
+            ALOGE("Wrong size");
+            return NULL;
+        }
 
         sp<MetaData> meta = mSourceImpls.editItemAt(index)->getFormat();
         const char *mime;
@@ -199,6 +207,9 @@
     ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
 
     if (n < (ssize_t)kTSPacketSize) {
+        if (n >= 0) {
+            mParser->signalEOS(ERROR_END_OF_STREAM);
+        }
         return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
     }
 
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 07ea605..5f0f567 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -31,6 +31,8 @@
         libdl
 
 LOCAL_MODULE:= libstagefright_omx
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/media/libstagefright/omx/FrameDropper.cpp b/media/libstagefright/omx/FrameDropper.cpp
index 9fba0b7..9a4952e 100644
--- a/media/libstagefright/omx/FrameDropper.cpp
+++ b/media/libstagefright/omx/FrameDropper.cpp
@@ -50,20 +50,23 @@
 
     if (mDesiredMinTimeUs < 0) {
         mDesiredMinTimeUs = timeUs + mMinIntervalUs;
-        ALOGV("first frame %lld, next desired frame %lld", timeUs, mDesiredMinTimeUs);
+        ALOGV("first frame %lld, next desired frame %lld",
+                (long long)timeUs, (long long)mDesiredMinTimeUs);
         return false;
     }
 
     if (timeUs < (mDesiredMinTimeUs - kMaxJitterUs)) {
         ALOGV("drop frame %lld, desired frame %lld, diff %lld",
-                timeUs, mDesiredMinTimeUs, mDesiredMinTimeUs - timeUs);
+                (long long)timeUs, (long long)mDesiredMinTimeUs,
+                (long long)(mDesiredMinTimeUs - timeUs));
         return true;
     }
 
     int64_t n = (timeUs - mDesiredMinTimeUs + kMaxJitterUs) / mMinIntervalUs;
     mDesiredMinTimeUs += (n + 1) * mMinIntervalUs;
     ALOGV("keep frame %lld, next desired frame %lld, diff %lld",
-            timeUs, mDesiredMinTimeUs, mDesiredMinTimeUs - timeUs);
+            (long long)timeUs, (long long)mDesiredMinTimeUs,
+            (long long)(mDesiredMinTimeUs - timeUs));
     return false;
 }
 
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 477cfc6..01cd8f0 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -38,13 +38,19 @@
 static const bool EXTRA_CHECK = true;
 
 
-GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
-        uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount,
-        bool useGraphicBufferInMeta) :
+GraphicBufferSource::GraphicBufferSource(
+        OMXNodeInstance* nodeInstance,
+        uint32_t bufferWidth,
+        uint32_t bufferHeight,
+        uint32_t bufferCount,
+        bool useGraphicBufferInMeta,
+        const sp<IGraphicBufferConsumer> &consumer) :
     mInitCheck(UNKNOWN_ERROR),
     mNodeInstance(nodeInstance),
     mExecuting(false),
     mSuspended(false),
+    mIsPersistent(false),
+    mConsumer(consumer),
     mNumFramesAvailable(0),
     mEndOfStream(false),
     mEndOfStreamSent(false),
@@ -74,20 +80,22 @@
         return;
     }
 
-    String8 name("GraphicBufferSource");
+    if (mConsumer == NULL) {
+        String8 name("GraphicBufferSource");
 
-    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
-    mConsumer->setConsumerName(name);
-    mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight);
-    mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
-
-    mInitCheck = mConsumer->setMaxAcquiredBufferCount(bufferCount);
-    if (mInitCheck != NO_ERROR) {
-        ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
-                bufferCount, mInitCheck);
-        return;
+        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+        mConsumer->setConsumerName(name);
+        mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
+        mInitCheck = mConsumer->setMaxAcquiredBufferCount(bufferCount);
+        if (mInitCheck != NO_ERROR) {
+            ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+                    bufferCount, mInitCheck);
+            return;
+        }
+    } else {
+        mIsPersistent = true;
     }
-
+    mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight);
     // Note that we can't create an sp<...>(this) in a ctor that will not keep a
     // reference once the ctor ends, as that would cause the refcount of 'this'
     // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
@@ -107,7 +115,7 @@
 
 GraphicBufferSource::~GraphicBufferSource() {
     ALOGV("~GraphicBufferSource");
-    if (mConsumer != NULL) {
+    if (mConsumer != NULL && !mIsPersistent) {
         status_t err = mConsumer->consumerDisconnect();
         if (err != NO_ERROR) {
             ALOGW("consumerDisconnect failed: %d", err);
@@ -292,8 +300,16 @@
         if (id == mLatestBufferId) {
             CHECK_GT(mLatestBufferUseCount--, 0);
         } else {
-            mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber,
-                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            if (mIsPersistent) {
+                mConsumer->detachBuffer(id);
+                int outSlot;
+                mConsumer->attachBuffer(&outSlot, mBufferSlot[id]);
+                mConsumer->releaseBuffer(outSlot, 0,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            } else {
+                mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            }
         }
     } else {
         ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
@@ -375,8 +391,15 @@
 
             --mNumFramesAvailable;
 
-            mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
-                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+            if (mIsPersistent) {
+                mConsumer->detachBuffer(item.mBuf);
+                mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
+                mConsumer->releaseBuffer(item.mBuf, 0,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            } else {
+                mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+            }
         }
         return;
     }
@@ -463,8 +486,15 @@
 
     if (err != OK) {
         ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
-        mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
-                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        if (mIsPersistent) {
+            mConsumer->detachBuffer(item.mBuf);
+            mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
+            mConsumer->releaseBuffer(item.mBuf, 0,
+                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        } else {
+            mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+        }
     } else {
         ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
         setLatestBuffer_l(item, dropped);
@@ -540,12 +570,19 @@
 
     if (mLatestBufferId >= 0) {
         if (mLatestBufferUseCount == 0) {
-            mConsumer->releaseBuffer(
-                    mLatestBufferId,
-                    mLatestBufferFrameNum,
-                    EGL_NO_DISPLAY,
-                    EGL_NO_SYNC_KHR,
-                    Fence::NO_FENCE);
+            if (mIsPersistent) {
+                mConsumer->detachBuffer(mLatestBufferId);
+
+                int outSlot;
+                mConsumer->attachBuffer(&outSlot, mBufferSlot[mLatestBufferId]);
+
+                mConsumer->releaseBuffer(outSlot, 0,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            } else {
+                mConsumer->releaseBuffer(
+                        mLatestBufferId, mLatestBufferFrameNum,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            }
         }
     }
 
@@ -787,8 +824,16 @@
                 ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf);
                 mBufferSlot[item.mBuf] = item.mGraphicBuffer;
             }
-            mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
-                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+
+            if (mIsPersistent) {
+                mConsumer->detachBuffer(item.mBuf);
+                mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
+                mConsumer->releaseBuffer(item.mBuf, 0,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+            } else {
+                mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
+                        EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
+            }
         }
         return;
     }
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 1067472..1047fb3 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -30,7 +30,7 @@
 
 namespace android {
 
-class FrameDropper;
+struct FrameDropper;
 
 /*
  * This class is used to feed OMX codecs from a Surface via BufferQueue.
@@ -50,9 +50,15 @@
  */
 class GraphicBufferSource : public BufferQueue::ConsumerListener {
 public:
-    GraphicBufferSource(OMXNodeInstance* nodeInstance,
-            uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount,
-            bool useGraphicBufferInMeta = false);
+    GraphicBufferSource(
+            OMXNodeInstance* nodeInstance,
+            uint32_t bufferWidth,
+            uint32_t bufferHeight,
+            uint32_t bufferCount,
+            bool useGraphicBufferInMeta = false,
+            const sp<IGraphicBufferConsumer> &consumer = NULL
+    );
+
     virtual ~GraphicBufferSource();
 
     // We can't throw an exception if the constructor fails, so we just set
@@ -219,6 +225,7 @@
     // Our BufferQueue interfaces. mProducer is passed to the producer through
     // getIGraphicBufferProducer, and mConsumer is used internally to retrieve
     // the buffers queued by the producer.
+    bool mIsPersistent;
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
 
@@ -240,7 +247,7 @@
     Vector<CodecBuffer> mCodecBuffers;
 
     ////
-    friend class AHandlerReflector<GraphicBufferSource>;
+    friend struct AHandlerReflector<GraphicBufferSource>;
 
     enum {
         kWhatRepeatLastFrame,
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index f8d38ff..a1ceb2e 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -32,6 +32,7 @@
 
 #include "OMXMaster.h"
 
+#include <OMX_AsString.h>
 #include <OMX_Component.h>
 
 namespace android {
@@ -233,11 +234,11 @@
             instance, &handle);
 
     if (err != OMX_ErrorNone) {
-        ALOGE("FAILED to allocate omx component '%s'", name);
+        ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err);
 
         instance->onGetHandleFailed();
 
-        return UNKNOWN_ERROR;
+        return StatusFromOMXError(err);
     }
 
     *node = makeNodeID(instance);
@@ -377,6 +378,20 @@
             port_index, bufferProducer);
 }
 
+status_t OMX::createPersistentInputSurface(
+        sp<IGraphicBufferProducer> *bufferProducer,
+        sp<IGraphicBufferConsumer> *bufferConsumer) {
+    return OMXNodeInstance::createPersistentInputSurface(
+            bufferProducer, bufferConsumer);
+}
+
+status_t OMX::setInputSurface(
+        node_id node, OMX_U32 port_index,
+        const sp<IGraphicBufferConsumer> &bufferConsumer) {
+    return findInstance(node)->setInputSurface(port_index, bufferConsumer);
+}
+
+
 status_t OMX::signalEndOfInputStream(node_id node) {
     return findInstance(node)->signalEndOfInputStream();
 }
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 4779d6a..91cee73 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -220,13 +220,15 @@
     return mNodeID;
 }
 
-static status_t StatusFromOMXError(OMX_ERRORTYPE err) {
+status_t StatusFromOMXError(OMX_ERRORTYPE err) {
     switch (err) {
         case OMX_ErrorNone:
             return OK;
         case OMX_ErrorUnsupportedSetting:
         case OMX_ErrorUnsupportedIndex:
             return ERROR_UNSUPPORTED;
+        case OMX_ErrorInsufficientResources:
+            return NO_MEMORY;
         default:
             return UNKNOWN_ERROR;
     }
@@ -787,9 +789,8 @@
     return OK;
 }
 
-status_t OMXNodeInstance::createInputSurface(
-        OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
-    Mutex::Autolock autolock(mLock);
+status_t OMXNodeInstance::createGraphicBufferSource(
+        OMX_U32 portIndex, sp<IGraphicBufferConsumer> bufferConsumer) {
     status_t err;
 
     const sp<GraphicBufferSource>& surfaceCheck = getGraphicBufferSource();
@@ -827,19 +828,75 @@
         return INVALID_OPERATION;
     }
 
-    GraphicBufferSource* bufferSource = new GraphicBufferSource(
-            this, def.format.video.nFrameWidth, def.format.video.nFrameHeight,
-            def.nBufferCountActual, usingGraphicBuffer);
+    sp<GraphicBufferSource> bufferSource = new GraphicBufferSource(this,
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            def.nBufferCountActual,
+            usingGraphicBuffer,
+            bufferConsumer);
+
     if ((err = bufferSource->initCheck()) != OK) {
-        delete bufferSource;
         return err;
     }
     setGraphicBufferSource(bufferSource);
 
-    *bufferProducer = bufferSource->getIGraphicBufferProducer();
     return OK;
 }
 
+status_t OMXNodeInstance::createInputSurface(
+        OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
+    Mutex::Autolock autolock(mLock);
+    status_t err = createGraphicBufferSource(portIndex);
+
+    if (err != OK) {
+        return err;
+    }
+
+    *bufferProducer = mGraphicBufferSource->getIGraphicBufferProducer();
+    return OK;
+}
+
+//static
+status_t OMXNodeInstance::createPersistentInputSurface(
+        sp<IGraphicBufferProducer> *bufferProducer,
+        sp<IGraphicBufferConsumer> *bufferConsumer) {
+    String8 name("GraphicBufferSource");
+
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    consumer->setConsumerName(name);
+    consumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER);
+
+    status_t err = consumer->setMaxAcquiredBufferCount(
+            BufferQueue::MAX_MAX_ACQUIRED_BUFFERS);
+    if (err != NO_ERROR) {
+        ALOGE("Unable to set BQ max acquired buffer count to %u: %d",
+                BufferQueue::MAX_MAX_ACQUIRED_BUFFERS, err);
+        return err;
+    }
+
+    sp<BufferQueue::ProxyConsumerListener> proxy =
+        new BufferQueue::ProxyConsumerListener(NULL);
+    err = consumer->consumerConnect(proxy, false);
+    if (err != NO_ERROR) {
+        ALOGE("Error connecting to BufferQueue: %s (%d)",
+                strerror(-err), err);
+        return err;
+    }
+
+    *bufferProducer = producer;
+    *bufferConsumer = consumer;
+
+    return OK;
+}
+
+status_t OMXNodeInstance::setInputSurface(
+        OMX_U32 portIndex, const sp<IGraphicBufferConsumer> &bufferConsumer) {
+    Mutex::Autolock autolock(mLock);
+    return createGraphicBufferSource(portIndex, bufferConsumer);
+}
+
 status_t OMXNodeInstance::signalEndOfInputStream() {
     // For non-Surface input, the MediaCodec should convert the call to a
     // pair of requests (dequeue input buffer, queue input buffer with EOS
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 801a1bd..04303c4 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -598,7 +598,7 @@
 
         if (port->mTransition == PortInfo::DISABLING) {
             if (port->mBuffers.empty()) {
-                ALOGV("Port %d now disabled.", i);
+                ALOGV("Port %zu now disabled.", i);
 
                 port->mTransition = PortInfo::NONE;
                 notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL);
@@ -607,7 +607,7 @@
             }
         } else if (port->mTransition == PortInfo::ENABLING) {
             if (port->mDef.bPopulated == OMX_TRUE) {
-                ALOGV("Port %d now enabled.", i);
+                ALOGV("Port %zu now enabled.", i);
 
                 port->mTransition = PortInfo::NONE;
                 port->mDef.bEnabled = OMX_TRUE;
@@ -628,14 +628,14 @@
     info->mTransition = PortInfo::NONE;
 }
 
-void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) {
+void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex __unused) {
 }
 
-void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) {
+void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex __unused) {
 }
 
 void SimpleSoftOMXComponent::onPortEnableCompleted(
-        OMX_U32 portIndex, bool enabled) {
+        OMX_U32 portIndex __unused, bool enabled __unused) {
 }
 
 List<SimpleSoftOMXComponent::BufferInfo *> &
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
old mode 100644
new mode 100755
index 9b6958a..0f9c00c
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -40,11 +40,12 @@
     { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
     { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
     { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
-    { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
-    { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" },
+    { "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },
+    { "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },
     { "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },
     { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
     { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
+    { "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },
     { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
     { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
     { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
@@ -85,7 +86,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/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index 9be637a..02e97f1 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -11,7 +11,8 @@
 	$(TOP)/frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE := omx_tests
 
@@ -37,4 +38,7 @@
 LOCAL_C_INCLUDES := \
 	frameworks/av/media/libstagefright/omx \
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/omx/tests/FrameDropper_test.cpp b/media/libstagefright/omx/tests/FrameDropper_test.cpp
index 4ac72c4..f966b5e 100644
--- a/media/libstagefright/omx/tests/FrameDropper_test.cpp
+++ b/media/libstagefright/omx/tests/FrameDropper_test.cpp
@@ -100,7 +100,8 @@
         for (size_t i = 0; i < size; ++i) {
             int jitter = GetJitter(i);
             int64_t testTimeUs = frames[i].timeUs + jitter;
-            printf("time %lld, testTime %lld, jitter %d\n", frames[i].timeUs, testTimeUs, jitter);
+            printf("time %lld, testTime %lld, jitter %d\n",
+                    (long long)frames[i].timeUs, (long long)testTimeUs, jitter);
             EXPECT_EQ(frames[i].shouldDrop, mFrameDropper->shouldDrop(testTimeUs));
         }
     }
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index fdc8d23..be8bc13 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -32,7 +32,7 @@
 namespace android {
 
 struct ABuffer;
-struct MediaBuffer;
+class MediaBuffer;
 
 struct ARTPWriter : public MediaWriter {
     ARTPWriter(int fd);
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 9fedb71..c5e8c35 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -31,7 +31,8 @@
     LOCAL_CFLAGS += -Wno-psabi
 endif
 
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
@@ -54,7 +55,8 @@
 	frameworks/av/media/libstagefright \
 	$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 0642343..ba17e90 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -651,7 +651,7 @@
                 int32_t result;
                 CHECK(msg->findInt32("result", &result));
 
-                ALOGI("SETUP(%d) completed with result %d (%s)",
+                ALOGI("SETUP(%zu) completed with result %d (%s)",
                      index, result, strerror(-result));
 
                 if (result == OK) {
@@ -1012,7 +1012,7 @@
 
                 int32_t eos;
                 if (msg->findInt32("eos", &eos)) {
-                    ALOGI("received BYE on track index %d", trackIndex);
+                    ALOGI("received BYE on track index %zu", trackIndex);
                     if (!mAllTracksHaveTime && dataReceivedOnAllChannels()) {
                         ALOGI("No time established => fake existing data");
 
@@ -1564,7 +1564,7 @@
             new APacketSource(mSessionDesc, index);
 
         if (source->initCheck() != OK) {
-            ALOGW("Unsupported format. Ignoring track #%d.", index);
+            ALOGW("Unsupported format. Ignoring track #%zu.", index);
 
             sp<AMessage> reply = new AMessage('setu', this);
             reply->setSize("index", index);
@@ -1606,7 +1606,7 @@
         info->mTimeScale = timescale;
         info->mEOSReceived = false;
 
-        ALOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
+        ALOGV("track #%zu URL=%s", mTracks.size(), trackURL.c_str());
 
         AString request = "SETUP ";
         request.append(trackURL);
@@ -1673,21 +1673,11 @@
         }
 
         size_t n = strlen(baseURL);
-        if (baseURL[n - 1] == '/') {
-            out->setTo(baseURL);
-            out->append(url);
-        } else {
-            const char *slashPos = strrchr(baseURL, '/');
-
-            if (slashPos > &baseURL[6]) {
-                out->setTo(baseURL, slashPos - baseURL);
-            } else {
-                out->setTo(baseURL);
-            }
-
+        out->setTo(baseURL);
+        if (baseURL[n - 1] != '/') {
             out->append("/");
-            out->append(url);
         }
+        out->append(url);
 
         return true;
     }
@@ -1731,8 +1721,8 @@
     }
 
     void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
-        ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
-             trackIndex, rtpTime, ntpTime);
+        ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = %#016llx",
+             trackIndex, rtpTime, (long long)ntpTime);
 
         int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
 
@@ -1851,8 +1841,8 @@
             return false;
         }
 
-        ALOGV("track %d rtpTime=%d mediaTimeUs = %lld us (%.2f secs)",
-             trackIndex, rtpTime, mediaTimeUs, mediaTimeUs / 1E6);
+        ALOGV("track %d rtpTime=%u mediaTimeUs = %lld us (%.2f secs)",
+             trackIndex, rtpTime, (long long)mediaTimeUs, mediaTimeUs / 1E6);
 
         accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
 
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 8d6ff5b..111e6c5 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -31,6 +31,9 @@
 	frameworks/av/media/libstagefright/include \
 	$(TOP)/frameworks/native/include/media/openmax \
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 LOCAL_32_BIT_ONLY := true
 
 include $(BUILD_NATIVE_TEST)
@@ -60,6 +63,39 @@
 	frameworks/av/media/libstagefright/include \
 	$(TOP)/frameworks/native/include/media/openmax \
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := MediaCodecListOverrides_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+	MediaCodecListOverrides_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	libmedia \
+	libstagefright \
+	libstagefright_foundation \
+	libstagefright_omx \
+	libutils \
+	liblog
+
+LOCAL_C_INCLUDES := \
+	frameworks/av/media/libstagefright \
+	frameworks/av/media/libstagefright/include \
+	frameworks/native/include/media/openmax \
+
+LOCAL_32_BIT_ONLY := true
+
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 include $(BUILD_NATIVE_TEST)
 
 # Include subdirectory makefiles
diff --git a/media/libstagefright/tests/DummyRecorder.h b/media/libstagefright/tests/DummyRecorder.h
index 1cbea1b..cd4d0ee 100644
--- a/media/libstagefright/tests/DummyRecorder.h
+++ b/media/libstagefright/tests/DummyRecorder.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-class MediaSource;
+struct MediaSource;
 class MediaBuffer;
 
 class DummyRecorder {
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
new file mode 100644
index 0000000..cee62a3
--- /dev/null
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecListOverrides_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodecList.h>
+
+namespace android {
+
+static const char kTestOverridesStr[] =
+"<MediaCodecs>\n"
+"    <Settings>\n"
+"        <Setting name=\"supports-multiple-secure-codecs\" value=\"false\" />\n"
+"        <Setting name=\"supports-secure-with-non-secure-codec\" value=\"true\" />\n"
+"    </Settings>\n"
+"    <Encoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"    </Encoders>\n"
+"    <Decoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"1\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+"        </MediaCodec>\n"
+"    </Decoders>\n"
+"</MediaCodecs>\n";
+
+class MediaCodecListOverridesTest : public ::testing::Test {
+public:
+    MediaCodecListOverridesTest() {}
+
+    void addMaxInstancesSetting(
+            const AString &key,
+            const AString &value,
+            KeyedVector<AString, CodecSettings> *results) {
+        CodecSettings settings;
+        settings.add("max-supported-instances", value);
+        results->add(key, settings);
+    }
+
+    void verifyProfileResults(const KeyedVector<AString, CodecSettings> &results) {
+        EXPECT_LT(0u, results.size());
+        for (size_t i = 0; i < results.size(); ++i) {
+            AString key = results.keyAt(i);
+            CodecSettings settings = results.valueAt(i);
+            EXPECT_EQ(1u, settings.size());
+            EXPECT_TRUE(settings.keyAt(0) == "max-supported-instances");
+            AString valueS = settings.valueAt(0);
+            int32_t value = strtol(valueS.c_str(), NULL, 10);
+            EXPECT_LT(0, value);
+            ALOGV("profileCodecs results %s %s", key.c_str(), valueS.c_str());
+        }
+    }
+
+    void exportTestResultsToXML(const char *fileName) {
+        CodecSettings gR;
+        gR.add("supports-multiple-secure-codecs", "false");
+        gR.add("supports-secure-with-non-secure-codec", "true");
+        KeyedVector<AString, CodecSettings> eR;
+        addMaxInstancesSetting("OMX.qcom.video.encoder.avc video/avc", "4", &eR);
+        addMaxInstancesSetting("OMX.qcom.video.encoder.mpeg4 video/mp4v-es", "4", &eR);
+        KeyedVector<AString, CodecSettings> dR;
+        addMaxInstancesSetting("OMX.qcom.video.decoder.avc.secure video/avc", "1", &dR);
+        addMaxInstancesSetting("OMX.qcom.video.decoder.h263 video/3gpp", "4", &dR);
+        addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg2 video/mpeg2", "3", &dR);
+        addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg4 video/mp4v-es", "3", &dR);
+
+        exportResultsToXML(fileName, gR, eR, dR);
+    }
+};
+
+TEST_F(MediaCodecListOverridesTest, splitString) {
+    AString s = "abc123";
+    AString delimiter = " ";
+    AString s1;
+    AString s2;
+    EXPECT_FALSE(splitString(s, delimiter, &s1, &s2));
+    s = "abc 123";
+    EXPECT_TRUE(splitString(s, delimiter, &s1, &s2));
+    EXPECT_TRUE(s1 == "abc");
+    EXPECT_TRUE(s2 == "123");
+}
+
+// TODO: the codec component never returns OMX_EventCmdComplete in unit test.
+TEST_F(MediaCodecListOverridesTest, DISABLED_profileCodecs) {
+    sp<IMediaCodecList> list = MediaCodecList::getInstance();
+    Vector<sp<MediaCodecInfo>> infos;
+    for (size_t i = 0; i < list->countCodecs(); ++i) {
+        infos.push_back(list->getCodecInfo(i));
+    }
+    CodecSettings global_results;
+    KeyedVector<AString, CodecSettings> encoder_results;
+    KeyedVector<AString, CodecSettings> decoder_results;
+    profileCodecs(
+            infos, &global_results, &encoder_results, &decoder_results, true /* forceToMeasure */);
+    verifyProfileResults(encoder_results);
+    verifyProfileResults(decoder_results);
+}
+
+TEST_F(MediaCodecListOverridesTest, exportTestResultsToXML) {
+    const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml";
+    remove(fileName);
+
+    exportTestResultsToXML(fileName);
+
+    // verify
+    AString overrides;
+    FILE *f = fopen(fileName, "rb");
+    ASSERT_TRUE(f != NULL);
+    fseek(f, 0, SEEK_END);
+    long size = ftell(f);
+    rewind(f);
+
+    char *buf = (char *)malloc(size);
+    EXPECT_EQ((size_t)1, fread(buf, size, 1, f));
+    overrides.setTo(buf, size);
+    fclose(f);
+    free(buf);
+
+    EXPECT_TRUE(overrides == kTestOverridesStr);
+
+    remove(fileName);
+}
+
+} // namespace android
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index fd889f9..3860e9b 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest.h>
 #include <utils/String8.h>
+#include <utils/String16.h>
 #include <utils/Errors.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -466,7 +467,7 @@
 // Set up the MediaRecorder which runs in the same process as mediaserver
 sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource,
         int outputFormat, int videoEncoder, int width, int height, int fps) {
-    sp<MediaRecorder> mr = new MediaRecorder();
+    sp<MediaRecorder> mr = new MediaRecorder(String16());
     mr->setVideoSource(videoSource);
     mr->setOutputFormat(outputFormat);
     mr->setVideoEncoder(videoEncoder);
diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp
index 5c323c1..c1e663c 100644
--- a/media/libstagefright/tests/Utils_test.cpp
+++ b/media/libstagefright/tests/Utils_test.cpp
@@ -192,6 +192,87 @@
     ASSERT_EQ(max(-4.3, 8.6), 8.6);
     ASSERT_EQ(max(8.6, -4.3), 8.6);
 
+    ASSERT_FALSE(isInRange(-43, 86u, -44));
+    ASSERT_TRUE(isInRange(-43, 87u, -43));
+    ASSERT_TRUE(isInRange(-43, 88u, -1));
+    ASSERT_TRUE(isInRange(-43, 89u, 0));
+    ASSERT_TRUE(isInRange(-43, 90u, 46));
+    ASSERT_FALSE(isInRange(-43, 91u, 48));
+    ASSERT_FALSE(isInRange(-43, 92u, 50));
+
+    ASSERT_FALSE(isInRange(43, 86u, 42));
+    ASSERT_TRUE(isInRange(43, 87u, 43));
+    ASSERT_TRUE(isInRange(43, 88u, 44));
+    ASSERT_TRUE(isInRange(43, 89u, 131));
+    ASSERT_FALSE(isInRange(43, 90u, 133));
+    ASSERT_FALSE(isInRange(43, 91u, 135));
+
+    ASSERT_FALSE(isInRange(43u, 86u, 42u));
+    ASSERT_TRUE(isInRange(43u, 85u, 43u));
+    ASSERT_TRUE(isInRange(43u, 84u, 44u));
+    ASSERT_TRUE(isInRange(43u, 83u, 125u));
+    ASSERT_FALSE(isInRange(43u, 82u, 125u));
+    ASSERT_FALSE(isInRange(43u, 81u, 125u));
+
+    ASSERT_FALSE(isInRange(-43, ~0u, 43));
+    ASSERT_FALSE(isInRange(-43, ~0u, 44));
+    ASSERT_FALSE(isInRange(-43, ~0u, ~0));
+    ASSERT_FALSE(isInRange(-43, ~0u, 41));
+    ASSERT_FALSE(isInRange(-43, ~0u, 40));
+
+    ASSERT_FALSE(isInRange(43u, ~0u, 43u));
+    ASSERT_FALSE(isInRange(43u, ~0u, 41u));
+    ASSERT_FALSE(isInRange(43u, ~0u, 40u));
+    ASSERT_FALSE(isInRange(43u, ~0u, ~0u));
+
+    ASSERT_FALSE(isInRange(-43, 86u, -44, 0u));
+    ASSERT_FALSE(isInRange(-43, 86u, -44, 1u));
+    ASSERT_FALSE(isInRange(-43, 86u, -44, 2u));
+    ASSERT_FALSE(isInRange(-43, 86u, -44, ~0u));
+    ASSERT_TRUE(isInRange(-43, 87u, -43, 0u));
+    ASSERT_TRUE(isInRange(-43, 87u, -43, 1u));
+    ASSERT_TRUE(isInRange(-43, 87u, -43, 86u));
+    ASSERT_TRUE(isInRange(-43, 87u, -43, 87u));
+    ASSERT_FALSE(isInRange(-43, 87u, -43, 88u));
+    ASSERT_FALSE(isInRange(-43, 87u, -43, ~0u));
+    ASSERT_TRUE(isInRange(-43, 88u, -1, 0u));
+    ASSERT_TRUE(isInRange(-43, 88u, -1, 45u));
+    ASSERT_TRUE(isInRange(-43, 88u, -1, 46u));
+    ASSERT_FALSE(isInRange(-43, 88u, -1, 47u));
+    ASSERT_FALSE(isInRange(-43, 88u, -1, ~3u));
+    ASSERT_TRUE(isInRange(-43, 90u, 46, 0u));
+    ASSERT_TRUE(isInRange(-43, 90u, 46, 1u));
+    ASSERT_FALSE(isInRange(-43, 90u, 46, 2u));
+    ASSERT_FALSE(isInRange(-43, 91u, 48, 0u));
+    ASSERT_FALSE(isInRange(-43, 91u, 48, 2u));
+    ASSERT_FALSE(isInRange(-43, 91u, 48, ~6u));
+    ASSERT_FALSE(isInRange(-43, 92u, 50, 0u));
+    ASSERT_FALSE(isInRange(-43, 92u, 50, 1u));
+
+    ASSERT_FALSE(isInRange(43u, 86u, 42u, 0u));
+    ASSERT_FALSE(isInRange(43u, 86u, 42u, 1u));
+    ASSERT_FALSE(isInRange(43u, 86u, 42u, 2u));
+    ASSERT_FALSE(isInRange(43u, 86u, 42u, ~0u));
+    ASSERT_TRUE(isInRange(43u, 87u, 43u, 0u));
+    ASSERT_TRUE(isInRange(43u, 87u, 43u, 1u));
+    ASSERT_TRUE(isInRange(43u, 87u, 43u, 86u));
+    ASSERT_TRUE(isInRange(43u, 87u, 43u, 87u));
+    ASSERT_FALSE(isInRange(43u, 87u, 43u, 88u));
+    ASSERT_FALSE(isInRange(43u, 87u, 43u, ~0u));
+    ASSERT_TRUE(isInRange(43u, 88u, 60u, 0u));
+    ASSERT_TRUE(isInRange(43u, 88u, 60u, 70u));
+    ASSERT_TRUE(isInRange(43u, 88u, 60u, 71u));
+    ASSERT_FALSE(isInRange(43u, 88u, 60u, 72u));
+    ASSERT_FALSE(isInRange(43u, 88u, 60u, ~3u));
+    ASSERT_TRUE(isInRange(43u, 90u, 132u, 0u));
+    ASSERT_TRUE(isInRange(43u, 90u, 132u, 1u));
+    ASSERT_FALSE(isInRange(43u, 90u, 132u, 2u));
+    ASSERT_FALSE(isInRange(43u, 91u, 134u, 0u));
+    ASSERT_FALSE(isInRange(43u, 91u, 134u, 2u));
+    ASSERT_FALSE(isInRange(43u, 91u, 134u, ~6u));
+    ASSERT_FALSE(isInRange(43u, 92u, 136u, 0u));
+    ASSERT_FALSE(isInRange(43u, 92u, 136u, 1u));
+
     ASSERT_EQ(periodicError(124, 100), 24);
     ASSERT_EQ(periodicError(288, 100), 12);
     ASSERT_EQ(periodicError(-345, 100), 45);
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 6a8b9fc..58fb12f 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -9,7 +9,8 @@
         TimedTextSRTSource.cpp    \
         TimedTextPlayer.cpp
 
-LOCAL_CFLAGS += -Wno-multichar -Werror
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_C_INCLUDES:= \
         $(TOP)/frameworks/av/include/media/stagefright/timedtext \
diff --git a/media/libstagefright/timedtext/test/Android.mk b/media/libstagefright/timedtext/test/Android.mk
index 9a9fde2..e0e0e0d 100644
--- a/media/libstagefright/timedtext/test/Android.mk
+++ b/media/libstagefright/timedtext/test/Android.mk
@@ -26,4 +26,7 @@
     libstagefright_foundation \
     libutils
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 include $(BUILD_NATIVE_TEST)
diff --git a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
index 3a06d61..211e732 100644
--- a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
+++ b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
@@ -63,10 +63,10 @@
     }
 
     virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
-        if (offset >= mSize) return 0;
+        if ((size_t)offset >= mSize) return 0;
 
         ssize_t avail = mSize - offset;
-        if (avail > size) {
+        if ((size_t)avail > size) {
             avail = size;
         }
         memcpy(data, mData + offset, avail);
diff --git a/media/libstagefright/webm/Android.mk b/media/libstagefright/webm/Android.mk
index 7081463..bc53c56 100644
--- a/media/libstagefright/webm/Android.mk
+++ b/media/libstagefright/webm/Android.mk
@@ -1,8 +1,10 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_CPPFLAGS += -D__STDINT_LIMITS \
-                  -Werror
+LOCAL_CPPFLAGS += -D__STDINT_LIMITS
+
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 LOCAL_SRC_FILES:= EbmlUtil.cpp        \
                   WebmElement.cpp     \
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index f70454a..fb28624 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -30,6 +30,9 @@
         libui                           \
         libutils                        \
 
+LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
+LOCAL_CLANG := true
+
 LOCAL_MODULE:= libstagefright_wfd
 
 LOCAL_MODULE_TAGS:= optional
diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp
index 2f4af5b..dbc511c 100644
--- a/media/libstagefright/wifi-display/VideoFormats.cpp
+++ b/media/libstagefright/wifi-display/VideoFormats.cpp
@@ -248,8 +248,8 @@
     }
 
     if (bestProfile == -1 || bestLevel == -1) {
-        ALOGE("Profile or level not set for resolution type %d, index %d",
-              type, index);
+        ALOGE("Profile or level not set for resolution type %d, index %zu",
+                type, index);
         bestProfile = PROFILE_CBP;
         bestLevel = LEVEL_31;
     }
@@ -382,7 +382,6 @@
     disableAll();
 
     unsigned native, dummy;
-    unsigned res[3];
     size_t size = strlen(spec);
     size_t offset = 0;
 
@@ -507,7 +506,7 @@
                 continue;
             }
 
-            ALOGV("type %u, index %u, %u x %u %c%u supported",
+            ALOGV("type %zu, index %zu, %zu x %zu %c%zu supported",
                   i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
 
             uint32_t score = width * height * framesPerSecond;
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index 4e72533..c66a898 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -252,8 +252,6 @@
     int64_t timeUs;
     CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs));
 
-    const size_t numTSPackets = tsPackets->size() / 188;
-
     size_t srcOffset = 0;
     while (srcOffset < tsPackets->size()) {
         sp<ABuffer> udpPacket =
@@ -672,8 +670,8 @@
 
             default:
             {
-                ALOGW("Unknown RTCP packet type %u of size %d",
-                     (unsigned)data[1], headerLength);
+                ALOGW("Unknown RTCP packet type %u of size %zu",
+                        (unsigned)data[1], headerLength);
                 break;
             }
         }
@@ -764,7 +762,7 @@
     return OK;
 }
 
-status_t RTPSender::parseAPP(const uint8_t *data, size_t size) {
+status_t RTPSender::parseAPP(const uint8_t *data, size_t size __unused) {
     if (!memcmp("late", &data[8], 4)) {
         int64_t avgLatencyUs = (int64_t)U64_AT(&data[12]);
         int64_t maxLatencyUs = (int64_t)U64_AT(&data[20]);
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 8368945..471152e 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -747,7 +747,7 @@
             buffer->meta()->setInt64("timeUs", timeUs);
 
             ALOGV("[%s] time %lld us (%.2f secs)",
-                  mIsVideo ? "video" : "audio", timeUs, timeUs / 1E6);
+                    mIsVideo ? "video" : "audio", (long long)timeUs, timeUs / 1E6);
 
             memcpy(buffer->data(), outbuf->base() + offset, size);
 
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
index 5876e07..b182990 100644
--- a/media/libstagefright/wifi-display/source/Converter.h
+++ b/media/libstagefright/wifi-display/source/Converter.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct ABuffer;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
 struct MediaCodec;
 
 #define ENABLE_SILENCE_DETECTION        0
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 6080943..ed5a404 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -345,12 +345,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 WifiDisplaySource::PlaybackSession::PlaybackSession(
+        const String16 &opPackageName,
         const sp<ANetworkSession> &netSession,
         const sp<AMessage> &notify,
         const in_addr &interfaceAddr,
         const sp<IHDCP> &hdcp,
         const char *path)
-    : mNetSession(netSession),
+    : mOpPackageName(opPackageName),
+      mNetSession(netSession),
       mNotify(notify),
       mInterfaceAddr(interfaceAddr),
       mHDCP(hdcp),
@@ -508,7 +510,7 @@
             } else if (what == Converter::kWhatEOS) {
                 CHECK_EQ(what, Converter::kWhatEOS);
 
-                ALOGI("output EOS on track %d", trackIndex);
+                ALOGI("output EOS on track %zu", trackIndex);
 
                 ssize_t index = mTracks.indexOfKey(trackIndex);
                 CHECK_GE(index, 0);
@@ -581,7 +583,7 @@
             CHECK(msg->findSize("trackIndex", &trackIndex));
 
             if (what == Track::kWhatStopped) {
-                ALOGI("Track %d stopped", trackIndex);
+                ALOGI("Track %zu stopped", trackIndex);
 
                 sp<Track> track = mTracks.valueFor(trackIndex);
                 looper()->unregisterHandler(track->id());
@@ -821,21 +823,27 @@
         return;
     }
 
+    int64_t delayUs = 1000000; // default delay is 1 sec
     int64_t sampleTimeUs;
     status_t err = mExtractor->getSampleTime(&sampleTimeUs);
 
-    int64_t nowUs = ALooper::GetNowUs();
+    if (err == OK) {
+        int64_t nowUs = ALooper::GetNowUs();
 
-    if (mFirstSampleTimeRealUs < 0ll) {
-        mFirstSampleTimeRealUs = nowUs;
-        mFirstSampleTimeUs = sampleTimeUs;
+        if (mFirstSampleTimeRealUs < 0ll) {
+            mFirstSampleTimeRealUs = nowUs;
+            mFirstSampleTimeUs = sampleTimeUs;
+        }
+
+        int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
+        delayUs = whenUs - nowUs;
+    } else {
+        ALOGW("could not get sample time (%d)", err);
     }
 
-    int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
-
     sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, this);
     msg->setInt32("generation", mPullExtractorGeneration);
-    msg->post(whenUs - nowUs);
+    msg->post(delayUs);
 
     mPullExtractorPending = true;
 }
@@ -1063,6 +1071,7 @@
 status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) {
     sp<AudioSource> audioSource = new AudioSource(
             AUDIO_SOURCE_REMOTE_SUBMIX,
+            mOpPackageName,
             48000 /* sampleRate */,
             2 /* channelCount */);
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 2824143..f6673df 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -22,11 +22,13 @@
 #include "VideoFormats.h"
 #include "WifiDisplaySource.h"
 
+#include <utils/String16.h>
+
 namespace android {
 
 struct ABuffer;
 struct IHDCP;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
 struct MediaPuller;
 struct MediaSource;
 struct MediaSender;
@@ -36,6 +38,7 @@
 // display.
 struct WifiDisplaySource::PlaybackSession : public AHandler {
     PlaybackSession(
+            const String16 &opPackageName,
             const sp<ANetworkSession> &netSession,
             const sp<AMessage> &notify,
             const struct in_addr &interfaceAddr,
@@ -96,6 +99,8 @@
         kWhatPullExtractorSample,
     };
 
+    String16 mOpPackageName;
+
     sp<ANetworkSession> mNetSession;
     sp<AMessage> mNotify;
     in_addr mInterfaceAddr;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 14d0951..e26165e 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -50,10 +50,12 @@
 const AString WifiDisplaySource::sUserAgent = MakeUserAgent();
 
 WifiDisplaySource::WifiDisplaySource(
+        const String16 &opPackageName,
         const sp<ANetworkSession> &netSession,
         const sp<IRemoteDisplayClient> &client,
         const char *path)
-    : mState(INITIALIZED),
+    : mOpPackageName(opPackageName),
+      mState(INITIALIZED),
       mNetSession(netSession),
       mClient(client),
       mSessionID(0),
@@ -881,7 +883,7 @@
                     &framesPerSecond,
                     &interlaced));
 
-        ALOGI("Picked video resolution %u x %u %c%u",
+        ALOGI("Picked video resolution %zu x %zu %c%zu",
               width, height, interlaced ? 'i' : 'p', framesPerSecond);
 
         ALOGI("Picked AVC profile %d, level %d",
@@ -1245,7 +1247,7 @@
 
     sp<PlaybackSession> playbackSession =
         new PlaybackSession(
-                mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
+                mOpPackageName, mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
 
     looper()->registerHandler(playbackSession);
 
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 0f779e4..c25a675 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -25,11 +25,13 @@
 
 #include <netinet/in.h>
 
+#include <utils/String16.h>
+
 namespace android {
 
 struct AReplyToken;
 struct IHDCP;
-struct IRemoteDisplayClient;
+class IRemoteDisplayClient;
 struct ParsedMessage;
 
 // Represents the RTSP server acting as a wifi display source.
@@ -38,6 +40,7 @@
     static const unsigned kWifiDisplayDefaultPort = 7236;
 
     WifiDisplaySource(
+            const String16 &opPackageName,
             const sp<ANetworkSession> &netSession,
             const sp<IRemoteDisplayClient> &client,
             const char *path = NULL);
@@ -114,6 +117,8 @@
 
     static const AString sUserAgent;
 
+    String16 mOpPackageName;
+
     State mState;
     VideoFormats mSupportedSourceVideoFormats;
     sp<ANetworkSession> mNetSession;
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
index bb86dfc..dc67288 100644
--- a/media/libstagefright/yuv/Android.mk
+++ b/media/libstagefright/yuv/Android.mk
@@ -12,7 +12,7 @@
 LOCAL_MODULE:= libstagefright_yuv
 
 
-LOCAL_CFLAGS += -Werror
-
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index 0e2e48c..ba47172 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -45,7 +45,8 @@
     frameworks/av/services/mediaresourcemanager \
     $(call include-path-for, audio-utils) \
     frameworks/av/services/soundtrigger \
-    frameworks/av/services/radio
+    frameworks/av/services/radio \
+    external/sonic
 
 LOCAL_MODULE:= mediaserver
 LOCAL_32_BIT_ONLY := true
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 99572f8..06b3c6e 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -33,6 +33,7 @@
 #include "CameraService.h"
 #include "MediaLogService.h"
 #include "MediaPlayerService.h"
+#include "ResourceManagerService.h"
 #include "service/AudioPolicyService.h"
 #include "SoundTriggerHwService.h"
 #include "RadioService.h"
@@ -128,6 +129,7 @@
         ALOGI("ServiceManager: %p", sm.get());
         AudioFlinger::instantiate();
         MediaPlayerService::instantiate();
+        ResourceManagerService::instantiate();
         CameraService::instantiate();
         AudioPolicyService::instantiate();
         SoundTriggerHwService::instantiate();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 80c1c2f..cd0c462 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -154,6 +154,10 @@
     } else {
         mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
     }
+    if (mData->mCodec == NULL) {  // failed to create codec
+        AMediaCodec_delete(mData);
+        return NULL;
+    }
     mData->mHandler = new CodecHandler(mData);
     mData->mLooper->registerHandler(mData->mHandler);
     mData->mGeneration = 1;
@@ -180,17 +184,21 @@
 
 EXPORT
 media_status_t AMediaCodec_delete(AMediaCodec *mData) {
-    if (mData->mCodec != NULL) {
-        mData->mCodec->release();
-        mData->mCodec.clear();
-    }
+    if (mData != NULL) {
+        if (mData->mCodec != NULL) {
+            mData->mCodec->release();
+            mData->mCodec.clear();
+        }
 
-    if (mData->mLooper != NULL) {
-        mData->mLooper->unregisterHandler(mData->mHandler->id());
-        mData->mLooper->stop();
-        mData->mLooper.clear();
+        if (mData->mLooper != NULL) {
+            if (mData->mHandler != NULL) {
+                mData->mLooper->unregisterHandler(mData->mHandler->id());
+            }
+            mData->mLooper->stop();
+            mData->mLooper.clear();
+        }
+        delete mData;
     }
-    delete mData;
     return AMEDIA_OK;
 }
 
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index fee2347..debcdf9 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -29,12 +29,6 @@
 
 include $(CLEAR_VARS)
 
-# Clang++ aborts on AudioMixer.cpp,
-# b/18373866, "do not know how to split this operator."
-ifeq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH))
-    LOCAL_CLANG := false
-endif
-
 LOCAL_SRC_FILES:=               \
     AudioFlinger.cpp            \
     Threads.cpp                 \
@@ -44,12 +38,13 @@
     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 \
+    $(TOPDIR)external/sonic \
     $(call include-path-for, audio-effects) \
     $(call include-path-for, audio-utils)
 
@@ -68,7 +63,8 @@
     libhardware_legacy \
     libeffects \
     libpowermanager \
-    libserviceutility
+    libserviceutility \
+    libsonic
 
 LOCAL_STATIC_LIBRARIES := \
     libscheduling_policy \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f3206cb..93b1642 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>
@@ -755,8 +757,12 @@
     // assigned to HALs which do not have master volume support will apply
     // master volume during the mix operation.  Threads with HALs which do
     // support master volume will simply ignore the setting.
-    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+        if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
+            continue;
+        }
         mPlaybackThreads.valueAt(i)->setMasterVolume(value);
+    }
 
     return NO_ERROR;
 }
@@ -873,8 +879,12 @@
     // assigned to HALs which do not have master mute support will apply master
     // mute during the mix operation.  Threads with HALs which do support master
     // mute will simply ignore the setting.
-    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+        if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
+            continue;
+        }
         mPlaybackThreads.valueAt(i)->setMasterMute(muted);
+    }
 
     return NO_ERROR;
 }
@@ -1011,6 +1021,14 @@
     return streamMute_l(stream);
 }
 
+
+void AudioFlinger::broacastParametersToRecordThreads_l(const String8& keyValuePairs)
+{
+    for (size_t i = 0; i < mRecordThreads.size(); i++) {
+        mRecordThreads.valueAt(i)->setParameters(keyValuePairs);
+    }
+}
+
 status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
 {
     ALOGV("setParameters(): io %d, keyvalue %s, calling pid %d",
@@ -1085,9 +1103,7 @@
             int value;
             if ((param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) &&
                     (value != 0)) {
-                for (size_t i = 0; i < mRecordThreads.size(); i++) {
-                    mRecordThreads.valueAt(i)->setParameters(keyValuePairs);
-                }
+                broacastParametersToRecordThreads_l(keyValuePairs);
             }
         }
     }
@@ -1140,19 +1156,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
@@ -1233,11 +1276,11 @@
         // the config change is always sent from playback or record threads to avoid deadlock
         // with AudioSystem::gLock
         for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-            mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::OUTPUT_OPENED);
+            mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED);
         }
 
         for (size_t i = 0; i < mRecordThreads.size(); i++) {
-            mRecordThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::INPUT_OPENED);
+            mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED);
         }
     }
 }
@@ -1271,14 +1314,13 @@
     }
 }
 
-void AudioFlinger::audioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2)
+void AudioFlinger::ioConfigChanged(audio_io_config_event event,
+                                   const sp<AudioIoDescriptor>& ioDesc)
 {
     Mutex::Autolock _l(mClientLock);
     size_t size = mNotificationClients.size();
     for (size_t i = 0; i < size; i++) {
-        mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event,
-                                                                              ioHandle,
-                                                                              param2);
+        mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
     }
 }
 
@@ -1387,9 +1429,11 @@
         uint32_t sampleRate,
         audio_format_t format,
         audio_channel_mask_t channelMask,
+        const String16& opPackageName,
         size_t *frameCount,
         IAudioFlinger::track_flags_t *flags,
         pid_t tid,
+        int clientUid,
         int *sessionId,
         size_t *notificationFrames,
         sp<IMemory>& cblk,
@@ -1406,7 +1450,7 @@
     buffers.clear();
 
     // check calling permissions
-    if (!recordingAllowed()) {
+    if (!recordingAllowed(opPackageName)) {
         ALOGE("openRecord() permission denied: recording not allowed");
         lStatus = PERMISSION_DENIED;
         goto Exit;
@@ -1419,9 +1463,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;
@@ -1460,8 +1503,7 @@
         // TODO: the uid should be passed in as a parameter to openRecord
         recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
                                                   frameCount, lSessionId, notificationFrames,
-                                                  IPCThreadState::self()->getCallingUid(),
-                                                  flags, tid, &lStatus);
+                                                  clientUid, flags, tid, &lStatus);
         LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
 
         if (lStatus == NO_ERROR) {
@@ -1797,7 +1839,7 @@
         *latencyMs = thread->latency();
 
         // notify client processes of the new output creation
-        thread->audioConfigChanged(AudioSystem::OUTPUT_OPENED);
+        thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
 
         // the first primary output opened designates the primary hw device
         if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
@@ -1835,7 +1877,7 @@
     thread->addOutputTrack(thread2);
     mPlaybackThreads.add(id, thread);
     // notify client processes of the new output creation
-    thread->audioConfigChanged(AudioSystem::OUTPUT_OPENED);
+    thread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
     return id;
 }
 
@@ -1860,11 +1902,10 @@
 
         if (thread->type() == ThreadBase::MIXER) {
             for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-                if (mPlaybackThreads.valueAt(i)->type() == ThreadBase::DUPLICATING) {
+                if (mPlaybackThreads.valueAt(i)->isDuplicating()) {
                     DuplicatingThread *dupThread =
                             (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
                     dupThread->removeOutputTrack((MixerThread *)thread.get());
-
                 }
             }
         }
@@ -1885,13 +1926,15 @@
                 }
             }
         }
-        audioConfigChanged(AudioSystem::OUTPUT_CLOSED, output, NULL);
+        const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
+        ioDesc->mIoHandle = output;
+        ioConfigChanged(AUDIO_OUTPUT_CLOSED, ioDesc);
     }
     thread->exit();
     // The thread entity (active unit of execution) is no longer running here,
     // but the ThreadBase container still exists.
 
-    if (thread->type() != ThreadBase::DUPLICATING) {
+    if (!thread->isDuplicating()) {
         closeOutputFinish(thread);
     }
 
@@ -1963,7 +2006,7 @@
 
     if (thread != 0) {
         // notify client processes of the new input creation
-        thread->audioConfigChanged(AudioSystem::INPUT_OPENED);
+        thread->ioConfigChanged(AUDIO_INPUT_OPENED);
         return NO_ERROR;
     }
     return NO_INIT;
@@ -2002,11 +2045,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)
@@ -2146,7 +2189,9 @@
                 putOrphanEffectChain_l(chain);
             }
         }
-        audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
+        const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
+        ioDesc->mIoHandle = input;
+        ioConfigChanged(AUDIO_INPUT_CLOSED, ioDesc);
         mRecordThreads.removeItem(input);
     }
     // FIXME: calling thread->exit() without mLock held should not be needed anymore now that
@@ -2337,6 +2382,9 @@
 {
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+        if(thread->isDuplicating()) {
+            continue;
+        }
         AudioStreamOut *output = thread->getOutput();
         if (output != NULL && output->audioHwDev == mPrimaryHardwareDev) {
             return thread;
@@ -2419,6 +2467,7 @@
         int32_t priority,
         audio_io_handle_t io,
         int sessionId,
+        const String16& opPackageName,
         status_t *status,
         int *id,
         int *enabled)
@@ -2515,7 +2564,7 @@
 
         // check recording permission for visualizer
         if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
-            !recordingAllowed()) {
+            !recordingAllowed(opPackageName)) {
             lStatus = PERMISSION_DENIED;
             goto Exit;
         }
@@ -2650,7 +2699,7 @@
     // Check whether the destination thread has a channel count of FCC_2, which is
     // currently required for (most) effects. Prevent moving the effect chain here rather
     // than disabling the addEffect_l() call in dstThread below.
-    if ((dstThread->type() == ThreadBase::MIXER || dstThread->type() == ThreadBase::DUPLICATING) &&
+    if ((dstThread->type() == ThreadBase::MIXER || dstThread->isDuplicating()) &&
             dstThread->mChannelCount != FCC_2) {
         ALOGW("moveEffectChain_l() effect chain failed because"
                 " destination thread %p channel count(%u) != %u",
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c7d9161..51b2610 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -73,18 +73,18 @@
 class AudioBuffer;
 class AudioResampler;
 class FastMixer;
+class PassthruBufferProvider;
 class ServerProxy;
 
 // ----------------------------------------------------------------------------
 
-// AudioFlinger has a hard-coded upper limit of 2 channels for capture and playback.
-// There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect.
-// Adding full support for > 2 channel capture or playback would require more than simply changing
-// this #define.  There is an independent hard-coded upper limit in AudioMixer;
-// removing that AudioMixer limit would be necessary but insufficient to support > 2 channels.
-// The macro FCC_2 highlights some (but not all) places where there is are 2-channel assumptions.
+// The macro FCC_2 highlights some (but not all) places where there are are 2-channel assumptions.
+// This is typically due to legacy implementation of stereo input or output.
 // Search also for "2", "left", "right", "[0]", "[1]", ">> 16", "<< 16", etc.
 #define FCC_2 2     // FCC_2 = Fixed Channel Count 2
+// The macro FCC_8 highlights places where there are 8-channel assumptions.
+// This is typically due to audio mixer and resampler limitations.
+#define FCC_8 8     // FCC_8 = Fixed Channel Count 8
 
 static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
 
@@ -120,9 +120,11 @@
                                 uint32_t sampleRate,
                                 audio_format_t format,
                                 audio_channel_mask_t channelMask,
+                                const String16& opPackageName,
                                 size_t *pFrameCount,
                                 IAudioFlinger::track_flags_t *flags,
                                 pid_t tid,
+                                int clientUid,
                                 int *sessionId,
                                 size_t *notificationFrames,
                                 sp<IMemory>& cblk,
@@ -216,6 +218,7 @@
                         int32_t priority,
                         audio_io_handle_t io,
                         int sessionId,
+                        const String16& opPackageName,
                         status_t *status /*non-NULL*/,
                         int *id,
                         int *enabled);
@@ -543,7 +546,8 @@
               // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
               float streamVolume_l(audio_stream_type_t stream) const
                                 { return mStreamTypes[stream].volume; }
-              void audioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
+              void ioConfigChanged(audio_io_config_event event,
+                                   const sp<AudioIoDescriptor>& ioDesc);
 
               // Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t.
               // They all share the same ID space, but the namespaces are actually independent
@@ -588,6 +592,7 @@
                 // Return true if the effect was found in mOrphanEffectChains, false otherwise.
                 bool            updateOrphanEffectChains(const sp<EffectModule>& effect);
 
+                void broacastParametersToRecordThreads_l(const String8& keyValuePairs);
 
     // AudioStreamIn is immutable, so their fields are const.
     // For emphasis, we could also make all pointers to them be "const *",
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..586c737 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -39,9 +39,6 @@
 #include <common_time/local_clock.h>
 #include <common_time/cc_helper.h>
 
-#include <media/EffectsFactoryApi.h>
-#include <audio_effects/effect_downmix.h>
-
 #include "AudioMixerOps.h"
 #include "AudioMixer.h"
 
@@ -91,323 +88,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 +122,7 @@
         t->resampler = NULL;
         t->downmixerBufferProvider = NULL;
         t->mReformatBufferProvider = NULL;
+        t->mTimestretchBufferProvider = NULL;
         t++;
     }
 
@@ -454,6 +135,7 @@
         delete t->resampler;
         delete t->downmixerBufferProvider;
         delete t->mReformatBufferProvider;
+        delete t->mTimestretchBufferProvider;
         t++;
     }
     delete [] mState.outputTemp;
@@ -532,6 +214,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 +222,7 @@
         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->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
         // Check the downmixing (or upmixing) requirements.
         status_t status = t->prepareForDownmix();
         if (status != OK) {
@@ -633,7 +317,7 @@
 
     // discard the previous downmixer if there was one
     unprepareForDownmix();
-    // Only remix (upmix or downmix) if the track and mixer/device channel masks
+    // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
     // are not the same and not handled internally, as mono -> stereo currently is.
     if (channelMask == mMixerChannelMask
             || (channelMask == AUDIO_CHANNEL_OUT_MONO
@@ -731,6 +415,10 @@
         mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
         bufferProvider = mPostDownmixReformatBufferProvider;
     }
+    if (mTimestretchBufferProvider) {
+        mTimestretchBufferProvider->setBufferProvider(bufferProvider);
+        bufferProvider = mTimestretchBufferProvider;
+    }
 }
 
 void AudioMixer::deleteTrackName(int name)
@@ -751,7 +439,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 +663,32 @@
             }
         }
         break;
+        case TIMESTRETCH:
+            switch (param) {
+            case PLAYBACK_RATE: {
+                const AudioPlaybackRate *playbackRate =
+                        reinterpret_cast<AudioPlaybackRate*>(value);
+                ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= playbackRate->mSpeed
+                        && playbackRate->mSpeed <= AUDIO_TIMESTRETCH_SPEED_MAX,
+                        "bad speed %f", playbackRate->mSpeed);
+                ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= playbackRate->mPitch
+                        && playbackRate->mPitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
+                        "bad pitch %f", playbackRate->mPitch);
+                //TODO: use function from AudioResamplerPublic.h to test validity.
+                if (track.setPlaybackRate(*playbackRate)) {
+                    ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
+                            "%f %f %d %d",
+                            playbackRate->mSpeed,
+                            playbackRate->mPitch,
+                            playbackRate->mStretchMode,
+                            playbackRate->mFallbackMode);
+                    // invalidateState(1 << name);
+                }
+            } break;
+            default:
+                LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
+            }
+            break;
 
     default:
         LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
@@ -992,11 +708,10 @@
                 // FIXME this is flawed for dynamic sample rates, as we choose the resampler
                 // quality level based on the initial ratio, but that could change later.
                 // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
-                if (!((trackSampleRate == 44100 && devSampleRate == 48000) ||
-                      (trackSampleRate == 48000 && devSampleRate == 44100))) {
-                    quality = AudioResampler::DYN_LOW_QUALITY;
-                } else {
+                if (isMusicRate(trackSampleRate)) {
                     quality = AudioResampler::DEFAULT_QUALITY;
+                } else {
+                    quality = AudioResampler::DYN_LOW_QUALITY;
                 }
 
                 // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
@@ -1018,6 +733,30 @@
     return false;
 }
 
+bool AudioMixer::track_t::setPlaybackRate(const AudioPlaybackRate &playbackRate)
+{
+    if ((mTimestretchBufferProvider == NULL &&
+            fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
+            fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) ||
+            isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
+        return false;
+    }
+    mPlaybackRate = playbackRate;
+    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, playbackRate);
+        reconfigureBufferProviders();
+    } else {
+        reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
+                ->setPlaybackRate(playbackRate);
+    }
+    return true;
+}
+
 /* Checks to see if the volume ramp has completed and clears the increment
  * variables appropriately.
  *
@@ -1035,7 +774,8 @@
 {
     if (useFloat) {
         for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
-            if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) {
+            if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) ||
+                     (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) {
                 volumeInc[i] = 0;
                 prevVolume[i] = volume[i] << 16;
                 mVolumeInc[i] = 0.;
@@ -1096,6 +836,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;
@@ -1178,7 +920,8 @@
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                     t.hook = getTrackHook(
-                            t.mMixerChannelCount == 2 // TODO: MONO_HACK.
+                            (t.mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO  // TODO: MONO_HACK
+                                    && t.channelMask == AUDIO_CHANNEL_OUT_MONO)
                                 ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
                             t.mMixerChannelCount,
                             t.mMixerInFormat, t.mMixerFormat);
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 381036b..7165c6c 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -23,12 +23,14 @@
 
 #include <hardware/audio_effect.h>
 #include <media/AudioBufferProvider.h>
+#include <media/AudioResamplerPublic.h>
 #include <media/nbaio/NBLog.h>
 #include <system/audio.h>
 #include <utils/Compat.h>
 #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 +74,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 +102,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 +165,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 +219,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 +231,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 +260,8 @@
         audio_channel_mask_t mMixerChannelMask;
         uint32_t             mMixerChannelCount;
 
+        AudioPlaybackRate    mPlaybackRate;
+
         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(const AudioPlaybackRate &playbackRate);
         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..8a580e8
--- /dev/null
+++ b/services/audioflinger/BufferProviders.cpp
@@ -0,0 +1,537 @@
+/*
+ * 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);
+    (void) memcpy_by_index_array_initialization_from_channel_mask(
+            mIdxAry, ARRAY_SIZE(mIdxAry), outputChannelMask, inputChannelMask);
+}
+
+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, const AudioPlaybackRate &playbackRate) :
+        mChannelCount(channelCount),
+        mFormat(format),
+        mSampleRate(sampleRate),
+        mFrameSize(channelCount * audio_bytes_per_sample(format)),
+        mLocalBufferFrameCount(0),
+        mLocalBufferData(NULL),
+        mRemaining(0),
+        mSonicStream(sonicCreateStream(sampleRate, mChannelCount)),
+        mFallbackFailErrorShown(false)
+{
+    LOG_ALWAYS_FATAL_IF(mSonicStream == NULL,
+            "TimestretchBufferProvider can't allocate Sonic stream");
+
+    setPlaybackRate(playbackRate);
+    ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f %d %d)",
+            this, channelCount, format, sampleRate, playbackRate.mSpeed,
+            playbackRate.mPitch, playbackRate.mStretchMode, playbackRate.mFallbackMode);
+    mBuffer.frameCount = 0;
+}
+
+TimestretchBufferProvider::~TimestretchBufferProvider()
+{
+    ALOGV("~TimestretchBufferProvider(%p)", this);
+    sonicDestroyStream(mSonicStream);
+    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 = mPlaybackRate.mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
+            ? outputDesired : outputDesired * mPlaybackRate.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(const AudioPlaybackRate &playbackRate)
+{
+    mPlaybackRate = playbackRate;
+    mFallbackFailErrorShown = false;
+    sonicSetSpeed(mSonicStream, mPlaybackRate.mSpeed);
+    //TODO: pitch is ignored for now
+    //TODO: optimize: if parameters are the same, don't do any extra computation.
+    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.
+    //TODO: add logic to track "very accurate" consumption related to speed, original sampling
+    //rate, actual frames processed.
+    const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed;
+    if (*srcFrames < targetSrc) { // limit dst frames to that possible
+        *dstFrames = *srcFrames / mPlaybackRate.mSpeed;
+    } else if (*srcFrames > targetSrc + 1) {
+        *srcFrames = targetSrc + 1;
+    }
+
+    if (mPlaybackRate.mSpeed< TIMESTRETCH_SONIC_SPEED_MIN  ||
+            mPlaybackRate.mSpeed >  TIMESTRETCH_SONIC_SPEED_MAX ) {
+        //fallback mode
+        if (*dstFrames > 0) {
+            switch(mPlaybackRate.mFallbackMode) {
+            case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
+                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 * remaining);
+                      }
+                  }
+                break;
+            case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
+            case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
+                memset(dstBuffer,0, mFrameSize * *dstFrames);
+                break;
+            case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
+            default:
+                if(!mFallbackFailErrorShown) {
+                    ALOGE("invalid parameters in TimestretchBufferProvider fallbackMode:%d",
+                            mPlaybackRate.mFallbackMode);
+                    mFallbackFailErrorShown = true;
+                }
+                break;
+            }
+        }
+    } else {
+        switch (mFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) {
+                ALOGE("sonicWriteFloatToStream cannot realloc");
+                *srcFrames = 0; // cannot consume all of srcBuffer
+            }
+            *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames);
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) {
+                ALOGE("sonicWriteShortToStream cannot realloc");
+                *srcFrames = 0; // cannot consume all of srcBuffer
+            }
+            *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames);
+            break;
+        default:
+            // could also be caught on construction
+            LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat);
+        }
+    }
+}
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h
new file mode 100644
index 0000000..4970b6c
--- /dev/null
+++ b/services/audioflinger/BufferProviders.h
@@ -0,0 +1,196 @@
+/*
+ * 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>
+#include <sonic.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,
+            const AudioPlaybackRate &playbackRate);
+    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(const AudioPlaybackRate &playbackRate);
+
+    // 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;
+    AudioPlaybackRate    mPlaybackRate;
+
+private:
+    AudioBufferProvider::Buffer mBuffer;          // for upstream request
+    size_t               mLocalBufferFrameCount;  // size of local buffer
+    void                *mLocalBufferData;        // internally allocated buffer for data returned
+                                                  // to caller
+    size_t               mRemaining;              // remaining data in local buffer
+    sonicStream          mSonicStream;            // handle to sonic timestretch object
+    //FIXME: this dependency should be abstracted out
+    bool                 mFallbackFailErrorShown; // log fallback error only once
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_BUFFER_PROVIDERS_H
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index 9e7e8a4..79ac12b 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -105,7 +105,7 @@
             mFormat = mInputSource->format();
             mSampleRate = Format_sampleRate(mFormat);
             unsigned channelCount = Format_channelCount(mFormat);
-            ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+            ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8);
         }
         dumpState->mSampleRate = mSampleRate;
         eitherChanged = true;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index efbdcff..9248bba 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 !=
@@ -283,52 +274,29 @@
                     goto exit;
                 }
             } else {
-                if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-                    if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
-                        sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
-                                                                  patch->sinks[0].ext.mix.handle);
-                        if (thread == 0) {
-                            ALOGW("createAudioPatch() bad capture I/O handle %d",
-                                                                  patch->sinks[0].ext.mix.handle);
-                            status = BAD_VALUE;
-                            goto exit;
-                        }
-                        status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
-                    } else {
-                        audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
-                        status = hwDevice->create_audio_patch(hwDevice,
-                                                               patch->num_sources,
-                                                               patch->sources,
-                                                               patch->num_sinks,
-                                                               patch->sinks,
-                                                               &halHandle);
-                    }
-                } else {
+                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
-                                                                    patch->sinks[0].ext.mix.handle);
+                                                              patch->sinks[0].ext.mix.handle);
                     if (thread == 0) {
                         ALOGW("createAudioPatch() bad capture I/O handle %d",
-                                                                    patch->sinks[0].ext.mix.handle);
+                                                              patch->sinks[0].ext.mix.handle);
                         status = BAD_VALUE;
                         goto exit;
                     }
-                    char *address;
-                    if (strcmp(patch->sources[0].ext.device.address, "") != 0) {
-                        address = audio_device_address_to_parameter(
-                                                            patch->sources[0].ext.device.type,
-                                                            patch->sources[0].ext.device.address);
-                    } else {
-                        address = (char *)calloc(1, 1);
+                    status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+                } else {
+                    if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) {
+                        status = INVALID_OPERATION;
+                        goto exit;
                     }
-                    AudioParameter param = AudioParameter(String8(address));
-                    free(address);
-                    param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING),
-                                 (int)patch->sources[0].ext.device.type);
-                    param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE),
-                                                     (int)patch->sinks[0].ext.mix.usecase.source);
-                    ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
-                                                                      param.toString().string());
-                    status = thread->setParameters(param.toString());
+
+                    audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+                    status = hwDevice->create_audio_patch(hwDevice,
+                                                           patch->num_sources,
+                                                           patch->sources,
+                                                           patch->num_sinks,
+                                                           patch->sinks,
+                                                           &halHandle);
                 }
             }
         } break;
@@ -341,6 +309,7 @@
                 goto exit;
             }
             // limit to connections between devices and output streams
+            audio_devices_t type = AUDIO_DEVICE_NONE;
             for (unsigned int i = 0; i < patch->num_sinks; i++) {
                 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
                     ALOGW("createAudioPatch() invalid sink type %d for mix source",
@@ -353,8 +322,8 @@
                     status = BAD_VALUE;
                     goto exit;
                 }
+                type |= patch->sinks[i].ext.device.type;
             }
-            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
             sp<ThreadBase> thread =
                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
             if (thread == 0) {
@@ -363,28 +332,14 @@
                 status = BAD_VALUE;
                 goto exit;
             }
-            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-                status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
-            } else {
-                audio_devices_t type = AUDIO_DEVICE_NONE;
-                for (unsigned int i = 0; i < patch->num_sinks; i++) {
-                    type |= patch->sinks[i].ext.device.type;
-                }
-                char *address;
-                if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
-                    //FIXME: we only support address on first sink with HAL version < 3.0
-                    address = audio_device_address_to_parameter(
-                                                                patch->sinks[0].ext.device.type,
-                                                                patch->sinks[0].ext.device.address);
-                } else {
-                    address = (char *)calloc(1, 1);
-                }
-                AudioParameter param = AudioParameter(String8(address));
-                free(address);
+            if (thread == audioflinger->primaryPlaybackThread_l()) {
+                AudioParameter param = AudioParameter();
                 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
-                status = thread->setParameters(param.toString());
+
+                audioflinger->broacastParametersToRecordThreads_l(param.toString());
             }
 
+            status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
         } break;
         default:
             status = BAD_VALUE;
@@ -472,6 +427,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,42 +534,30 @@
                 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;
             }
 
-            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
-            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
-                    sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
-                                                                    patch->sinks[0].ext.mix.handle);
-                    if (thread == 0) {
-                        ALOGW("releaseAudioPatch() bad capture I/O handle %d",
-                                                                  patch->sinks[0].ext.mix.handle);
-                        status = BAD_VALUE;
-                        break;
-                    }
-                    status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
-                } else {
-                    audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
-                    status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle);
-                }
-            } else {
+            if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
                 sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
-                                                                    patch->sinks[0].ext.mix.handle);
+                                                                patch->sinks[0].ext.mix.handle);
                 if (thread == 0) {
                     ALOGW("releaseAudioPatch() bad capture I/O handle %d",
-                                                                  patch->sinks[0].ext.mix.handle);
+                                                              patch->sinks[0].ext.mix.handle);
                     status = BAD_VALUE;
                     break;
                 }
-                AudioParameter param;
-                param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
-                ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
-                                                                      param.toString().string());
-                status = thread->setParameters(param.toString());
+                status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
+            } else {
+                AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+                if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) {
+                    status = INVALID_OPERATION;
+                    break;
+                }
+                audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+                status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle);
             }
         } break;
         case AUDIO_PORT_TYPE_MIX: {
@@ -632,14 +576,7 @@
                 status = BAD_VALUE;
                 break;
             }
-            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
-            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-                status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
-            } else {
-                AudioParameter param;
-                param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
-                status = thread->setParameters(param.toString());
-            }
+            status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
         } break;
         default:
             status = BAD_VALUE;
@@ -693,5 +630,4 @@
     return NO_ERROR;
 }
 
-
 } // namespace android
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 45df6a9..7bc6f0c 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -156,11 +156,6 @@
     bool                mResumeToStopping; // track was paused in stopping state.
     bool                mFlushHwPending; // track requests for thread flush
 
-    // for last call to getTimestamp
-    bool                mPreviousTimestampValid;
-    // This is either the first timestamp or one that has passed
-    // the check to prevent retrograde motion.
-    AudioTimestamp      mPreviousTimestamp;
 };  // end of Track
 
 class TimedTrack : public Track {
@@ -298,6 +293,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/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index fae19a1..33bd416 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -14,61 +14,114 @@
  * limitations under the License.
  */
 
+#include <binder/AppOpsManager.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 #include "ServiceUtilities.h"
 
+/* When performing permission checks we do not use permission cache for
+ * runtime permissions (protection level dangerous) as they may change at
+ * runtime. All other permissions (protection level normal and dangerous)
+ * can be cached as they never change. Of course all permission checked
+ * here are platform defined.
+ */
+
 namespace android {
 
 // Not valid until initialized by AudioFlinger constructor.  It would have to be
 // re-initialized if the process containing AudioFlinger service forks (which it doesn't).
 pid_t getpid_cached;
 
-bool recordingAllowed() {
+bool recordingAllowed(const String16& opPackageName) {
+    // Note: We are getting the UID from the calling IPC thread state because all
+    // clients that perform recording create AudioRecord in their own processes
+    // and the system does not create AudioRecord objects on behalf of apps. This
+    // differs from playback where in some situations the system recreates AudioTrack
+    // instances associated with a client's MediaPlayer on behalf of this client.
+    // In the latter case we have to store the client UID and pass in along for
+    // security checks.
+
     if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
     static const String16 sRecordAudio("android.permission.RECORD_AUDIO");
-    // don't use PermissionCache; this is not a system permission
-    bool ok = checkCallingPermission(sRecordAudio);
-    if (!ok) ALOGE("Request requires android.permission.RECORD_AUDIO");
-    return ok;
+
+    // IMPORTANT: Don't use PermissionCache - a runtime permission and may change.
+    const bool ok = checkCallingPermission(sRecordAudio);
+    if (!ok) {
+        ALOGE("Request requires android.permission.RECORD_AUDIO");
+        return false;
+    }
+
+    const uid_t uid = IPCThreadState::self()->getCallingUid();
+    String16 checkedOpPackageName = opPackageName;
+
+    // In some cases the calling code has no access to the package it runs under.
+    // For example, code using the wilhelm framework's OpenSL-ES APIs. In this
+    // case we will get the packages for the calling UID and pick the first one
+    // for attributing the app op. This will work correctly for runtime permissions
+    // as for legacy apps we will toggle the app op for all packages in the UID.
+    // The caveat is that the operation may be attributed to the wrong package and
+    // stats based on app ops may be slightly off.
+    if (checkedOpPackageName.size() <= 0) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder = sm->getService(String16("permission"));
+        if (binder == 0) {
+            ALOGE("Cannot get permission service");
+            return false;
+        }
+
+        sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
+        Vector<String16> packages;
+
+        permCtrl->getPackagesForUid(uid, packages);
+
+        if (packages.isEmpty()) {
+            ALOGE("No packages for calling UID");
+            return false;
+        }
+        checkedOpPackageName = packages[0];
+    }
+
+    AppOpsManager appOps;
+    if (appOps.noteOp(AppOpsManager::OP_RECORD_AUDIO, uid, checkedOpPackageName)
+            != AppOpsManager::MODE_ALLOWED) {
+        ALOGE("Request denied by app op OP_RECORD_AUDIO");
+        return false;
+    }
+
+    return true;
 }
 
 bool captureAudioOutputAllowed() {
     if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
     static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
-    // don't use PermissionCache; this is not a system permission
-    bool ok = checkCallingPermission(sCaptureAudioOutput);
+    // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+    bool ok = PermissionCache::checkCallingPermission(sCaptureAudioOutput);
     if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
     return ok;
 }
 
 bool captureHotwordAllowed() {
     static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
-    bool ok = checkCallingPermission(sCaptureHotwordAllowed);
+    // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+    bool ok = PermissionCache::checkCallingPermission(sCaptureHotwordAllowed);
     if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
     return ok;
 }
 
-bool captureFmTunerAllowed() {
-    static const String16 sCaptureFmTunerAllowed("android.permission.ACCESS_FM_RADIO");
-    bool ok = checkCallingPermission(sCaptureFmTunerAllowed);
-    if (!ok) ALOGE("android.permission.ACCESS_FM_RADIO");
-    return ok;
-}
-
 bool settingsAllowed() {
     if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
     static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
-    // don't use PermissionCache; this is not a system permission
-    bool ok = checkCallingPermission(sAudioSettings);
+    // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+    bool ok = PermissionCache::checkCallingPermission(sAudioSettings);
     if (!ok) ALOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
     return ok;
 }
 
 bool modifyAudioRoutingAllowed() {
     static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING");
-    bool ok = checkCallingPermission(sModifyAudioRoutingAllowed);
+    // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
+    bool ok = PermissionCache::checkCallingPermission(sModifyAudioRoutingAllowed);
     if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING");
     return ok;
 }
@@ -76,7 +129,7 @@
 bool dumpAllowed() {
     // don't optimize for same pid, since mediaserver never dumps itself
     static const String16 sDump("android.permission.DUMP");
-    // OK to use PermissionCache; this is a system permission
+    // IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
     bool ok = PermissionCache::checkCallingPermission(sDump);
     // convention is for caller to dump an error message to fd instead of logging here
     //if (!ok) ALOGE("Request requires android.permission.DUMP");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index ce18a90..fba6dce 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -20,12 +20,10 @@
 
 extern pid_t getpid_cached;
 
-bool recordingAllowed();
+bool recordingAllowed(const String16& opPackageName);
 bool captureAudioOutputAllowed();
 bool captureHotwordAllowed();
-bool captureFmTunerAllowed();
 bool settingsAllowed();
 bool modifyAudioRoutingAllowed();
 bool dumpAllowed();
-
 }
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..594ed05 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -56,6 +56,7 @@
 
 #include "AudioFlinger.h"
 #include "AudioMixer.h"
+#include "BufferProviders.h"
 #include "FastMixer.h"
 #include "FastCapture.h"
 #include "ServiceUtilities.h"
@@ -86,7 +87,17 @@
 #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;
+}
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
 
 namespace android {
 
@@ -489,6 +500,7 @@
         // mName will be set by concrete (non-virtual) subclass
         mDeathRecipient(new PMDeathRecipient(this))
 {
+    memset(&mPatch, 0, sizeof(struct audio_patch));
 }
 
 AudioFlinger::ThreadBase::~ThreadBase()
@@ -573,16 +585,16 @@
     return status;
 }
 
-void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)
+void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event)
 {
     Mutex::Autolock _l(mLock);
-    sendIoConfigEvent_l(event, param);
+    sendIoConfigEvent_l(event);
 }
 
 // sendIoConfigEvent_l() must be called with ThreadBase::mLock held
-void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param)
+void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event)
 {
-    sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, param);
+    sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event);
     sendConfigEvent_l(configEvent);
 }
 
@@ -646,7 +658,7 @@
         } break;
         case CFG_EVENT_IO: {
             IoConfigEventData *data = (IoConfigEventData *)event->mData.get();
-            audioConfigChanged(data->mEvent, data->mParam);
+            ioConfigChanged(data->mEvent);
         } break;
         case CFG_EVENT_SET_PARAMETER: {
             SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get();
@@ -1602,13 +1614,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;
         }
@@ -1904,32 +1922,29 @@
     return out_s8;
 }
 
-void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) {
-    AudioSystem::OutputDescriptor desc;
-    void *param2 = NULL;
+void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event) {
+    sp<AudioIoDescriptor> desc = new AudioIoDescriptor();
+    ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event);
 
-    ALOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event,
-            param);
+    desc->mIoHandle = mId;
 
     switch (event) {
-    case AudioSystem::OUTPUT_OPENED:
-    case AudioSystem::OUTPUT_CONFIG_CHANGED:
-        desc.channelMask = mChannelMask;
-        desc.samplingRate = mSampleRate;
-        desc.format = mFormat;
-        desc.frameCount = mNormalFrameCount; // FIXME see
+    case AUDIO_OUTPUT_OPENED:
+    case AUDIO_OUTPUT_CONFIG_CHANGED:
+        desc->mPatch = mPatch;
+        desc->mChannelMask = mChannelMask;
+        desc->mSamplingRate = mSampleRate;
+        desc->mFormat = mFormat;
+        desc->mFrameCount = mNormalFrameCount; // FIXME see
                                              // AudioFlinger::frameCount(audio_io_handle_t)
-        desc.latency = latency_l();
-        param2 = &desc;
+        desc->mLatency = latency_l();
         break;
 
-    case AudioSystem::STREAM_CONFIG_CHANGED:
-        param2 = &param;
-    case AudioSystem::OUTPUT_CLOSED:
+    case AUDIO_OUTPUT_CLOSED:
     default:
         break;
     }
-    mAudioFlinger->audioConfigChanged(event, mId, param2);
+    mAudioFlinger->ioConfigChanged(event, desc);
 }
 
 void AudioFlinger::PlaybackThread::writeCallback()
@@ -2038,6 +2053,9 @@
             ALOGW("direct output implements resume but not pause");
         }
     }
+    if (!mHwSupportsPause && mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) {
+        LOG_ALWAYS_FATAL("HW_AV_SYNC requested but HAL does not implement pause and resume");
+    }
 
     if (mType == DUPLICATING && mMixerBufferEnabled && mEffectBufferEnabled) {
         // For best precision, we use float instead of the associated output
@@ -2661,13 +2679,23 @@
                 if (exitPending()) {
                     break;
                 }
-                releaseWakeLock_l();
+                bool released = false;
+                // The following works around a bug in the offload driver. Ideally we would release
+                // the wake lock every time, but that causes the last offload buffer(s) to be
+                // dropped while the device is on battery, so we need to hold a wake lock during
+                // the drain phase.
+                if (mBytesRemaining && !(mDrainSequence & 1)) {
+                    releaseWakeLock_l();
+                    released = true;
+                }
                 mWakeLockUids.clear();
                 mActiveTracksGeneration++;
                 ALOGV("wait async completion");
                 mWaitWorkCV.wait(mLock);
                 ALOGV("async completion/wake");
-                acquireWakeLock_l();
+                if (released) {
+                    acquireWakeLock_l();
+                }
                 standbyTime = systemTime() + standbyDelay;
                 sleepTime = 0;
 
@@ -2916,21 +2944,79 @@
     return INVALID_OPERATION;
 }
 
+status_t AudioFlinger::MixerThread::createAudioPatch_l(const struct audio_patch *patch,
+                                                          audio_patch_handle_t *handle)
+{
+    // if !&IDLE, holds the FastMixer state to restore after new parameters processed
+    FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
+    if (mFastMixer != 0) {
+        FastMixerStateQueue *sq = mFastMixer->sq();
+        FastMixerState *state = sq->begin();
+        if (!(state->mCommand & FastMixerState::IDLE)) {
+            previousCommand = state->mCommand;
+            state->mCommand = FastMixerState::HOT_IDLE;
+            sq->end();
+            sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+        } else {
+            sq->end(false /*didModify*/);
+        }
+    }
+    status_t status = PlaybackThread::createAudioPatch_l(patch, handle);
+
+    if (!(previousCommand & FastMixerState::IDLE)) {
+        ALOG_ASSERT(mFastMixer != 0);
+        FastMixerStateQueue *sq = mFastMixer->sq();
+        FastMixerState *state = sq->begin();
+        ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE);
+        state->mCommand = previousCommand;
+        sq->end();
+        sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+    }
+
+    return status;
+}
+
 status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch,
                                                           audio_patch_handle_t *handle)
 {
     status_t status = NO_ERROR;
-    if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-        // store new device and send to effects
-        audio_devices_t type = AUDIO_DEVICE_NONE;
-        for (unsigned int i = 0; i < patch->num_sinks; i++) {
-            type |= patch->sinks[i].ext.device.type;
-        }
-        mOutDevice = type;
-        for (size_t i = 0; i < mEffectChains.size(); i++) {
-            mEffectChains[i]->setDevice_l(mOutDevice);
+
+    // store new device and send to effects
+    audio_devices_t type = AUDIO_DEVICE_NONE;
+    for (unsigned int i = 0; i < patch->num_sinks; i++) {
+        type |= patch->sinks[i].ext.device.type;
+    }
+
+#ifdef ADD_BATTERY_DATA
+    // when changing the audio output device, call addBatteryData to notify
+    // the change
+    if (mOutDevice != type) {
+        uint32_t params = 0;
+        // check whether speaker is on
+        if (type & AUDIO_DEVICE_OUT_SPEAKER) {
+            params |= IMediaPlayerService::kBatteryDataSpeakerOn;
         }
 
+        audio_devices_t deviceWithoutSpeaker
+            = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
+        // check if any other device (except speaker) is on
+        if (type & deviceWithoutSpeaker) {
+            params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
+        }
+
+        if (params != 0) {
+            addBatteryData(params);
+        }
+    }
+#endif
+
+    for (size_t i = 0; i < mEffectChains.size(); i++) {
+        mEffectChains[i]->setDevice_l(type);
+    }
+    mOutDevice = type;
+    mPatch = *patch;
+
+    if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
         audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
         status = hwDevice->create_audio_patch(hwDevice,
                                                patch->num_sources,
@@ -2939,19 +3025,72 @@
                                                patch->sinks,
                                                handle);
     } else {
-        ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+        char *address;
+        if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
+            //FIXME: we only support address on first sink with HAL version < 3.0
+            address = audio_device_address_to_parameter(
+                                                        patch->sinks[0].ext.device.type,
+                                                        patch->sinks[0].ext.device.address);
+        } else {
+            address = (char *)calloc(1, 1);
+        }
+        AudioParameter param = AudioParameter(String8(address));
+        free(address);
+        param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
+        status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+                param.toString().string());
+        *handle = AUDIO_PATCH_HANDLE_NONE;
     }
+    sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
+    return status;
+}
+
+status_t AudioFlinger::MixerThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
+{
+    // if !&IDLE, holds the FastMixer state to restore after new parameters processed
+    FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
+    if (mFastMixer != 0) {
+        FastMixerStateQueue *sq = mFastMixer->sq();
+        FastMixerState *state = sq->begin();
+        if (!(state->mCommand & FastMixerState::IDLE)) {
+            previousCommand = state->mCommand;
+            state->mCommand = FastMixerState::HOT_IDLE;
+            sq->end();
+            sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+        } else {
+            sq->end(false /*didModify*/);
+        }
+    }
+
+    status_t status = PlaybackThread::releaseAudioPatch_l(handle);
+
+    if (!(previousCommand & FastMixerState::IDLE)) {
+        ALOG_ASSERT(mFastMixer != 0);
+        FastMixerStateQueue *sq = mFastMixer->sq();
+        FastMixerState *state = sq->begin();
+        ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE);
+        state->mCommand = previousCommand;
+        sq->end();
+        sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+    }
+
     return status;
 }
 
 status_t AudioFlinger::PlaybackThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
 {
     status_t status = NO_ERROR;
+
+    mOutDevice = AUDIO_DEVICE_NONE;
+
     if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
         audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
         status = hwDevice->release_audio_patch(hwDevice, handle);
     } else {
-        ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+        AudioParameter param;
+        param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
+        status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+                param.toString().string());
     }
     return status;
 }
@@ -3586,21 +3725,16 @@
         // 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();
+        AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
+
+        desiredFrames = sourceFramesNeededWithTimestretch(
+                sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);
+        // 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 +3897,14 @@
                 AudioMixer::RESAMPLE,
                 AudioMixer::SAMPLE_RATE,
                 (void *)(uintptr_t)reqSampleRate);
+
+            AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
+            mAudioMixer->setParameter(
+                name,
+                AudioMixer::TIMESTRETCH,
+                AudioMixer::PLAYBACK_RATE,
+                &playbackRate);
+
             /*
              * Select the appropriate output buffer for the track.
              *
@@ -4032,7 +4174,7 @@
             audio_devices_t deviceWithoutSpeaker
                 = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
             // check if any other device (except speaker) is on
-            if (value & deviceWithoutSpeaker ) {
+            if (value & deviceWithoutSpeaker) {
                 params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
             }
 
@@ -4074,7 +4216,7 @@
                 }
                 mTracks[i]->mName = name;
             }
-            sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+            sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
         }
     }
 
@@ -4246,9 +4388,9 @@
         sp<Track> l = mLatestActiveTrack.promote();
         bool last = l.get() == track;
 
-        if (mHwSupportsPause && track->isPausing()) {
+        if (track->isPausing()) {
             track->setPaused();
-            if (last && !mHwPaused) {
+            if (mHwSupportsPause && last && !mHwPaused) {
                 doHwPause = true;
                 mHwPaused = true;
             }
@@ -4258,13 +4400,11 @@
             if (last) {
                 flushPending = true;
             }
-        } else if (mHwSupportsPause && track->isResumePending()){
+        } else if (track->isResumePending()) {
             track->resumeAck();
-            if (last) {
-                if (mHwPaused) {
-                    doHwResume = true;
-                    mHwPaused = false;
-                }
+            if (last && mHwPaused) {
+                doHwResume = true;
+                mHwPaused = false;
             }
         }
 
@@ -4526,7 +4666,7 @@
         }
         if (status == NO_ERROR && reconfig) {
             readOutputParameters_l();
-            sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+            sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
         }
     }
 
@@ -5085,10 +5225,13 @@
             mOutputTracks[i]->destroy();
             mOutputTracks.removeAt(i);
             updateWaitTime_l();
+            if (thread->getOutput() == mOutput) {
+                mOutput = NULL;
+            }
             return;
         }
     }
-    ALOGV("removeOutputTrack(): unkonwn thread: %p", thread);
+    ALOGV("removeOutputTrack(): unknown thread: %p", thread);
 }
 
 // caller must hold mLock
@@ -5202,11 +5345,11 @@
         }
         initFastCapture =
                 // either capture sample rate is same as (a reasonable) primary output sample rate
-                (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) &&
+                ((isMusicRate(primaryOutputSampleRate) &&
                     (mSampleRate == primaryOutputSampleRate)) ||
                 // or primary output sample rate is unknown, and capture sample rate is reasonable
                 ((primaryOutputSampleRate == 0) &&
-                    ((mSampleRate == 44100 || mSampleRate == 48000)))) &&
+                        isMusicRate(mSampleRate))) &&
                 // and the buffer size is < 12 ms
                 (mFrameCount * 1000) / mSampleRate < 12;
         break;
@@ -5290,7 +5433,6 @@
     // FIXME mNormalSource
 }
 
-
 AudioFlinger::RecordThread::~RecordThread()
 {
     if (mFastCapture != 0) {
@@ -5310,7 +5452,7 @@
     }
     mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter);
     mAudioFlinger->unregisterWriter(mNBLogWriter);
-    delete[] mRsmpInBuffer;
+    free(mRsmpInBuffer);
 }
 
 void AudioFlinger::RecordThread::onFirstRef()
@@ -5543,7 +5685,7 @@
         // If an NBAIO source is present, use it to read the normal capture's data
         if (mPipeSource != 0) {
             size_t framesToRead = mBufferSize / mFrameSize;
-            framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount],
+            framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
                     framesToRead, AudioBufferProvider::kInvalidPTS);
             if (framesRead == 0) {
                 // since pipe is non-blocking, simulate blocking input
@@ -5552,7 +5694,7 @@
         // otherwise use the HAL / AudioStreamIn directly
         } else {
             ssize_t bytesRead = mInput->stream->read(mInput->stream,
-                    &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+                    (uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize);
             if (bytesRead < 0) {
                 framesRead = bytesRead;
             } else {
@@ -5572,13 +5714,13 @@
         ALOG_ASSERT(framesRead > 0);
 
         if (mTeeSink != 0) {
-            (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead);
+            (void) mTeeSink->write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
         }
         // If destination is non-contiguous, we now correct for reading past end of buffer.
         {
             size_t part1 = mRsmpInFramesP2 - rear;
             if ((size_t) framesRead > part1) {
-                memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
+                memcpy(mRsmpInBuffer, (uint8_t*)mRsmpInBuffer + mRsmpInFramesP2 * mFrameSize,
                         (framesRead - part1) * mFrameSize);
             }
         }
@@ -5594,6 +5736,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 +5753,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 +6083,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 +6261,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 +6314,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 +6331,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->raw = (uint8_t*)recordThread->mRsmpInBuffer + front * recordThread->mFrameSize;
     buffer->frameCount = part1;
-    activeTrack->mRsmpInUnrel = part1;
+    mRsmpInUnrel = part1;
     return NO_ERROR;
 }
 
@@ -6270,18 +6348,264 @@
 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),
+            mIsLegacyDownmix(false),
+            mIsLegacyUpmix(false),
+            mRequiresFloat(false),
+            mInputConverterProvider(NULL)
+{
+    (void)updateParameters(srcChannelMask, srcFormat, srcSampleRate,
+            dstChannelMask, dstFormat, dstSampleRate);
+}
+
+AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() {
+    free(mBuf);
+    delete mResampler;
+    delete mInputConverterProvider;
+}
+
+size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
+        AudioBufferProvider *provider, size_t frames)
+{
+    if (mInputConverterProvider != NULL) {
+        mInputConverterProvider->setBufferProvider(provider);
+        provider = mInputConverterProvider;
+    }
+
+    if (mResampler == NULL) {
+        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;
+            }
+            // format convert to destination buffer
+            convertNoResampler(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 buffer if needed
+         if (mBufFrameSize != 0 && mBufFrames < frames) {
+             free(mBuf);
+             mBufFrames = frames;
+             (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+         }
+        // resampler accumulates, but we only have one source track
+        memset(mBuf, 0, frames * mBufFrameSize);
+        frames = mResampler->resample((int32_t*)mBuf, frames, provider);
+        // format convert to destination buffer
+        convertResampler(dst, mBuf, 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;
+    }
+
+    ALOGV("RecordBufferConverter updateParameters srcMask:%#x dstMask:%#x"
+            "  srcFormat:%#x dstFormat:%#x  srcRate:%u dstRate:%u",
+            srcChannelMask, dstChannelMask, srcFormat, dstFormat, srcSampleRate, dstSampleRate);
+    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 to resample?
+    delete mResampler;
+    mResampler = NULL;
+    if (mSrcSampleRate != mDstSampleRate) {
+        mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_FLOAT,
+                mSrcChannelCount, mDstSampleRate);
+        mResampler->setSampleRate(mSrcSampleRate);
+        mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+    }
+
+    // are we running legacy channel conversion modes?
+    mIsLegacyDownmix = (mSrcChannelMask == AUDIO_CHANNEL_IN_STEREO
+                            || mSrcChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK)
+                   && mDstChannelMask == AUDIO_CHANNEL_IN_MONO;
+    mIsLegacyUpmix = mSrcChannelMask == AUDIO_CHANNEL_IN_MONO
+                   && (mDstChannelMask == AUDIO_CHANNEL_IN_STEREO
+                            || mDstChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK);
+
+    // do we need to process in float?
+    mRequiresFloat = mResampler != NULL || mIsLegacyDownmix || mIsLegacyUpmix;
+
+    // do we need a staging buffer to convert for destination (we can still optimize this)?
+    // we use mBufFrameSize > 0 to indicate both frame size as well as buffer necessity
+    if (mResampler != NULL) {
+        mBufFrameSize = max(mSrcChannelCount, FCC_2)
+                * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+    } else if ((mIsLegacyUpmix || mIsLegacyDownmix) && mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+        mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+    } else if (mSrcChannelMask != mDstChannelMask && mDstFormat != mSrcFormat) {
+        mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat);
+    } else {
+        mBufFrameSize = 0;
+    }
+    mBufFrames = 0; // force the buffer to be resized.
+
+    // do we need an input converter buffer provider to give us float?
+    delete mInputConverterProvider;
+    mInputConverterProvider = NULL;
+    if (mRequiresFloat && mSrcFormat != AUDIO_FORMAT_PCM_FLOAT) {
+        mInputConverterProvider = new ReformatBufferProvider(
+                audio_channel_count_from_in_mask(mSrcChannelMask),
+                mSrcFormat,
+                AUDIO_FORMAT_PCM_FLOAT,
+                256 /* provider buffer frame count */);
+    }
+
+    // do we need a remixer to do channel mask conversion
+    if (!mIsLegacyDownmix && !mIsLegacyUpmix && mSrcChannelMask != mDstChannelMask) {
+        (void) memcpy_by_index_array_initialization_from_channel_mask(
+                mIdxAry, ARRAY_SIZE(mIdxAry), mDstChannelMask, mSrcChannelMask);
+    }
+    return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convertNoResampler(
+        void *dst, const void *src, size_t frames)
+{
+    // src is native type unless there is legacy upmix or downmix, whereupon it is float.
+    if (mBufFrameSize != 0 && mBufFrames < frames) {
+        free(mBuf);
+        mBufFrames = frames;
+        (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+    }
+    // do we need to do legacy upmix and downmix?
+    if (mIsLegacyUpmix || mIsLegacyDownmix) {
+        void *dstBuf = mBuf != NULL ? mBuf : dst;
+        if (mIsLegacyUpmix) {
+            upmix_to_stereo_float_from_mono_float((float *)dstBuf,
+                    (const float *)src, frames);
+        } else /*mIsLegacyDownmix */ {
+            downmix_to_mono_float_from_stereo_float((float *)dstBuf,
+                    (const float *)src, frames);
+        }
+        if (mBuf != NULL) {
+            memcpy_by_audio_format(dst, mDstFormat, mBuf, AUDIO_FORMAT_PCM_FLOAT,
+                    frames * mDstChannelCount);
+        }
+        return;
+    }
+    // do we need to do channel mask conversion?
+    if (mSrcChannelMask != mDstChannelMask) {
+        void *dstBuf = mBuf != NULL ? mBuf : dst;
+        memcpy_by_index_array(dstBuf, mDstChannelCount,
+                src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames);
+        if (dstBuf == dst) {
+            return; // format is the same
+        }
+    }
+    // convert to destination buffer
+    const void *convertBuf = mBuf != NULL ? mBuf : src;
+    memcpy_by_audio_format(dst, mDstFormat, convertBuf, mSrcFormat,
+            frames * mDstChannelCount);
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convertResampler(
+        void *dst, /*not-a-const*/ void *src, size_t frames)
+{
+    // src buffer format is ALWAYS float when entering this routine
+    if (mIsLegacyUpmix) {
+        ; // mono to stereo already handled by resampler
+    } else if (mIsLegacyDownmix
+            || (mSrcChannelMask == mDstChannelMask && mSrcChannelCount == 1)) {
+        // the resampler outputs stereo for mono input channel (a feature?)
+        // must convert to mono
+        downmix_to_mono_float_from_stereo_float((float *)src,
+                (const float *)src, frames);
+    } else if (mSrcChannelMask != mDstChannelMask) {
+        // convert to mono channel again for channel mask conversion (could be skipped
+        // with further optimization).
+        if (mSrcChannelCount == 1) {
+            downmix_to_mono_float_from_stereo_float((float *)src,
+                (const float *)src, frames);
+        }
+        // convert to destination format (in place, OK as float is larger than other types)
+        if (mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+            memcpy_by_audio_format(src, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+                    frames * mSrcChannelCount);
+        }
+        // channel convert and save to dst
+        memcpy_by_index_array(dst, mDstChannelCount,
+                src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mDstFormat), frames);
+        return;
+    }
+    // convert to destination format and save to dst
+    memcpy_by_audio_format(dst, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+            frames * mDstChannelCount);
+}
+
 bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
                                                         status_t& status)
 {
@@ -6292,6 +6616,10 @@
     audio_format_t reqFormat = mFormat;
     uint32_t samplingRate = mSampleRate;
     audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+    // possible that we are > 2 channels, use channel index mask
+    if (channelMask == AUDIO_CHANNEL_INVALID && mChannelCount <= FCC_8) {
+        audio_channel_mask_for_index_assignment_from_count(mChannelCount);
+    }
 
     AudioParameter param = AudioParameter(keyValuePair);
     int value;
@@ -6303,7 +6631,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;
@@ -6312,7 +6640,8 @@
     }
     if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
         audio_channel_mask_t mask = (audio_channel_mask_t) value;
-        if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
+        if (!audio_is_input_channel(mask) ||
+                audio_channel_count_from_in_mask(mask) > FCC_8) {
             status = BAD_VALUE;
         } else {
             channelMask = mask;
@@ -6377,19 +6706,17 @@
         }
         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 ||
-                        channelMask == AUDIO_CHANNEL_IN_STEREO)) {
+                        mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_8) {
                 status = NO_ERROR;
             }
             if (status == NO_ERROR) {
                 readInputParameters_l();
-                sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
+                sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
             }
         }
     }
@@ -6410,26 +6737,27 @@
     return out_s8;
 }
 
-void AudioFlinger::RecordThread::audioConfigChanged(int event, int param __unused) {
-    AudioSystem::OutputDescriptor desc;
-    const void *param2 = NULL;
+void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event) {
+    sp<AudioIoDescriptor> desc = new AudioIoDescriptor();
+
+    desc->mIoHandle = mId;
 
     switch (event) {
-    case AudioSystem::INPUT_OPENED:
-    case AudioSystem::INPUT_CONFIG_CHANGED:
-        desc.channelMask = mChannelMask;
-        desc.samplingRate = mSampleRate;
-        desc.format = mFormat;
-        desc.frameCount = mFrameCount;
-        desc.latency = 0;
-        param2 = &desc;
+    case AUDIO_INPUT_OPENED:
+    case AUDIO_INPUT_CONFIG_CHANGED:
+        desc->mPatch = mPatch;
+        desc->mChannelMask = mChannelMask;
+        desc->mSamplingRate = mSampleRate;
+        desc->mFormat = mFormat;
+        desc->mFrameCount = mFrameCount;
+        desc->mLatency = 0;
         break;
 
-    case AudioSystem::INPUT_CLOSED:
+    case AUDIO_INPUT_CLOSED:
     default:
         break;
     }
-    mAudioFlinger->audioConfigChanged(event, mId, param2);
+    mAudioFlinger->ioConfigChanged(event, desc);
 }
 
 void AudioFlinger::RecordThread::readInputParameters_l()
@@ -6437,10 +6765,13 @@
     mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
     mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
     mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
+    if (mChannelCount > FCC_8) {
+        ALOGE("HAL channel count %d > %d", mChannelCount, FCC_8);
+    }
     mHALFormat = mInput->stream->common.get_format(&mInput->stream->common);
     mFormat = mHALFormat;
-    if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
-        ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+    if (!audio_is_linear_pcm(mFormat)) {
+        ALOGE("HAL format %#x is not linear pcm", mFormat);
     }
     mFrameSize = audio_stream_in_frame_size(mInput->stream);
     mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
@@ -6451,9 +6782,11 @@
     // 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;
+    free(mRsmpInBuffer);
 
     // TODO optimize audio capture buffer sizes ...
     // Here we calculate the size of the sliding buffer used as a source
@@ -6463,7 +6796,7 @@
     // The current value is higher than necessary.  However it should not add to latency.
 
     // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
-    mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount];
+    (void)posix_memalign(&mRsmpInBuffer, 32, (mRsmpInFramesP2 + mFrameCount - 1) * mFrameSize);
 
     // AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
     // But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
@@ -6567,33 +6900,35 @@
                                                           audio_patch_handle_t *handle)
 {
     status_t status = NO_ERROR;
-    if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-        // store new device and send to effects
-        mInDevice = patch->sources[0].ext.device.type;
+
+    // store new device and send to effects
+    mInDevice = patch->sources[0].ext.device.type;
+    mPatch = *patch;
+    for (size_t i = 0; i < mEffectChains.size(); i++) {
+        mEffectChains[i]->setDevice_l(mInDevice);
+    }
+
+    // disable AEC and NS if the device is a BT SCO headset supporting those
+    // pre processings
+    if (mTracks.size() > 0) {
+        bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+                            mAudioFlinger->btNrecIsOff();
+        for (size_t i = 0; i < mTracks.size(); i++) {
+            sp<RecordTrack> track = mTracks[i];
+            setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+            setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
+        }
+    }
+
+    // store new source and send to effects
+    if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
+        mAudioSource = patch->sinks[0].ext.mix.usecase.source;
         for (size_t i = 0; i < mEffectChains.size(); i++) {
-            mEffectChains[i]->setDevice_l(mInDevice);
+            mEffectChains[i]->setAudioSource_l(mAudioSource);
         }
+    }
 
-        // disable AEC and NS if the device is a BT SCO headset supporting those
-        // pre processings
-        if (mTracks.size() > 0) {
-            bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
-                                mAudioFlinger->btNrecIsOff();
-            for (size_t i = 0; i < mTracks.size(); i++) {
-                sp<RecordTrack> track = mTracks[i];
-                setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
-                setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
-            }
-        }
-
-        // store new source and send to effects
-        if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
-            mAudioSource = patch->sinks[0].ext.mix.usecase.source;
-            for (size_t i = 0; i < mEffectChains.size(); i++) {
-                mEffectChains[i]->setAudioSource_l(mAudioSource);
-            }
-        }
-
+    if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
         audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
         status = hwDevice->create_audio_patch(hwDevice,
                                                patch->num_sources,
@@ -6602,19 +6937,44 @@
                                                patch->sinks,
                                                handle);
     } else {
-        ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+        char *address;
+        if (strcmp(patch->sources[0].ext.device.address, "") != 0) {
+            address = audio_device_address_to_parameter(
+                                                patch->sources[0].ext.device.type,
+                                                patch->sources[0].ext.device.address);
+        } else {
+            address = (char *)calloc(1, 1);
+        }
+        AudioParameter param = AudioParameter(String8(address));
+        free(address);
+        param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING),
+                     (int)patch->sources[0].ext.device.type);
+        param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE),
+                                         (int)patch->sinks[0].ext.mix.usecase.source);
+        status = mInput->stream->common.set_parameters(&mInput->stream->common,
+                param.toString().string());
+        *handle = AUDIO_PATCH_HANDLE_NONE;
     }
+
+    sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
+
     return status;
 }
 
 status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
 {
     status_t status = NO_ERROR;
+
+    mInDevice = AUDIO_DEVICE_NONE;
+
     if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
         audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
         status = hwDevice->release_audio_patch(hwDevice, handle);
     } else {
-        ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+        AudioParameter param;
+        param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
+        status = mInput->stream->common.set_parameters(&mInput->stream->common,
+                param.toString().string());
     }
     return status;
 }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index d600ea9..37bacae 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -100,22 +100,21 @@
 
     class IoConfigEventData : public ConfigEventData {
     public:
-        IoConfigEventData(int event, int param) :
-            mEvent(event), mParam(param) {}
+        IoConfigEventData(audio_io_config_event event) :
+            mEvent(event) {}
 
         virtual  void dump(char *buffer, size_t size) {
-            snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam);
+            snprintf(buffer, size, "IO event: event %d\n", mEvent);
         }
 
-        const int mEvent;
-        const int mParam;
+        const audio_io_config_event mEvent;
     };
 
     class IoConfigEvent : public ConfigEvent {
     public:
-        IoConfigEvent(int event, int param) :
+        IoConfigEvent(audio_io_config_event event) :
             ConfigEvent(CFG_EVENT_IO) {
-            mData = new IoConfigEventData(event, param);
+            mData = new IoConfigEventData(event);
         }
         virtual ~IoConfigEvent() {}
     };
@@ -231,6 +230,8 @@
 
                 // static externally-visible
                 type_t      type() const { return mType; }
+                bool isDuplicating() const { return (mType == DUPLICATING); }
+
                 audio_io_handle_t id() const { return mId;}
 
                 // dynamic externally-visible
@@ -250,13 +251,13 @@
                                                     status_t& status) = 0;
     virtual     status_t    setParameters(const String8& keyValuePairs);
     virtual     String8     getParameters(const String8& keys) = 0;
-    virtual     void        audioConfigChanged(int event, int param = 0) = 0;
+    virtual     void        ioConfigChanged(audio_io_config_event event) = 0;
                 // sendConfigEvent_l() must be called with ThreadBase::mLock held
                 // Can temporarily release the lock if waiting for a reply from
                 // processConfigEvents_l().
                 status_t    sendConfigEvent_l(sp<ConfigEvent>& event);
-                void        sendIoConfigEvent(int event, int param = 0);
-                void        sendIoConfigEvent_l(int event, int param = 0);
+                void        sendIoConfigEvent(audio_io_config_event event);
+                void        sendIoConfigEvent_l(audio_io_config_event event);
                 void        sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
                 status_t    sendSetParameterConfigEvent_l(const String8& keyValuePair);
                 status_t    sendCreateAudioPatchConfigEvent(const struct audio_patch *patch,
@@ -427,6 +428,7 @@
                 bool                    mStandby;     // Whether thread is currently in standby.
                 audio_devices_t         mOutDevice;   // output device
                 audio_devices_t         mInDevice;    // input device
+                struct audio_patch      mPatch;
                 audio_source_t          mAudioSource;
 
                 const audio_io_handle_t mId;
@@ -560,7 +562,7 @@
                                 { return android_atomic_acquire_load(&mSuspended) > 0; }
 
     virtual     String8     getParameters(const String8& keys);
-    virtual     void        audioConfigChanged(int event, int param = 0);
+    virtual     void        ioConfigChanged(audio_io_config_event event);
                 status_t    getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
                 // FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency.
                 // Consider also removing and passing an explicit mMainBuffer initialization
@@ -713,8 +715,9 @@
                                    audio_patch_handle_t *handle);
     virtual     status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
 
-                bool        usesHwAvSync() const { return (mType == DIRECT) && (mOutput != NULL) &&
-                                                (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
+                bool        usesHwAvSync() const { return (mType == DIRECT) && (mOutput != NULL)
+                                    && mHwSupportsPause
+                                    && (mOutput->flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC); }
 
 private:
 
@@ -865,6 +868,10 @@
     virtual     void        threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
     virtual     uint32_t    correctLatency_l(uint32_t latency) const;
 
+    virtual     status_t    createAudioPatch_l(const struct audio_patch *patch,
+                                   audio_patch_handle_t *handle);
+    virtual     status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
+
                 AudioMixer* mAudioMixer;    // normal mixer
 private:
                 // one-time initialization, no locks required
@@ -1036,17 +1043,132 @@
 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:
+        // format conversion when not using resampler
+        void convertNoResampler(void *dst, const void *src, size_t frames);
+
+        // format conversion when using resampler; modifies src in-place
+        void convertResampler(void *dst, /*not-a-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;
+
+        bool                 mIsLegacyDownmix;  // legacy stereo to mono conversion needed
+        bool                 mIsLegacyUpmix;    // legacy mono to stereo conversion needed
+        bool                 mRequiresFloat;    // data processing requires float (e.g. resampler)
+        PassthruBufferProvider *mInputConverterProvider;    // converts input to float
+        int8_t               mIdxAry[sizeof(uint32_t) * 8]; // used for channel mask conversion
     };
 
 #include "RecordTracks.h"
@@ -1111,7 +1233,7 @@
                                                status_t& status);
     virtual void        cacheParameters_l() {}
     virtual String8     getParameters(const String8& keys);
-    virtual void        audioConfigChanged(int event, int param = 0);
+    virtual void        ioConfigChanged(audio_io_config_event event);
     virtual status_t    createAudioPatch_l(const struct audio_patch *patch,
                                            audio_patch_handle_t *handle);
     virtual status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
@@ -1157,7 +1279,7 @@
             Condition                           mStartStopCond;
 
             // resampler converts input at HAL Hz to output at AudioRecord client Hz
-            int16_t                             *mRsmpInBuffer; // see new[] for details on the size
+            void                               *mRsmpInBuffer; //
             size_t                              mRsmpInFrames;  // size of resampler input in frames
             size_t                              mRsmpInFramesP2;// size rounded up to a power-of-2
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index dc9f249..1b03060 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -404,8 +404,7 @@
     mIsInvalid(false),
     mAudioTrackServerProxy(NULL),
     mResumeToStopping(false),
-    mFlushHwPending(false),
-    mPreviousTimestampValid(false)
+    mFlushHwPending(false)
 {
     // client == 0 implies sharedBuffer == 0
     ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -863,7 +862,6 @@
         if (mState == FLUSHED) {
             mState = IDLE;
         }
-        mPreviousTimestampValid = false;
     }
 }
 
@@ -885,12 +883,10 @@
 {
     // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
     if (isFastTrack()) {
-        // FIXME no lock held to set mPreviousTimestampValid = false
         return INVALID_OPERATION;
     }
     sp<ThreadBase> thread = mThread.promote();
     if (thread == 0) {
-        // FIXME no lock held to set mPreviousTimestampValid = false
         return INVALID_OPERATION;
     }
 
@@ -900,12 +896,14 @@
     status_t result = INVALID_OPERATION;
     if (!isOffloaded() && !isDirect()) {
         if (!playbackThread->mLatchQValid) {
-            mPreviousTimestampValid = false;
             return INVALID_OPERATION;
         }
-        uint32_t unpresentedFrames =
-                ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
-                playbackThread->mSampleRate;
+        // 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();
+        AudioPlaybackRate playbackRate = mAudioTrackServerProxy->getPlaybackRate();
+        uint32_t unpresentedFrames = ((double) playbackThread->mLatchQ.mUnpresentedFrames *
+                sampleRate * playbackRate.mSpeed)/ 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.
@@ -916,10 +914,7 @@
         uint32_t framesWritten = i >= 0 ?
                 playbackThread->mLatchQ.mFramesReleased[i] :
                 mAudioTrackServerProxy->framesReleased();
-        if (framesWritten < unpresentedFrames) {
-            mPreviousTimestampValid = false;
-            // return invalid result
-        } else {
+        if (framesWritten >= unpresentedFrames) {
             timestamp.mPosition = framesWritten - unpresentedFrames;
             timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
             result = NO_ERROR;
@@ -928,41 +923,6 @@
         result = playbackThread->getTimestamp_l(timestamp);
     }
 
-    // Prevent retrograde motion in timestamp.
-    if (result == NO_ERROR) {
-        if (mPreviousTimestampValid) {
-            if (timestamp.mTime.tv_sec < mPreviousTimestamp.mTime.tv_sec ||
-                    (timestamp.mTime.tv_sec == mPreviousTimestamp.mTime.tv_sec &&
-                    timestamp.mTime.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) {
-                ALOGW("WARNING - retrograde timestamp time");
-                // FIXME Consider blocking this from propagating upwards.
-            }
-
-            // Looking at signed delta will work even when the timestamps
-            // are wrapping around.
-            int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition
-                    - mPreviousTimestamp.mPosition);
-            // position can bobble slightly as an artifact; this hides the bobble
-            static const int32_t MINIMUM_POSITION_DELTA = 8;
-            if (deltaPosition < 0) {
-#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec)
-                ALOGW("WARNING - retrograde timestamp position corrected,"
-                        " %d = %u - %u, (at %llu, %llu nanos)",
-                        deltaPosition,
-                        timestamp.mPosition,
-                        mPreviousTimestamp.mPosition,
-                        TIME_TO_NANOS(timestamp.mTime),
-                        TIME_TO_NANOS(mPreviousTimestamp.mTime));
-#undef TIME_TO_NANOS
-            }
-            if (deltaPosition < MINIMUM_POSITION_DELTA) {
-                // Current timestamp is bad. Use last valid timestamp.
-                timestamp = mPreviousTimestamp;
-            }
-        }
-        mPreviousTimestamp = timestamp;
-        mPreviousTimestampValid = true;
-    }
     return result;
 }
 
@@ -1861,13 +1821,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 +1950,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 +1984,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..536eb93 100644
--- a/services/audioflinger/tests/Android.mk
+++ b/services/audioflinger/tests/Android.mk
@@ -39,11 +39,13 @@
 LOCAL_SRC_FILES:= \
 	test-mixer.cpp \
 	../AudioMixer.cpp.arm \
+	../BufferProviders.cpp
 
 LOCAL_C_INCLUDES := \
 	$(call include-path-for, audio-effects) \
 	$(call include-path-for, audio-utils) \
-	frameworks/av/services/audioflinger
+	frameworks/av/services/audioflinger \
+	external/sonic
 
 LOCAL_STATIC_LIBRARIES := \
 	libsndfile
@@ -57,7 +59,8 @@
 	libdl \
 	libcutils \
 	libutils \
-	liblog
+	liblog \
+	libsonic
 
 LOCAL_MODULE:= test-mixer
 
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..8523fc5 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -106,10 +106,12 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
+                                        uid_t uid,
                                         uint32_t samplingRate,
                                         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,
@@ -128,10 +130,12 @@
     virtual status_t getInputForAttr(const audio_attributes_t *attr,
                                      audio_io_handle_t *input,
                                      audio_session_t session,
+                                     uid_t uid,
                                      uint32_t samplingRate,
                                      audio_format_t format,
                                      audio_channel_mask_t channelMask,
                                      audio_input_flags_t flags,
+                                     audio_port_handle_t selectedDeviceId,
                                      input_type_t *inputType) = 0;
     // indicates to the audio policy manager that the input starts being used.
     virtual status_t startInput(audio_io_handle_t input,
@@ -207,7 +211,7 @@
                                       struct audio_patch *patches,
                                       unsigned int *generation) = 0;
     virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
-    virtual void clearAudioPatches(uid_t uid) = 0;
+    virtual void releaseResourcesForUid(uid_t uid) = 0;
 
     virtual status_t acquireSoundTriggerSession(audio_session_t *session,
                                            audio_io_handle_t *ioHandle,
@@ -217,6 +221,11 @@
 
     virtual status_t registerPolicyMixes(Vector<AudioMix> mixes) = 0;
     virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes) = 0;
+
+    virtual status_t startAudioSource(const struct audio_port_config *source,
+                                      const audio_attributes_t *attributes,
+                                      audio_io_handle_t *handle) = 0;
+    virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
 };
 
 
@@ -319,6 +328,8 @@
     virtual void onAudioPatchListUpdate() = 0;
 
     virtual audio_unique_id_t newAudioUniqueId() = 0;
+
+    virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0;
 };
 
 extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
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/include/policy.h b/services/audiopolicy/common/include/policy.h
index a2327ee..4eef02f2 100755
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -20,7 +20,7 @@
 
 // For mixed output and inputs, the policy will use max mixer sampling rates.
 // Do not limit sampling rate otherwise
-#define MAX_MIXER_SAMPLING_RATE 48000
+#define MAX_MIXER_SAMPLING_RATE 192000
 
 // For mixed output and inputs, the policy will use max mixer channel count.
 // Do not limit channel count otherwise
@@ -60,7 +60,7 @@
  *
  * @return true if the device is a virtual one, false otherwise.
  */
-static bool is_virtual_input_device(audio_devices_t device)
+static inline bool is_virtual_input_device(audio_devices_t device)
 {
     if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
         device &= ~AUDIO_DEVICE_BIT_IN;
@@ -78,7 +78,7 @@
  *
  * @return true if the device needs distinguish on address, false otherwise..
  */
-static bool device_distinguishes_on_address(audio_devices_t device)
+static inline bool device_distinguishes_on_address(audio_devices_t device)
 {
     return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0);
 }
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..50f622d 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,70 @@
 
     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()
-    uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
+    AudioPolicyClientInterface *mClientInterface;
+
+protected:
+    audio_port_handle_t mId;
 };
 
-class AudioOutputCollection :
-        public DefaultKeyedVector< audio_io_handle_t, sp<AudioOutputDescriptor> >
+// 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)
+    uint32_t mGlobalRefCount;  // non-stream-specific ref count
+};
+
+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 +146,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..82e2c43 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,17 @@
     audio_format_t pickFormat() const;
 
     static const audio_format_t sPcmFormatCompareTable[];
+    static int compareFormats(const audio_format_t *format1, const audio_format_t *format2) {
+        return compareFormats(*format1, *format2);
+    }
     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 +102,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 14a7d36..0b08430 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
@@ -38,12 +38,14 @@
     uint32_t value;
 };
 
-#define STRING_TO_ENUM(string) { #string, string }
+// TODO: move to a separate file. Should be in sync with audio.h.
+#define STRING_TO_ENUM(string) { #string, (uint32_t)string } // uint32_t cast removes warning
+#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 +96,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),
@@ -152,6 +205,17 @@
     STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
 };
 
+const StringToEnum sIndexChannelsNameToEnumTable[] = {
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_1),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_2),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_3),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_4),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_5),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_6),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_7),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_8),
+};
+
 const StringToEnum sGainModeNameToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
     STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index d15f6b4..d1a2f4f 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -29,7 +29,7 @@
 class DeviceDescriptor : public AudioPort, public AudioPortConfig
 {
 public:
-    DeviceDescriptor(const String8& name, audio_devices_t type);
+    DeviceDescriptor(audio_devices_t type);
 
     virtual ~DeviceDescriptor() {}
 
@@ -41,19 +41,21 @@
             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 mTag;
     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;
 };
@@ -70,12 +72,12 @@
     audio_devices_t types() const { return mDeviceTypes; }
 
     void loadDevicesFromType(audio_devices_t types);
-    void loadDevicesFromName(char *name, const DeviceVector& declaredDevices);
+    void loadDevicesFromTag(char *tag, const DeviceVector& declaredDevices);
 
     sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const;
     DeviceVector getDevicesFromType(audio_devices_t types) const;
     sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
-    sp<DeviceDescriptor> getDeviceFromName(const String8& name) const;
+    sp<DeviceDescriptor> getDeviceFromTag(const String8& tag) const;
     DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) const;
 
     audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const;
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..a278375 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,188 @@
     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), mGlobalRefCount(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);
+
+    // handle stream-independent ref count
+    uint32_t oldGlobalRefCount = mGlobalRefCount;
+    if ((delta + (int)mGlobalRefCount) < 0) {
+        ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount);
+        mGlobalRefCount = 0;
+    } else {
+        mGlobalRefCount += delta;
+    }
+    if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) {
+        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+        {
+            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId,
+                    MIX_STATE_MIXING);
+        }
+
+    } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) {
+        if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+        {
+            mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId,
+                    MIX_STATE_IDLE);
+        }
+    }
+}
+
+
+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 +402,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 +420,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 +431,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 +442,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 +470,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..6f1998c 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;
@@ -176,17 +176,17 @@
 
     ssize_t index = indexOfKey(address);
     if (index < 0) {
-        ALOGW("getInputForAttr() no policy for address %s", address.string());
+        ALOGW("getInputMixForAttr() no policy for address %s", address.string());
         return BAD_VALUE;
     }
     sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
-    AudioMix mix = audioPolicyMix->getMix();
+    AudioMix *mix = audioPolicyMix->getMix();
 
-    if (mix.mMixType != MIX_TYPE_PLAYERS) {
-        ALOGW("getInputForAttr() bad policy mix type for address %s", address.string());
+    if (mix->mMixType != MIX_TYPE_PLAYERS) {
+        ALOGW("getInputMixForAttr() 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..afcd073 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;
@@ -128,7 +146,7 @@
                     break;
                 }
             }
-            if (!hasFormat) { // never import a channel mask twice
+            if (!hasFormat) { // never import a format twice
                 mFormats.add(format);
             }
         }
@@ -198,6 +216,12 @@
         }
         str = strtok(NULL, "|");
     }
+    // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
+    // TODO: compareFormats could be a lambda to convert between pointer-to-format to format:
+    // [](const audio_format_t *format1, const audio_format_t *format2) {
+    //     return compareFormats(*format1, *format2);
+    // }
+    mFormats.sort(compareFormats);
 }
 
 void AudioPort::loadInChannels(char *name)
@@ -216,8 +240,14 @@
                 (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable,
                                                    ARRAY_SIZE(sInChannelsNameToEnumTable),
                                                    str);
+        if (channelMask == 0) { // if not found, check the channel index table
+            channelMask = (audio_channel_mask_t)
+                      ConfigParsingUtils::stringToEnum(sIndexChannelsNameToEnumTable,
+                              ARRAY_SIZE(sIndexChannelsNameToEnumTable),
+                              str);
+        }
         if (channelMask != 0) {
-            ALOGV("loadInChannels() adding channelMask %04x", channelMask);
+            ALOGV("loadInChannels() adding channelMask %#x", channelMask);
             mChannelMasks.add(channelMask);
         }
         str = strtok(NULL, "|");
@@ -242,6 +272,12 @@
                 (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable,
                                                    ARRAY_SIZE(sOutChannelsNameToEnumTable),
                                                    str);
+        if (channelMask == 0) { // if not found, check the channel index table
+            channelMask = (audio_channel_mask_t)
+                      ConfigParsingUtils::stringToEnum(sIndexChannelsNameToEnumTable,
+                              ARRAY_SIZE(sIndexChannelsNameToEnumTable),
+                              str);
+        }
         if (channelMask != 0) {
             mChannelMasks.add(channelMask);
         }
@@ -340,6 +376,9 @@
         uint32_t *updatedSamplingRate) const
 {
     if (mSamplingRates.isEmpty()) {
+        if (updatedSamplingRate != NULL) {
+            *updatedSamplingRate = samplingRate;
+        }
         return NO_ERROR;
     }
 
@@ -369,16 +408,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 +422,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,35 +447,101 @@
     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;
     }
 
     const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK;
+    const bool isIndex = audio_channel_mask_get_representation(channelMask)
+            == AUDIO_CHANNEL_REPRESENTATION_INDEX;
+    int bestMatch = 0;
     for (size_t i = 0; i < mChannelMasks.size(); i ++) {
-        // FIXME Does not handle multi-channel automatic conversions yet
         audio_channel_mask_t supported = mChannelMasks[i];
         if (supported == channelMask) {
+            // Exact matches always taken.
+            if (updatedChannelMask != NULL) {
+                *updatedChannelMask = channelMask;
+            }
             return NO_ERROR;
         }
-        if (isRecordThread) {
-            // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix.
-            // FIXME Abstract this out to a table.
-            if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO)
-                    && channelMask == AUDIO_CHANNEL_IN_MONO) ||
-                (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
-                    || channelMask == AUDIO_CHANNEL_IN_STEREO))) {
-                return NO_ERROR;
+
+        // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
+        if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
+            // Approximate (best) match:
+            // The match score measures how well the supported channel mask matches the
+            // desired mask, where increasing-is-better.
+            //
+            // TODO: Some tweaks may be needed.
+            // Should be a static function of the data processing library.
+            //
+            // In priority:
+            // match score = 1000 if legacy channel conversion equivalent (always prefer this)
+            // OR
+            // match score += 100 if the channel mask representations match
+            // match score += number of channels matched.
+            //
+            // If there are no matched channels, the mask may still be accepted
+            // but the playback or record will be silent.
+            const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
+                    == AUDIO_CHANNEL_REPRESENTATION_INDEX);
+            int match;
+            if (isIndex && isSupportedIndex) {
+                // index equivalence
+                match = 100 + __builtin_popcount(
+                        audio_channel_mask_get_bits(channelMask)
+                            & audio_channel_mask_get_bits(supported));
+            } else if (isIndex && !isSupportedIndex) {
+                const uint32_t equivalentBits =
+                        (1 << audio_channel_count_from_in_mask(supported)) - 1 ;
+                match = __builtin_popcount(
+                        audio_channel_mask_get_bits(channelMask) & equivalentBits);
+            } else if (!isIndex && isSupportedIndex) {
+                const uint32_t equivalentBits =
+                        (1 << audio_channel_count_from_in_mask(channelMask)) - 1;
+                match = __builtin_popcount(
+                        equivalentBits & audio_channel_mask_get_bits(supported));
+            } else {
+                // positional equivalence
+                match = 100 + __builtin_popcount(
+                        audio_channel_mask_get_bits(channelMask)
+                            & audio_channel_mask_get_bits(supported));
+                switch (supported) {
+                case AUDIO_CHANNEL_IN_FRONT_BACK:
+                case AUDIO_CHANNEL_IN_STEREO:
+                    if (channelMask == AUDIO_CHANNEL_IN_MONO) {
+                        match = 1000;
+                    }
+                    break;
+                case AUDIO_CHANNEL_IN_MONO:
+                    if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
+                            || channelMask == AUDIO_CHANNEL_IN_STEREO) {
+                        match = 1000;
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+            if (match > bestMatch) {
+                bestMatch = match;
+                if (updatedChannelMask != NULL) {
+                    *updatedChannelMask = supported;
+                } else {
+                    return NO_ERROR; // any match will do in this case.
+                }
             }
         }
     }
-    return BAD_VALUE;
+    return bestMatch > 0 ? NO_ERROR : 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 +555,35 @@
     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);
+
+    // iterate from best format to worst format (reverse order)
+    for (ssize_t i = mFormats.size() - 1; i >= 0 ; --i) {
+        if (mFormats[i] == format ||
+                (checkInexact
+                        && mFormats[i] != AUDIO_FORMAT_DEFAULT
+                        && audio_is_linear_pcm(mFormats[i]))) {
+            // for inexact checks we take the first linear pcm format due to sorting.
+            if (updatedFormat != NULL) {
+                *updatedFormat = mFormats[i];
+            }
+            return NO_ERROR;
+        }
+    }
+    return BAD_VALUE;
+}
 
 uint32_t AudioPort::pickSamplingRate() const
 {
@@ -482,9 +611,13 @@
 
     // For mixed output and inputs, use max mixer sampling rates. Do not
     // limit sampling rate otherwise
+    // For inputs, also see checkCompatibleSamplingRate().
     if (mType != AUDIO_PORT_TYPE_MIX) {
         maxRate = UINT_MAX;
     }
+    // TODO: should mSamplingRates[] be ordered in terms of our preference
+    // and we return the first (and hence most preferred) match?  This is of concern if
+    // we want to choose 96kHz over 192kHz for USB driver stability or resource constraints.
     for (size_t i = 0; i < mSamplingRates.size(); i ++) {
         if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) {
             samplingRate = mSamplingRates[i];
@@ -629,7 +762,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);
     }
@@ -673,10 +806,15 @@
             const char *formatStr = ConfigParsingUtils::enumToString(sFormatNameToEnumTable,
                                                  ARRAY_SIZE(sFormatNameToEnumTable),
                                                  mFormats[i]);
-            if (i == 0 && strcmp(formatStr, "") == 0) {
+            const bool isEmptyStr = formatStr[0] == 0;
+            if (i == 0 && isEmptyStr) {
                 snprintf(buffer, SIZE, "Dynamic");
             } else {
-                snprintf(buffer, SIZE, "%s", formatStr);
+                if (isEmptyStr) {
+                    snprintf(buffer, SIZE, "%#x", mFormats[i]);
+                } else {
+                    snprintf(buffer, SIZE, "%s", formatStr);
+                }
             }
             result.append(buffer);
             result.append(i == (mFormats.size() - 1) ? "" : ", ");
@@ -687,13 +825,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 +876,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..89ef045 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, "|");
@@ -218,23 +218,23 @@
     node = node->first_child;
     while (node) {
         if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
-            availableOutputDevices.loadDevicesFromName((char *)node->value,
+            availableOutputDevices.loadDevicesFromTag((char *)node->value,
                                                         declaredDevices);
             ALOGV("loadGlobalConfig() Attached Output Devices %08x",
                   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);
+                defaultOutputDevice = new DeviceDescriptor(device);
             } else {
                 ALOGW("loadGlobalConfig() default device not specified");
             }
             ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type());
         } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
-            availableInputDevices.loadDevicesFromName((char *)node->value,
+            availableInputDevices.loadDevicesFromTag((char *)node->value,
                                                        declaredDevices);
             ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types());
         } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 7df7d75..797077a 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -24,24 +24,35 @@
 
 namespace android {
 
-String8 DeviceDescriptor::emptyNameStr = String8("");
-
-DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) :
-    AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
+DeviceDescriptor::DeviceDescriptor(audio_devices_t type) :
+    AudioPort(String8(""), 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),
+    mTag(""), 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:
     // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE)
     // - have the same address or one device does not specify the address
     // - have the same channel mask or one device does not specify the channel mask
+    if (other == 0) {
+        return false;
+    }
     return (mDeviceType == other->mDeviceType) &&
            (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) &&
            (mChannelMask == 0 || other->mChannelMask == 0 ||
@@ -129,21 +140,21 @@
         uint32_t i = 31 - __builtin_clz(types);
         uint32_t type = 1 << i;
         types &= ~type;
-        add(new DeviceDescriptor(String8("device_type"), type | role_bit));
+        add(new DeviceDescriptor(type | role_bit));
     }
 }
 
-void DeviceVector::loadDevicesFromName(char *name,
+void DeviceVector::loadDevicesFromTag(char *tag,
                                        const DeviceVector& declaredDevices)
 {
-    char *devName = strtok(name, "|");
-    while (devName != NULL) {
-        if (strlen(devName) != 0) {
-            audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceNameToEnumTable,
-                                 ARRAY_SIZE(sDeviceNameToEnumTable),
-                                 devName);
+    char *devTag = strtok(tag, "|");
+    while (devTag != NULL) {
+        if (strlen(devTag) != 0) {
+            audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceTypeToEnumTable,
+                                 ARRAY_SIZE(sDeviceTypeToEnumTable),
+                                 devTag);
             if (type != AUDIO_DEVICE_NONE) {
-                sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(name), type);
+                sp<DeviceDescriptor> dev = new DeviceDescriptor(type);
                 if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX ||
                         type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) {
                     dev->mAddress = String8("0");
@@ -151,13 +162,13 @@
                 add(dev);
             } else {
                 sp<DeviceDescriptor> deviceDesc =
-                        declaredDevices.getDeviceFromName(String8(devName));
+                        declaredDevices.getDeviceFromTag(String8(devTag));
                 if (deviceDesc != 0) {
                     add(deviceDesc);
                 }
             }
          }
-         devName = strtok(NULL, "|");
+         devTag = strtok(NULL, "|");
      }
 }
 
@@ -183,7 +194,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;
         }
@@ -223,11 +234,11 @@
     return devices;
 }
 
-sp<DeviceDescriptor> DeviceVector::getDeviceFromName(const String8& name) const
+sp<DeviceDescriptor> DeviceVector::getDeviceFromTag(const String8& tag) const
 {
     sp<DeviceDescriptor> device;
     for (size_t i = 0; i < size(); i++) {
-        if (itemAt(i)->mName == name) {
+        if (itemAt(i)->mTag == tag) {
             device = itemAt(i);
             break;
         }
@@ -303,8 +314,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 +328,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..7e2050b 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) {
@@ -58,7 +58,7 @@
         } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
             profile->loadInChannels((char *)node->value);
         } else if (strcmp(node->name, DEVICES_TAG) == 0) {
-            profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+            profile->mSupportedDevices.loadDevicesFromTag((char *)node->value,
                                                            mDeclaredDevices);
         } else if (strcmp(node->name, FLAGS_TAG) == 0) {
             profile->mFlags = ConfigParsingUtils::parseInputFlagNames((char *)node->value);
@@ -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) {
@@ -104,7 +105,7 @@
         } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
             profile->loadOutChannels((char *)node->value);
         } else if (strcmp(node->name, DEVICES_TAG) == 0) {
-            profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+            profile->mSupportedDevices.loadDevicesFromTag((char *)node->value,
                                                            mDeclaredDevices);
         } else if (strcmp(node->name, FLAGS_TAG) == 0) {
             profile->mFlags = ConfigParsingUtils::parseOutputFlagNames((char *)node->value);
@@ -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 {
@@ -153,8 +154,8 @@
         ALOGW("loadDevice() bad type %08x", type);
         return BAD_VALUE;
     }
-    sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
-    deviceDesc->mModule = this;
+    sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(type);
+    deviceDesc->mTag = String8(root->name);
 
     node = root->first_child;
     while (node) {
@@ -172,8 +173,8 @@
         node = node->next;
     }
 
-    ALOGV("loadDevice() adding device name %s type %08x address %s",
-          deviceDesc->mName.string(), type, deviceDesc->mAddress.string());
+    ALOGV("loadDevice() adding device tag %s type %08x address %s",
+          deviceDesc->mTag.string(), type, deviceDesc->mAddress.string());
 
     mDeclaredDevices.add(deviceDesc);
 
@@ -183,16 +184,17 @@
 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);
     profile->mFormats.add(config->format);
 
-    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device);
+    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device);
     devDesc->mAddress = address;
     profile->mSupportedDevices.add(devDesc);
 
+    profile->attach(this);
     mOutputProfiles.add(profile);
 
     return NO_ERROR;
@@ -213,18 +215,19 @@
 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);
     profile->mFormats.add(config->format);
 
-    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device);
+    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device);
     devDesc->mAddress = address;
     profile->mSupportedDevices.add(devDesc);
 
     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;
@@ -348,7 +351,8 @@
     }
 
     sp<DeviceDescriptor> devDesc =
-            new DeviceDescriptor(String8(device_name != NULL ? device_name : ""), device);
+            new DeviceDescriptor(device);
+    devDesc->mName = device_name;
     devDesc->mAddress = address;
     return devDesc;
 }
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/Android.mk b/services/audiopolicy/enginedefault/Android.mk
index b0ae835..8d43b89 100755
--- a/services/audiopolicy/enginedefault/Android.mk
+++ b/services/audiopolicy/enginedefault/Android.mk
@@ -43,6 +43,4 @@
     libutils \
     libaudioutils \
 
-include external/stlport/libstlport.mk
-
 include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 1fd3341..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;
             }
@@ -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..d1ee400 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())) {
@@ -617,10 +620,12 @@
                                               audio_io_handle_t *output,
                                               audio_session_t session,
                                               audio_stream_type_t *stream,
+                                              uid_t uid,
                                               uint32_t samplingRate,
                                               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 +644,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)) {
@@ -655,8 +660,22 @@
         return BAD_VALUE;
     }
 
-    ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x",
-          attributes.usage, attributes.content_type, attributes.tags, attributes.flags);
+    ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
+            " session %d selectedDeviceId %d",
+            attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
+            session, selectedDeviceId);
+
+    *stream = streamTypefromAttributesInt(&attributes);
+
+    // 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, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
 
     routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
     audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
@@ -668,13 +687,14 @@
     ALOGV("getOutputForAttr() device 0x%x, samplingRate %d, format %x, channelMask %x, flags %x",
           device, samplingRate, format, channelMask, flags);
 
-    *stream = streamTypefromAttributesInt(&attributes);
     *output = getOutputForDevice(device, session, *stream,
                                  samplingRate, format, channelMask,
                                  flags, offloadInfo);
     if (*output == AUDIO_IO_HANDLE_NONE) {
+        mOutputRoutes.removeRoute(session);
         return INVALID_OPERATION;
     }
+
     return NO_ERROR;
 }
 
@@ -699,7 +719,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 =
@@ -747,6 +768,9 @@
     if (stream != AUDIO_STREAM_MUSIC) {
         flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
     }
+    if (stream == AUDIO_STREAM_TTS) {
+        flags = AUDIO_OUTPUT_FLAG_TTS;
+    }
 
     sp<IOProfile> profile;
 
@@ -775,10 +799,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 +819,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 +830,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 +880,6 @@
     }
 
 non_direct_output:
-
     // ignoring channel mask due to downmix capability in mixer
 
     // open a non direct output
@@ -874,7 +897,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 +925,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 +964,63 @@
                                              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);
+
+    // Routing?
+    mOutputRoutes.incRouteActivity(session);
+
+    audio_devices_t newDevice;
+    if (outputDesc->mPolicyMix != NULL) {
+        newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+    } else if (mOutputRoutes.hasRouteChanged(session)) {
+        newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
+        checkStrategyRoute(getStrategy(stream), output);
+    } else {
+        newDevice = AUDIO_DEVICE_NONE;
+    }
+
+    uint32_t delayMs = 0;
+
+    status_t status = startSource(outputDesc, stream, newDevice, &delayMs);
+
+    if (status != NO_ERROR) {
+        mOutputRoutes.decRouteActivity(session);
+        return status;
+    }
+    // Automatically enable the remote submix input when output is started on a re routing mix
+    // of type MIX_TYPE_RECORDERS
+    if (audio_is_remote_submix_device(newDevice) && 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,20 +1033,15 @@
         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()
     outputDesc->changeRefCount(stream, 1);
 
-    if (outputDesc->mRefCount[stream] == 1) {
+    if (outputDesc->mRefCount[stream] == 1 || device != AUDIO_DEVICE_NONE) {
         // 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 +1057,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 +1069,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 +1078,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 +1106,39 @@
         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?
+    bool forceDeviceUpdate = false;
+    if (outputDesc->mRefCount[stream] > 0) {
+        int activityCount = mOutputRoutes.decRouteActivity(session);
+        forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0));
+
+        if (forceDeviceUpdate) {
+            checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
+        }
+    }
+
+    return stopSource(outputDesc, stream, forceDeviceUpdate);
+}
+
+status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc,
+                                            audio_stream_type_t stream,
+                                            bool forceDeviceUpdate)
+{
     // always handle stream stop, check which stream type is stopping
     handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
 
@@ -1067,41 +1150,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");
-            }
 
+        // store time at which the stream was stopped - see isStreamActive()
+        if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) {
             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 +1182,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 +1211,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 +1226,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();
         }
@@ -1162,10 +1239,12 @@
 status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
                                              audio_io_handle_t *input,
                                              audio_session_t session,
+                                             uid_t uid,
                                              uint32_t samplingRate,
                                              audio_format_t format,
                                              audio_channel_mask_t channelMask,
                                              audio_input_flags_t flags,
+                                             audio_port_handle_t selectedDeviceId,
                                              input_type_t *inputType)
 {
     ALOGV("getInputForAttr() source %d, samplingRate %d, format %d, channelMask %x,"
@@ -1187,9 +1266,19 @@
     }
     halInputSource = inputSource;
 
+    // Explicit routing?
+    sp<DeviceDescriptor> deviceDesc;
+    for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
+        if (mAvailableInputDevices[i]->getId() == selectedDeviceId) {
+            deviceDesc = mAvailableInputDevices[i];
+            break;
+        }
+    }
+    mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
+
     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 +1336,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,18 +1395,19 @@
     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();
+
     return NO_ERROR;
 }
 
@@ -1353,7 +1449,17 @@
         }
     }
 
-    if (inputDesc->mRefCount == 0) {
+    // Routing?
+    mInputRoutes.incRouteActivity(session);
+
+    if (inputDesc->mRefCount == 0 || mInputRoutes.hasRouteChanged(session)) {
+        // if input maps to a dynamic policy with an activity listener, notify of state change
+        if ((inputDesc->mPolicyMix != NULL)
+                && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+            mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+                    MIX_STATE_MIXING);
+        }
+
         if (mInputs.activeInputsCount() == 0) {
             SoundTrigger::setCaptureState(true);
         }
@@ -1406,7 +1512,17 @@
     }
 
     inputDesc->mRefCount--;
+
+    // Routing?
+    mInputRoutes.decRouteActivity(session);
+
     if (inputDesc->mRefCount == 0) {
+        // if input maps to a dynamic policy with an activity listener, notify of state change
+        if ((inputDesc->mPolicyMix != NULL)
+                && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+            mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId,
+                    MIX_STATE_IDLE);
+        }
 
         // automatically disable the remote submix output when input is stopped if not
         // used by a policy mix of type MIX_TYPE_RECORDERS
@@ -1442,6 +1558,10 @@
         ALOGW("releaseInput() releasing unknown input %d", input);
         return;
     }
+
+    // Routing
+    mInputRoutes.removeRoute(session);
+
     sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
     ALOG_ASSERT(inputDesc != 0);
 
@@ -1505,8 +1625,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 +1634,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 +1663,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 +1696,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 +1720,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 +1774,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 +1934,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 +2152,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 +2186,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 +2203,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 +2243,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 +2299,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 +2321,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 +2383,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 +2449,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;
             }
@@ -2356,6 +2497,12 @@
     return status;
 }
 
+void AudioPolicyManager::releaseResourcesForUid(uid_t uid)
+{
+    clearAudioPatches(uid);
+    clearSessionRoutes(uid);
+}
+
 void AudioPolicyManager::clearAudioPatches(uid_t uid)
 {
     for (ssize_t i = (ssize_t)mAudioPatches.size() - 1; i >= 0; i--)  {
@@ -2366,6 +2513,82 @@
     }
 }
 
+
+void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy,
+                                            audio_io_handle_t ouptutToSkip)
+{
+    audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
+    SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);
+    for (size_t j = 0; j < mOutputs.size(); j++) {
+        if (mOutputs.keyAt(j) == ouptutToSkip) {
+            continue;
+        }
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(j);
+        if (!isStrategyActive(outputDesc, (routing_strategy)strategy)) {
+            continue;
+        }
+        // If the default device for this strategy is on another output mix,
+        // invalidate all tracks in this strategy to force re connection.
+        // Otherwise select new device on the output mix.
+        if (outputs.indexOf(mOutputs.keyAt(j)) < 0) {
+            for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
+                if (stream == AUDIO_STREAM_PATCH) {
+                    continue;
+                }
+                if (getStrategy((audio_stream_type_t)stream) == strategy) {
+                    mpClientInterface->invalidateStream((audio_stream_type_t)stream);
+                }
+            }
+        } else {
+            audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
+            setOutputDevice(outputDesc, newDevice, false);
+        }
+    }
+}
+
+void AudioPolicyManager::clearSessionRoutes(uid_t uid)
+{
+    // remove output routes associated with this uid
+    SortedVector<routing_strategy> affectedStrategies;
+    for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--)  {
+        sp<SessionRoute> route = mOutputRoutes.valueAt(i);
+        if (route->mUid == uid) {
+            mOutputRoutes.removeItemsAt(i);
+            if (route->mDeviceDescriptor != 0) {
+                affectedStrategies.add(getStrategy(route->mStreamType));
+            }
+        }
+    }
+    // reroute outputs if necessary
+    for (size_t i = 0; i < affectedStrategies.size(); i++) {
+        checkStrategyRoute(affectedStrategies[i], AUDIO_IO_HANDLE_NONE);
+    }
+
+    // remove input routes associated with this uid
+    SortedVector<audio_source_t> affectedSources;
+    for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--)  {
+        sp<SessionRoute> route = mInputRoutes.valueAt(i);
+        if (route->mUid == uid) {
+            mInputRoutes.removeItemsAt(i);
+            if (route->mDeviceDescriptor != 0) {
+                affectedSources.add(route->mSource);
+            }
+        }
+    }
+    // reroute inputs if necessary
+    SortedVector<audio_io_handle_t> inputsToClose;
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
+        if (affectedSources.indexOf(inputDesc->mInputSource) >= 0) {
+            inputsToClose.add(inputDesc->mIoHandle);
+        }
+    }
+    for (size_t i = 0; i < inputsToClose.size(); i++) {
+        closeInput(inputsToClose[i]);
+    }
+}
+
+
 status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session,
                                        audio_io_handle_t *ioHandle,
                                        audio_devices_t *device)
@@ -2377,6 +2600,18 @@
     return mSoundTriggerSessions.acquireSession(*session, *ioHandle);
 }
 
+status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *source __unused,
+                                       const audio_attributes_t *attributes __unused,
+                                       audio_io_handle_t *handle __unused)
+{
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyManager::stopAudioSource(audio_io_handle_t handle __unused)
+{
+    return INVALID_OPERATION;
+}
+
 // ----------------------------------------------------------------------------
 // AudioPolicyManager
 // ----------------------------------------------------------------------------
@@ -2390,7 +2625,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),
@@ -2417,7 +2651,7 @@
     mUidCached = getuid();
     mpClientInterface = clientInterface;
 
-    mDefaultOutputDevice = new DeviceDescriptor(String8("Speaker"), AUDIO_DEVICE_OUT_SPEAKER);
+    mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
     if (ConfigParsingUtils::loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE,
                  mHwModules, mAvailableInputDevices, mAvailableOutputDevices,
                  mDefaultOutputDevice, mSpeakerDrcEnabled) != NO_ERROR) {
@@ -2474,7 +2708,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 +2717,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 +2745,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 +2793,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 +2855,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 +2995,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 +3023,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 +3059,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 +3078,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 +3097,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 +3105,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 +3164,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 +3174,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 +3244,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 +3269,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 +3277,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 +3321,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 +3339,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 +3449,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 +3576,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 +3585,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,15 +3654,17 @@
     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;
 
     ALOGVV("getOutputsForDevice() device %04x", device);
     for (size_t i = 0; i < openOutputs.size(); i++) {
         ALOGVV("output %d isDuplicated=%d device=%04x",
-                i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices());
+                i, openOutputs.valueAt(i)->isDuplicated(),
+                openOutputs.valueAt(i)->supportedDevices());
         if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) {
             ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i));
             outputs.add(openOutputs.keyAt(i));
@@ -3459,14 +3698,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 +3717,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 +3817,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);
@@ -3657,7 +3895,6 @@
 
     audio_devices_t device = getDeviceAndMixForInputSource(inputDesc->mInputSource);
 
-    ALOGV("getNewInputDevice() selected device %x", device);
     return device;
 }
 
@@ -3761,9 +3998,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 +4016,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->isActive()) {
+            return route->mDeviceDescriptor->type();
+        }
+    }
+
     if (fromCache) {
         ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
               strategy, mDeviceForStrategy[strategy]);
@@ -3812,7 +4064,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 +4083,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 +4110,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 +4127,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 +4168,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 +4179,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 +4246,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 +4364,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 +4385,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;
             }
@@ -4158,21 +4414,21 @@
 
 audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
 {
-    return mEngine->getDeviceForInputSource(inputSource);
+    for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) {
+         sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex);
+         if (inputSource == route->mSource && route->isActive()) {
+             return route->mDeviceDescriptor->type();
+         }
+     }
+
+     return mEngine->getDeviceForInputSource(inputSource);
 }
 
 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 +4446,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 +4491,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 +4521,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 +4542,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 +4553,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 +4590,7 @@
         if (--outputDesc->mMuteCount[stream] == 0) {
             checkAndSetVolume(stream,
                               streamDesc.getVolumeIndex(device),
-                              output,
+                              outputDesc,
                               device,
                               delayMs);
         }
@@ -4373,7 +4609,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,18 +4642,113 @@
     }
 }
 
+// --- SessionRoute class implementation
+void AudioPolicyManager::SessionRoute::log(const char* prefix) {
+    ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d",
+          prefix, mStreamType, mSource, mSession,
+          mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
+          mRefCount, mActivityCount);
+}
+
+// --- SessionRouteMap class implementation
+bool AudioPolicyManager::SessionRouteMap::hasRoute(audio_session_t session)
+{
+    return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
+}
+
+bool AudioPolicyManager::SessionRouteMap::hasRouteChanged(audio_session_t session)
+{
+    if (indexOfKey(session) >= 0) {
+        if (valueFor(session)->mChanged) {
+            valueFor(session)->mChanged = false;
+            return true;
+        }
+    }
+    return false;
+}
+
+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::SessionRouteMap::addRoute(audio_session_t session,
+                                                   audio_stream_type_t streamType,
+                                                   audio_source_t source,
+                                                   sp<DeviceDescriptor> descriptor,
+                                                   uid_t uid)
+{
+    if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) {
+        ALOGE("Adding Output Route to InputRouteMap");
+        return;
+    } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) {
+        ALOGE("Adding Input Route to OutputRouteMap");
+        return;
+    }
+
+    sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+
+    if (route != 0) {
+        if (((route->mDeviceDescriptor == 0) && (descriptor != 0)) ||
+                ((route->mDeviceDescriptor != 0) &&
+                 ((descriptor == 0) || (!route->mDeviceDescriptor->equals(descriptor))))) {
+            route->mChanged = true;
+        }
+        route->mRefCount++;
+        route->mDeviceDescriptor = descriptor;
+    } else {
+        route = new AudioPolicyManager::SessionRoute(session, streamType, source, descriptor, uid);
+        route->mRefCount++;
+        add(session, route);
+        if (descriptor != 0) {
+            route->mChanged = true;
+        }
+    }
+}
+
 void AudioPolicyManager::defaultAudioPolicyConfig(void)
 {
     sp<HwModule> module;
     sp<IOProfile> profile;
     sp<DeviceDescriptor> defaultInputDevice =
-                    new DeviceDescriptor(String8("builtin-mic"), AUDIO_DEVICE_IN_BUILTIN_MIC);
+                    new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
     mAvailableOutputDevices.add(mDefaultOutputDevice);
     mAvailableInputDevices.add(defaultInputDevice);
 
     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 +4756,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..ea16864 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
@@ -106,10 +109,12 @@
                                           audio_io_handle_t *output,
                                           audio_session_t session,
                                           audio_stream_type_t *stream,
+                                          uid_t uid,
                                           uint32_t samplingRate,
                                           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,
@@ -123,10 +128,12 @@
         virtual status_t getInputForAttr(const audio_attributes_t *attr,
                                          audio_io_handle_t *input,
                                          audio_session_t session,
+                                         uid_t uid,
                                          uint32_t samplingRate,
                                          audio_format_t format,
                                          audio_channel_mask_t channelMask,
                                          audio_input_flags_t flags,
+                                         audio_port_handle_t selectedDeviceId,
                                          input_type_t *inputType);
 
         // indicates to the audio policy manager that the input starts being used.
@@ -172,19 +179,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);
@@ -206,7 +209,6 @@
                                           struct audio_patch *patches,
                                           unsigned int *generation);
         virtual status_t setAudioPortConfig(const struct audio_port_config *config);
-        virtual void clearAudioPatches(uid_t uid);
 
         virtual status_t acquireSoundTriggerSession(audio_session_t *session,
                                                audio_io_handle_t *ioHandle,
@@ -220,6 +222,13 @@
         virtual status_t registerPolicyMixes(Vector<AudioMix> mixes);
         virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes);
 
+        virtual status_t startAudioSource(const struct audio_port_config *source,
+                                          const audio_attributes_t *attributes,
+                                          audio_io_handle_t *handle);
+        virtual status_t stopAudioSource(audio_io_handle_t handle);
+
+        virtual void     releaseResourcesForUid(uid_t uid);
+
         // Audio policy configuration file parsing (audio_policy.conf)
         // TODO candidates to be moved to ConfigParsingUtils
                 void defaultAudioPolicyConfig(void);
@@ -227,6 +236,94 @@
         // return the strategy corresponding to a given stream type
         routing_strategy getStrategy(audio_stream_type_t stream) const;
 
+protected:
+        class SessionRoute : public RefBase {
+        public:
+            // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the
+            // streamType argument
+            static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT;
+
+            // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the
+            // source argument
+
+            static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT;
+
+            SessionRoute(audio_session_t session,
+                         audio_stream_type_t streamType,
+                         audio_source_t source,
+                         sp<DeviceDescriptor> deviceDescriptor,
+                         uid_t uid)
+               : mUid(uid),
+                 mSession(session),
+                 mDeviceDescriptor(deviceDescriptor),
+                 mRefCount(0),
+                 mActivityCount(0),
+                 mChanged(false),
+                 mStreamType(streamType),
+                 mSource(source)
+                  {}
+
+            void log(const char* prefix);
+
+            bool isActive() {
+                return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0));
+            }
+
+            uid_t                       mUid;
+            audio_session_t             mSession;
+            sp<DeviceDescriptor>        mDeviceDescriptor;
+
+            // "reference" counting
+            int                         mRefCount;      // +/- on references
+            int                         mActivityCount; // +/- on start/stop
+            bool                        mChanged;
+            // for outputs
+            const audio_stream_type_t   mStreamType;
+            // for inputs
+            const audio_source_t        mSource;
+        };
+
+        class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute>> {
+        public:
+            // These constants identify the SessionRoutMap as holding EITHER input routes,
+            // or output routes.  An error will occur if an attempt is made to add a SessionRoute
+            // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a
+            // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT)
+            // and similarly  for output SessionRoutes and Input SessionRouteMaps.
+            typedef enum {
+              MAPTYPE_INPUT = 0,
+              MAPTYPE_OUTPUT = 1
+            } session_route_map_type_t;
+
+            SessionRouteMap(session_route_map_type_t mapType) :
+                mMapType(mapType) {
+            }
+
+            bool hasRoute(audio_session_t session);
+
+            void removeRoute(audio_session_t session);
+
+            int incRouteActivity(audio_session_t session);
+            int decRouteActivity(audio_session_t session);
+            bool hasRouteChanged(audio_session_t session); // also clears the changed flag
+            void log(const char* caption);
+
+            // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the
+            // source argument.
+            // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT
+            // in the streamType argument.
+            void addRoute(audio_session_t session,
+                          audio_stream_type_t streamType,
+                          audio_source_t source,
+                          sp<DeviceDescriptor> deviceDescriptor,
+                          uid_t uid);
+
+        private:
+            // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input)
+            // or outputs (mMapType == kSessionRouteMap_Output)
+            const session_route_map_type_t mMapType;
+        };
+
         // From AudioPolicyManagerObserver
         virtual const AudioPatchCollection &getAudioPatches() const
         {
@@ -240,7 +337,7 @@
         {
             return mPolicyMixes;
         }
-        virtual const AudioOutputCollection &getOutputs() const
+        virtual const SwAudioOutputCollection &getOutputs() const
         {
             return mOutputs;
         }
@@ -265,7 +362,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 +385,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 +406,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 +483,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 +512,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 +527,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 +553,46 @@
 
         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);
 
+        // if argument "device" is different from AUDIO_DEVICE_NONE,  startSource() will force
+        // the re-evaluation of the output device.
+        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,
+                            bool forceDeviceUpdate);
+
+        void clearAudioPatches(uid_t uid);
+        void clearSessionRoutes(uid_t uid);
+        void checkStrategyRoute(routing_strategy strategy, audio_io_handle_t ouptutToSkip);
+
         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(SessionRouteMap::MAPTYPE_OUTPUT);
+        SessionRouteMap mInputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_INPUT);
+
         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 +657,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/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 3e090e9..489a9be 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -213,6 +213,12 @@
     mAudioPolicyService->onAudioPatchListUpdate();
 }
 
+void AudioPolicyService::AudioPolicyClient::onDynamicPolicyMixStateUpdate(
+        String8 regId, int32_t state)
+{
+    mAudioPolicyService->onDynamicPolicyMixStateUpdate(regId, state);
+}
+
 audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId()
 {
     return AudioSystem::newAudioUniqueId();
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index e6ace20..282ddeb 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -109,8 +109,8 @@
         Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
         for (size_t i = 0; i < effects.size(); i++) {
             EffectDesc *effect = effects[i];
-            sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0,
-                                                 audioSession, input);
+            sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0,
+                                                 0, audioSession, input);
             status_t status = fx->initCheck();
             if (status != NO_ERROR && status != ALREADY_EXISTS) {
                 ALOGW("addInputEffects(): failed to create Fx %s on source %d",
@@ -254,7 +254,7 @@
         Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
         for (size_t i = 0; i < effects.size(); i++) {
             EffectDesc *effect = effects[i];
-            sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, 0, 0, 0,
+            sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0,
                                                  audioSession, output);
             status_t status = fx->initCheck();
             if (status != NO_ERROR && status != ALREADY_EXISTS) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index e9ff8389..65639c3 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -146,10 +146,12 @@
                                               audio_io_handle_t *output,
                                               audio_session_t session,
                                               audio_stream_type_t *stream,
+                                              uid_t uid,
                                               uint32_t samplingRate,
                                               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)
 {
     if (mAudioPolicyManager == NULL) {
@@ -157,8 +159,17 @@
     }
     ALOGV("getOutput()");
     Mutex::Autolock _l(mLock);
-    return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate,
-                                    format, channelMask, flags, offloadInfo);
+
+    // if the caller is us, trust the specified uid
+    if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) {
+        uid_t newclientUid = IPCThreadState::self()->getCallingUid();
+        if (uid != (uid_t)-1 && uid != newclientUid) {
+            ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid);
+        }
+        uid = newclientUid;
+    }
+    return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, samplingRate,
+                                    format, channelMask, flags, selectedDeviceId, offloadInfo);
 }
 
 status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -247,10 +258,12 @@
 status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
                                              audio_io_handle_t *input,
                                              audio_session_t session,
+                                             uid_t uid,
                                              uint32_t samplingRate,
                                              audio_format_t format,
                                              audio_channel_mask_t channelMask,
-                                             audio_input_flags_t flags)
+                                             audio_input_flags_t flags,
+                                             audio_port_handle_t selectedDeviceId)
 {
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
@@ -261,19 +274,28 @@
         return BAD_VALUE;
     }
 
-    if (((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) ||
-        ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) {
+    if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
         return BAD_VALUE;
     }
     sp<AudioPolicyEffects>audioPolicyEffects;
     status_t status;
     AudioPolicyInterface::input_type_t inputType;
+    // if the caller is us, trust the specified uid
+    if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) {
+        uid_t newclientUid = IPCThreadState::self()->getCallingUid();
+        if (uid != (uid_t)-1 && uid != newclientUid) {
+            ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid);
+        }
+        uid = newclientUid;
+    }
+
     {
         Mutex::Autolock _l(mLock);
         // the audio_in_acoustics_t parameter is ignored by get_input()
-        status = mAudioPolicyManager->getInputForAttr(attr, input, session,
+        status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
                                                      samplingRate, format, channelMask,
-                                                     flags, &inputType);
+                                                     flags, selectedDeviceId,
+                                                     &inputType);
         audioPolicyEffects = mAudioPolicyEffects;
 
         if (status == NO_ERROR) {
@@ -546,9 +568,6 @@
                                             unsigned int *generation)
 {
     Mutex::Autolock _l(mLock);
-    if(!modifyAudioRoutingAllowed()) {
-        return PERMISSION_DENIED;
-    }
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
@@ -559,9 +578,6 @@
 status_t AudioPolicyService::getAudioPort(struct audio_port *port)
 {
     Mutex::Autolock _l(mLock);
-    if(!modifyAudioRoutingAllowed()) {
-        return PERMISSION_DENIED;
-    }
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
@@ -602,9 +618,6 @@
         unsigned int *generation)
 {
     Mutex::Autolock _l(mLock);
-    if(!modifyAudioRoutingAllowed()) {
-        return PERMISSION_DENIED;
-    }
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
@@ -661,4 +674,26 @@
     }
 }
 
+status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
+                                  const audio_attributes_t *attributes,
+                                  audio_io_handle_t *handle)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->startAudioSource(source, attributes, handle);
+}
+
+status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->stopAudioSource(handle);
+}
+
 }; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
index 5a91192..13af3ef 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
@@ -234,10 +234,12 @@
 status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
                                              audio_io_handle_t *input,
                                              audio_session_t session,
+                                             uid_t uid __unused,
                                              uint32_t samplingRate,
                                              audio_format_t format,
                                              audio_channel_mask_t channelMask,
-                                             audio_input_flags_t flags __unused)
+                                             audio_input_flags_t flags __unused,
+                                             audio_port_handle_t selectedDeviceId __unused)
 {
     if (mpAudioPolicy == NULL) {
         return NO_INIT;
@@ -255,8 +257,7 @@
         inputSource = AUDIO_SOURCE_MIC;
     }
 
-    if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) ||
-        ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) {
+    if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
         return BAD_VALUE;
     }
 
@@ -565,10 +566,12 @@
                                               audio_io_handle_t *output,
                                               audio_session_t session __unused,
                                               audio_stream_type_t *stream,
+                                              uid_t uid __unused,
                                               uint32_t samplingRate,
                                               audio_format_t format,
                                               audio_channel_mask_t channelMask,
                                               audio_output_flags_t flags,
+                                              audio_port_handle_t selectedDeviceId __unused,
                                               const audio_offload_info_t *offloadInfo)
 {
     if (attr != NULL) {
@@ -604,4 +607,16 @@
     return INVALID_OPERATION;
 }
 
+status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
+                                  const audio_attributes_t *attributes,
+                                  audio_io_handle_t *handle)
+{
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+{
+    return INVALID_OPERATION;
+}
+
 }; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 00f188f..c5f4fb7 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -177,7 +177,7 @@
     {
         Mutex::Autolock _l(mLock);
         if (mAudioPolicyManager) {
-            mAudioPolicyManager->clearAudioPatches(uid);
+            mAudioPolicyManager->releaseResourcesForUid(uid);
         }
     }
 #endif
@@ -222,6 +222,21 @@
     }
 }
 
+void AudioPolicyService::onDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
+{
+    ALOGV("AudioPolicyService::onDynamicPolicyMixStateUpdate(%s, %d)",
+            regId.string(), state);
+    mOutputCommandThread->dynamicPolicyMixStateUpdateCommand(regId, state);
+}
+
+void AudioPolicyService::doOnDynamicPolicyMixStateUpdate(String8 regId, int32_t state)
+{
+    Mutex::Autolock _l(mNotificationClientsLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onDynamicPolicyMixStateUpdate(regId, state);
+    }
+}
+
 status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
                                                       int delayMs)
 {
@@ -262,6 +277,14 @@
     }
 }
 
+void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate(
+        String8 regId, int32_t state)
+{
+    if (mAudioPolicyServiceClient != 0) {
+            mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
+    }
+}
+
 void AudioPolicyService::binderDied(const wp<IBinder>& who) {
     ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
             IPCThreadState::self()->getCallingPid());
@@ -511,6 +534,20 @@
                         command->mStatus = af->setAudioPortConfig(&data->mConfig);
                     }
                     } break;
+                case DYN_POLICY_MIX_STATE_UPDATE: {
+                    DynPolicyMixStateUpdateData *data =
+                            (DynPolicyMixStateUpdateData *)command->mParam.get();
+                    //###ALOGV("AudioCommandThread() processing dyn policy mix state update");
+                    ALOGV("AudioCommandThread() processing dyn policy mix state update %s %d",
+                            data->mRegId.string(), data->mState);
+                    svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnDynamicPolicyMixStateUpdate(data->mRegId, data->mState);
+                    mLock.lock();
+                    } break;
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -747,6 +784,20 @@
     return sendCommand(command, delayMs);
 }
 
+void AudioPolicyService::AudioCommandThread::dynamicPolicyMixStateUpdateCommand(
+        String8 regId, int32_t state)
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = DYN_POLICY_MIX_STATE_UPDATE;
+    DynPolicyMixStateUpdateData *data = new DynPolicyMixStateUpdateData();
+    data->mRegId = regId;
+    data->mState = state;
+    command->mParam = data;
+    ALOGV("AudioCommandThread() sending dynamic policy mix (id=%s) state update to %d",
+            regId.string(), state);
+    sendCommand(command);
+}
+
 status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
 {
     {
@@ -888,6 +939,10 @@
             delayMs = 1;
         } break;
 
+        case DYN_POLICY_MIX_STATE_UPDATE: {
+
+        } break;
+
         case START_TONE:
         case STOP_TONE:
         default:
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 0378384..eb50cdd 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -80,10 +80,12 @@
                                       audio_io_handle_t *output,
                                       audio_session_t session,
                                       audio_stream_type_t *stream,
+                                      uid_t uid,
                                       uint32_t samplingRate = 0,
                                       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);
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
@@ -97,10 +99,12 @@
     virtual status_t getInputForAttr(const audio_attributes_t *attr,
                                      audio_io_handle_t *input,
                                      audio_session_t session,
+                                     uid_t uid,
                                      uint32_t samplingRate,
                                      audio_format_t format,
                                      audio_channel_mask_t channelMask,
-                                     audio_input_flags_t flags);
+                                     audio_input_flags_t flags,
+                                     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
     virtual status_t startInput(audio_io_handle_t input,
                                 audio_session_t session);
     virtual status_t stopInput(audio_io_handle_t input,
@@ -191,6 +195,11 @@
 
     virtual status_t registerPolicyMixes(Vector<AudioMix> mixes, bool registration);
 
+    virtual status_t startAudioSource(const struct audio_port_config *source,
+                                      const audio_attributes_t *attributes,
+                                      audio_io_handle_t *handle);
+    virtual status_t stopAudioSource(audio_io_handle_t handle);
+
             status_t doStopOutput(audio_io_handle_t output,
                                   audio_stream_type_t stream,
                                   audio_session_t session);
@@ -212,6 +221,9 @@
             void onAudioPatchListUpdate();
             void doOnAudioPatchListUpdate();
 
+            void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
+            void doOnDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
+
 private:
                         AudioPolicyService() ANDROID_API;
     virtual             ~AudioPolicyService();
@@ -242,6 +254,7 @@
             UPDATE_AUDIOPORT_LIST,
             UPDATE_AUDIOPATCH_LIST,
             SET_AUDIOPORT_CONFIG,
+            DYN_POLICY_MIX_STATE_UPDATE
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -279,6 +292,7 @@
                     void        updateAudioPatchListCommand();
                     status_t    setAudioPortConfigCommand(const struct audio_port_config *config,
                                                           int delayMs);
+                    void        dynamicPolicyMixStateUpdateCommand(String8 regId, int32_t state);
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
     private:
@@ -363,6 +377,12 @@
             struct audio_port_config mConfig;
         };
 
+        class DynPolicyMixStateUpdateData : public AudioCommandData {
+        public:
+            String8 mRegId;
+            int32_t mState;
+        };
+
         Mutex   mLock;
         Condition mWaitWorkCV;
         Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -468,6 +488,7 @@
 
         virtual void onAudioPortListUpdate();
         virtual void onAudioPatchListUpdate();
+        virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
 
         virtual audio_unique_id_t newAudioUniqueId();
 
@@ -483,8 +504,9 @@
                                                 uid_t uid);
         virtual             ~NotificationClient();
 
-                            void        onAudioPortListUpdate();
-                            void        onAudioPatchListUpdate();
+                            void      onAudioPortListUpdate();
+                            void      onAudioPatchListUpdate();
+                            void      onDynamicPolicyMixStateUpdate(String8 regId, int32_t state);
 
                 // IBinder::DeathRecipient
                 virtual     void        binderDied(const wp<IBinder>& who);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e9c96c6..59e1c37 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -122,7 +122,7 @@
 // should be ok for now.
 static CameraService *gCameraService;
 
-CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH),
+CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH),
         mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) {
     ALOGI("CameraService started (pid=%d)", getpid());
     gCameraService = this;
@@ -140,76 +140,90 @@
     BnCameraService::onFirstRef();
 
     camera_module_t *rawModule;
-    if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
-                (const hw_module_t **)&rawModule) < 0) {
-        ALOGE("Could not load camera HAL module");
+    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
+            (const hw_module_t **)&rawModule);
+    if (err < 0) {
+        ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
+        logServiceError("Could not load camera HAL module", err);
         mNumberOfCameras = 0;
+        return;
     }
-    else {
-        mModule = new CameraModule(rawModule);
-        ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
-        mNumberOfCameras = mModule->getNumberOfCameras();
 
-        mFlashlight = new CameraFlashlight(*mModule, *this);
-        status_t res = mFlashlight->findFlashUnits();
-        if (res) {
-            // impossible because we haven't open any camera devices.
-            ALOGE("Failed to find flash units.");
-        }
+    mModule = new CameraModule(rawModule);
+    ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
+    err = mModule->init();
+    if (err != OK) {
+        ALOGE("Could not initialize camera HAL module: %d (%s)", err,
+            strerror(-err));
+        logServiceError("Could not initialize camera HAL module", err);
 
-        for (int i = 0; i < mNumberOfCameras; i++) {
-            String8 cameraId = String8::format("%d", i);
-
-            // Defaults to use for cost and conflicting devices
-            int cost = 100;
-            char** conflicting_devices = nullptr;
-            size_t conflicting_devices_length = 0;
-
-            // If using post-2.4 module version, query the cost + conflicting devices from the HAL
-            if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
-                struct camera_info info;
-                status_t rc = mModule->getCameraInfo(i, &info);
-                if (rc == NO_ERROR) {
-                    cost = info.resource_cost;
-                    conflicting_devices = info.conflicting_devices;
-                    conflicting_devices_length = info.conflicting_devices_length;
-                } else {
-                    ALOGE("%s: Received error loading camera info for device %d, cost and"
-                            " conflicting devices fields set to defaults for this device.",
-                            __FUNCTION__, i);
-                }
-            }
-
-            std::set<String8> conflicting;
-            for (size_t i = 0; i < conflicting_devices_length; i++) {
-                conflicting.emplace(String8(conflicting_devices[i]));
-            }
-
-            // Initialize state for each camera device
-            {
-                Mutex::Autolock lock(mCameraStatesLock);
-                mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
-                        conflicting));
-            }
-
-            if (mFlashlight->hasFlashUnit(cameraId)) {
-                mTorchStatusMap.add(cameraId,
-                        ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
-            }
-        }
-
-        if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
-            mModule->setCallbacks(this);
-        }
-
-        VendorTagDescriptor::clearGlobalVendorTagDescriptor();
-
-        if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) {
-            setUpVendorTags();
-        }
-
-        CameraDeviceFactory::registerService(this);
+        mNumberOfCameras = 0;
+        delete mModule;
+        mModule = nullptr;
+        return;
     }
+
+    mNumberOfCameras = mModule->getNumberOfCameras();
+
+    mFlashlight = new CameraFlashlight(*mModule, *this);
+    status_t res = mFlashlight->findFlashUnits();
+    if (res) {
+        // impossible because we haven't open any camera devices.
+        ALOGE("Failed to find flash units.");
+    }
+
+    for (int i = 0; i < mNumberOfCameras; i++) {
+        String8 cameraId = String8::format("%d", i);
+
+        // Defaults to use for cost and conflicting devices
+        int cost = 100;
+        char** conflicting_devices = nullptr;
+        size_t conflicting_devices_length = 0;
+
+        // If using post-2.4 module version, query the cost + conflicting devices from the HAL
+        if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
+            struct camera_info info;
+            status_t rc = mModule->getCameraInfo(i, &info);
+            if (rc == NO_ERROR) {
+                cost = info.resource_cost;
+                conflicting_devices = info.conflicting_devices;
+                conflicting_devices_length = info.conflicting_devices_length;
+            } else {
+                ALOGE("%s: Received error loading camera info for device %d, cost and"
+                        " conflicting devices fields set to defaults for this device.",
+                        __FUNCTION__, i);
+            }
+        }
+
+        std::set<String8> conflicting;
+        for (size_t i = 0; i < conflicting_devices_length; i++) {
+            conflicting.emplace(String8(conflicting_devices[i]));
+        }
+
+        // Initialize state for each camera device
+        {
+            Mutex::Autolock lock(mCameraStatesLock);
+            mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
+                    conflicting));
+        }
+
+        if (mFlashlight->hasFlashUnit(cameraId)) {
+            mTorchStatusMap.add(cameraId,
+                    ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+        }
+    }
+
+    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
+        mModule->setCallbacks(this);
+    }
+
+    VendorTagDescriptor::clearGlobalVendorTagDescriptor();
+
+    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) {
+        setUpVendorTags();
+    }
+
+    CameraDeviceFactory::registerService(this);
 }
 
 CameraService::~CameraService() {
@@ -242,6 +256,8 @@
     }
 
     if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+        logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
+                newStatus));
         sp<BasicClient> clientToDisconnect;
         {
             // Don't do this in updateStatus to avoid deadlock over mServiceLock
@@ -274,6 +290,10 @@
         }
 
     } else {
+        if (oldStatus == ICameraServiceListener::Status::STATUS_NOT_PRESENT) {
+            logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
+                    newStatus));
+        }
         updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id);
     }
 
@@ -293,12 +313,11 @@
     ICameraServiceListener::TorchStatus status;
     status_t res = getTorchStatusLocked(cameraId, &status);
     if (res) {
-        ALOGE("%s: cannot get torch status of camera %s", cameraId.string());
+        ALOGE("%s: cannot get torch status of camera %s: %s (%d)",
+                __FUNCTION__, cameraId.string(), strerror(-res), res);
         return;
     }
     if (status == newStatus) {
-        ALOGE("%s: Torch state transition to the same status 0x%x not allowed",
-              __FUNCTION__, (uint32_t)newStatus);
         return;
     }
 
@@ -500,34 +519,12 @@
 int CameraService::getCameraPriorityFromProcState(int procState) {
     // Find the priority for the camera usage based on the process state.  Higher priority clients
     // win for evictions.
-    // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose
-    //       the camera to the top/foreground applications.
-    switch(procState) {
-        case PROCESS_STATE_TOP: // User visible
-            return 100;
-        case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground
-            return 90;
-        case PROCESS_STATE_PERSISTENT: // Persistent system services
-        case PROCESS_STATE_PERSISTENT_UI:
-            return 80;
-        case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes
-            return 70;
-        case PROCESS_STATE_BACKUP: // Everything else
-        case PROCESS_STATE_HEAVY_WEIGHT:
-        case PROCESS_STATE_SERVICE:
-        case PROCESS_STATE_RECEIVER:
-        case PROCESS_STATE_HOME:
-        case PROCESS_STATE_LAST_ACTIVITY:
-        case PROCESS_STATE_CACHED_ACTIVITY:
-        case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-        case PROCESS_STATE_CACHED_EMPTY:
-            return 1;
-        case PROCESS_STATE_NONEXISTENT:
-            return -1;
-        default:
-            ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__);
-            return -1;
+    if (procState < 0) {
+        ALOGE("%s: Received invalid process state %d from ActivityManagerService!", __FUNCTION__,
+                procState);
+        return -1;
     }
+    return INT_MAX - procState;
 }
 
 status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {
@@ -765,8 +762,8 @@
     } else {
         // We only trust our own process to forward client UIDs
         if (callingPid != getpid()) {
-            ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",
-                    callingPid);
+            ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)",
+                    callingPid, clientUid);
             return PERMISSION_DENIED;
         }
     }
@@ -796,10 +793,12 @@
         return -EACCES;
     }
 
-    // Only allow clients who are being used by the current foreground device user.
-    if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) {
-        ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground "
-                "device user)", callingPid);
+    // Only allow clients who are being used by the current foreground device user, unless calling
+    // from our own process.
+    if (callingPid != getpid() &&
+            (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID)) {
+        ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from previous "
+                "device user %d, current device user %d)", callingPid, clientUserId, mLastUserId);
         return PERMISSION_DENIED;
     }
 
@@ -858,7 +857,7 @@
         std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
 
     status_t ret = NO_ERROR;
-    std::vector<sp<BasicClient>> evictedClients;
+    std::vector<DescriptorPtr> evictedClients;
     DescriptorPtr clientDescriptor;
     {
         if (effectiveApiLevel == API_1) {
@@ -869,9 +868,12 @@
             if (current != nullptr) {
                 auto clientSp = current->getValue();
                 if (clientSp.get() != nullptr) { // should never be needed
-                    if (clientSp->getRemote() == remoteCallback) {
+                    if (!clientSp->canCastToApiClient(effectiveApiLevel)) {
+                        ALOGW("CameraService connect called from same client, but with a different"
+                                " API level, evicting prior client...");
+                    } else if (clientSp->getRemote() == remoteCallback) {
                         ALOGI("CameraService::connect X (PID %d) (second call from same"
-                                "app binder, returning the same client)", clientPid);
+                                " app binder, returning the same client)", clientPid);
                         *client = clientSp;
                         return NO_ERROR;
                     }
@@ -934,7 +936,7 @@
                     mActiveClientManager.getIncompatibleClients(clientDescriptor);
 
             String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
-                    "(PID %d, priority %d)", curTime.string(),
+                    "(PID %d, priority %d) due to eviction policy", curTime.string(),
                     cameraId.string(), packageName.string(), clientPid,
                     getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
 
@@ -946,6 +948,7 @@
             }
 
             // Log the client's attempt
+            Mutex::Autolock l(mLogLock);
             mEventLog.add(msg);
 
             return -EBUSY;
@@ -965,14 +968,12 @@
 
             ALOGE("CameraService::connect evicting conflicting client for camera ID %s",
                     i->getKey().string());
-            evictedClients.push_back(clientSp);
-
-            String8 curTime = getFormattedCurrentTime();
+            evictedClients.push_back(i);
 
             // Log the clients evicted
-            mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
-                    PRId32 ", priority %" PRId32 ")\n   - Evicted by device %s client for "
-                    "package %s (PID %d, priority %" PRId32 ")", curTime.string(),
+            logEvent(String8::format("EVICT device %s client held by package %s (PID"
+                    " %" PRId32 ", priority %" PRId32 ")\n   - Evicted by device %s client for"
+                    " package %s (PID %d, priority %" PRId32 ")",
                     i->getKey().string(), String8{clientSp->getPackageName()}.string(),
                     i->getOwnerId(), i->getPriority(), cameraId.string(),
                     packageName.string(), clientPid,
@@ -994,12 +995,31 @@
     // Destroy evicted clients
     for (auto& i : evictedClients) {
         // Disconnect is blocking, and should only have returned when HAL has cleaned up
-        i->disconnect(); // Clients will remove themselves from the active client list here
+        i->getValue()->disconnect(); // Clients will remove themselves from the active client list
     }
-    evictedClients.clear();
 
     IPCThreadState::self()->restoreCallingIdentity(token);
 
+    for (const auto& i : evictedClients) {
+        ALOGV("%s: Waiting for disconnect to complete for client for device %s (PID %" PRId32 ")",
+                __FUNCTION__, i->getKey().string(), i->getOwnerId());
+        ret = mActiveClientManager.waitUntilRemoved(i, DEFAULT_DISCONNECT_TIMEOUT_NS);
+        if (ret == TIMED_OUT) {
+            ALOGE("%s: Timed out waiting for client for device %s to disconnect, "
+                    "current clients:\n%s", __FUNCTION__, i->getKey().string(),
+                    mActiveClientManager.toString().string());
+            return -EBUSY;
+        }
+        if (ret != NO_ERROR) {
+            ALOGE("%s: Received error waiting for client for device %s to disconnect: %s (%d), "
+                    "current clients:\n%s", __FUNCTION__, i->getKey().string(), strerror(-ret),
+                    ret, mActiveClientManager.toString().string());
+            return ret;
+        }
+    }
+
+    evictedClients.clear();
+
     // Once clients have been disconnected, relock
     mServiceLock.lock();
 
@@ -1027,6 +1047,8 @@
             clientPackageName, clientUid, API_1, false, false, /*out*/client);
 
     if(ret != NO_ERROR) {
+        logRejected(id, getCallingPid(), String8(clientPackageName),
+                String8::format("%s (%d)", strerror(-ret), ret));
         return ret;
     }
 
@@ -1042,6 +1064,7 @@
         /*out*/
         sp<ICamera>& device) {
 
+    String8 id = String8::format("%d", cameraId);
     int apiVersion = mModule->getModuleApiVersion();
     if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
             apiVersion < CAMERA_MODULE_API_VERSION_2_3) {
@@ -1053,16 +1076,19 @@
          */
         ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!",
                 __FUNCTION__, apiVersion);
+        logRejected(id, getCallingPid(), String8(clientPackageName),
+                String8("HAL module version doesn't support legacy HAL connections"));
         return INVALID_OPERATION;
     }
 
     status_t ret = NO_ERROR;
-    String8 id = String8::format("%d", cameraId);
     sp<Client> client = nullptr;
     ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName,
             clientUid, API_1, true, false, /*out*/client);
 
     if(ret != NO_ERROR) {
+        logRejected(id, getCallingPid(), String8(clientPackageName),
+                String8::format("%s (%d)", strerror(-ret), ret));
         return ret;
     }
 
@@ -1086,6 +1112,8 @@
             /*out*/client);
 
     if(ret != NO_ERROR) {
+        logRejected(id, getCallingPid(), String8(clientPackageName),
+                String8::format("%s (%d)", strerror(-ret), ret));
         return ret;
     }
 
@@ -1105,14 +1133,14 @@
     // verify id is valid.
     auto state = getCameraState(id);
     if (state == nullptr) {
-        ALOGE("%s: camera id is invalid %s", id.string());
+        ALOGE("%s: camera id is invalid %s", __FUNCTION__, id.string());
         return -EINVAL;
     }
 
     ICameraServiceListener::Status cameraStatus = state->getStatus();
     if (cameraStatus != ICameraServiceListener::STATUS_PRESENT &&
             cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
-        ALOGE("%s: camera id is invalid %s", id.string());
+        ALOGE("%s: camera id is invalid %s", __FUNCTION__, id.string());
         return -EINVAL;
     }
 
@@ -1426,6 +1454,8 @@
         newUserId = DEFAULT_LAST_USER_ID;
     }
 
+    logUserSwitch(mLastUserId, newUserId);
+
     mLastUserId = newUserId;
 
     // Current user has switched, evict all current clients.
@@ -1444,12 +1474,12 @@
 
         ALOGE("Evicting conflicting client for camera ID %s due to user change",
                 i->getKey().string());
+
         // Log the clients evicted
-        mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
+        logEvent(String8::format("EVICT device %s client held by package %s (PID %"
                 PRId32 ", priority %" PRId32 ")\n   - Evicted due to user switch.",
-                curTime.string(), i->getKey().string(),
-                String8{clientSp->getPackageName()}.string(), i->getOwnerId(),
-                i->getPriority()));
+                i->getKey().string(), String8{clientSp->getPackageName()}.string(),
+                i->getOwnerId(), i->getPriority()));
 
     }
 
@@ -1470,22 +1500,57 @@
     mServiceLock.lock();
 }
 
-void CameraService::logDisconnected(const String8& cameraId, int clientPid,
-        const String8& clientPackage) {
-
+void CameraService::logEvent(const char* event) {
     String8 curTime = getFormattedCurrentTime();
-    // Log the clients evicted
-    mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)",
-            curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+    Mutex::Autolock l(mLogLock);
+    mEventLog.add(String8::format("%s : %s", curTime.string(), event));
 }
 
-void CameraService::logConnected(const String8& cameraId, int clientPid,
-        const String8& clientPackage) {
-
-    String8 curTime = getFormattedCurrentTime();
+void CameraService::logDisconnected(const char* cameraId, int clientPid,
+        const char* clientPackage) {
     // Log the clients evicted
-    mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)",
-            curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+    logEvent(String8::format("DISCONNECT device %s client for package %s (PID %d)", cameraId,
+            clientPackage, clientPid));
+}
+
+void CameraService::logConnected(const char* cameraId, int clientPid,
+        const char* clientPackage) {
+    // Log the clients evicted
+    logEvent(String8::format("CONNECT device %s client for package %s (PID %d)", cameraId,
+            clientPackage, clientPid));
+}
+
+void CameraService::logRejected(const char* cameraId, int clientPid,
+        const char* clientPackage, const char* reason) {
+    // Log the client rejected
+    logEvent(String8::format("REJECT device %s client for package %s (PID %d), reason: (%s)",
+            cameraId, clientPackage, clientPid, reason));
+}
+
+void CameraService::logUserSwitch(int oldUserId, int newUserId) {
+    // Log the new and old users
+    logEvent(String8::format("USER_SWITCH from old user: %d , to new user: %d", oldUserId,
+            newUserId));
+}
+
+void CameraService::logDeviceRemoved(const char* cameraId, const char* reason) {
+    // Log the device removal
+    logEvent(String8::format("REMOVE device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logDeviceAdded(const char* cameraId, const char* reason) {
+    // Log the device removal
+    logEvent(String8::format("ADD device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logClientDied(int clientPid, const char* reason) {
+    // Log the device removal
+    logEvent(String8::format("DIED client(s) with PID %d, reason: (%s)", clientPid, reason));
+}
+
+void CameraService::logServiceError(const char* msg, int errorCode) {
+    String8 curTime = getFormattedCurrentTime();
+    logEvent(String8::format("SERVICE ERROR: %s : %d (%s)", msg, errorCode, strerror(errorCode)));
 }
 
 status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
@@ -1670,6 +1735,11 @@
     return mClientPid;
 }
 
+bool CameraService::BasicClient::canCastToApiClient(apiLevel level) const {
+    // Defaults to API2.
+    return level == API_2;
+}
+
 status_t CameraService::BasicClient::startCameraOps() {
     int32_t res;
     // Notify app ops that the camera is not available
@@ -1685,12 +1755,18 @@
     res = mAppOpsManager.startOp(AppOpsManager::OP_CAMERA,
             mClientUid, mClientPackageName);
 
-    if (res != AppOpsManager::MODE_ALLOWED) {
+    if (res == AppOpsManager::MODE_ERRORED) {
         ALOGI("Camera %d: Access for \"%s\" has been revoked",
                 mCameraId, String8(mClientPackageName).string());
         return PERMISSION_DENIED;
     }
 
+    if (res == AppOpsManager::MODE_IGNORED) {
+        ALOGI("Camera %d: Access for \"%s\" has been restricted",
+                mCameraId, String8(mClientPackageName).string());
+        return INVALID_OPERATION;
+    }
+
     mOpsActive = true;
 
     // Transition device availability listeners from PRESENT -> NOT_AVAILABLE
@@ -1782,6 +1858,10 @@
     BasicClient::disconnect();
 }
 
+bool CameraService::Client::canCastToApiClient(apiLevel level) const {
+    return level == API_1;
+}
+
 CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
         mClient(client) {
 }
@@ -1911,7 +1991,7 @@
 }
 
 status_t CameraService::dump(int fd, const Vector<String16>& args) {
-    String8 result;
+    String8 result("Dump of the Camera Service:\n");
     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
         result.appendFormat("Permission Denial: "
                 "can't dump CameraService from pid=%d, uid=%d\n",
@@ -1930,6 +2010,10 @@
         if (!mModule) {
             result = String8::format("No camera module available!\n");
             write(fd, result.string(), result.size());
+
+            // Dump event log for error information
+            dumpEventLog(fd);
+
             if (locked) mServiceLock.unlock();
             return NO_ERROR;
         }
@@ -1955,17 +2039,7 @@
             desc->dump(fd, /*verbosity*/2, /*indentation*/4);
         }
 
-        result = String8("Prior client events (most recent at top):\n");
-
-        for (const auto& msg : mEventLog) {
-            result.appendFormat("%s\n", msg.string());
-        }
-
-        if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) {
-            result.append("...\n");
-        }
-
-        write(fd, result.string(), result.size());
+        dumpEventLog(fd);
 
         bool stateLocked = tryLock(mCameraStatesLock);
         if (!stateLocked) {
@@ -2073,6 +2147,24 @@
     return NO_ERROR;
 }
 
+void CameraService::dumpEventLog(int fd) {
+    String8 result = String8("\nPrior client events (most recent at top):\n");
+
+    Mutex::Autolock l(mLogLock);
+    for (const auto& msg : mEventLog) {
+        result.appendFormat("  %s\n", msg.string());
+    }
+
+    if (mEventLog.size() == DEFAULT_EVENT_LOG_LENGTH) {
+        result.append("  ...\n");
+    } else if (mEventLog.size() == 0) {
+        result.append("  [no events yet]\n");
+    }
+    result.append("\n");
+
+    write(fd, result.string(), result.size());
+}
+
 void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {
     Mutex::Autolock al(mTorchClientMapMutex);
     for (size_t i = 0; i < mTorchClientMap.size(); i++) {
@@ -2094,10 +2186,12 @@
 /*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
 
     /**
-      * While tempting to promote the wp<IBinder> into a sp,
-      * it's actually not supported by the binder driver
+      * While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
+      * binder driver
       */
 
+    logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
+
     // check torch client
     handleTorchClientBinderDied(who);
 
@@ -2133,16 +2227,20 @@
     state->updateStatus(status, cameraId, rejectSourceStates, [this]
             (const String8& cameraId, ICameraServiceListener::Status status) {
 
-            // Update torch status
-            if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
-                status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
-                // Update torch status to not available when the camera device becomes not present
-                // or not available.
-                onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
-            } else if (status == ICameraServiceListener::STATUS_PRESENT) {
-                // Update torch status to available when the camera device becomes present or
-                // available
-                onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+            if (status != ICameraServiceListener::STATUS_ENUMERATING) {
+                // Update torch status if it has a flash unit.
+                Mutex::Autolock al(mTorchStatusMutex);
+                ICameraServiceListener::TorchStatus torchStatus;
+                if (getTorchStatusLocked(cameraId, &torchStatus) !=
+                        NAME_NOT_FOUND) {
+                    ICameraServiceListener::TorchStatus newTorchStatus =
+                            status == ICameraServiceListener::STATUS_PRESENT ?
+                            ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF :
+                            ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+                    if (torchStatus != newTorchStatus) {
+                        onTorchStatusChangedLocked(cameraId, newTorchStatus);
+                    }
+                }
             }
 
             Mutex::Autolock lock(mStatusListenerLock);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index ca1c504..1041550 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -38,9 +38,9 @@
 #include "CameraFlashlight.h"
 
 #include "common/CameraModule.h"
+#include "media/RingBuffer.h"
 #include "utils/AutoConditionLock.h"
 #include "utils/ClientManager.h"
-#include "utils/RingBuffer.h"
 
 #include <set>
 #include <string>
@@ -65,33 +65,23 @@
     class Client;
     class BasicClient;
 
+    // The effective API level.  The Camera2 API running in LEGACY mode counts as API_1.
     enum apiLevel {
         API_1 = 1,
         API_2 = 2
     };
 
-    // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
+    // Process state (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
     static const int PROCESS_STATE_NONEXISTENT = -1;
-    static const int PROCESS_STATE_PERSISTENT = 0;
-    static const int PROCESS_STATE_PERSISTENT_UI = 1;
-    static const int PROCESS_STATE_TOP = 2;
-    static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
-    static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
-    static const int PROCESS_STATE_BACKUP = 5;
-    static const int PROCESS_STATE_HEAVY_WEIGHT = 6;
-    static const int PROCESS_STATE_SERVICE = 7;
-    static const int PROCESS_STATE_RECEIVER = 8;
-    static const int PROCESS_STATE_HOME = 9;
-    static const int PROCESS_STATE_LAST_ACTIVITY = 10;
-    static const int PROCESS_STATE_CACHED_ACTIVITY = 11;
-    static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
-    static const int PROCESS_STATE_CACHED_EMPTY = 13;
 
     // 3 second busy timeout when other clients are connecting
     static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
 
+    // 1 second busy timeout when other clients are disconnecting
+    static const nsecs_t DEFAULT_DISCONNECT_TIMEOUT_NS = 1000000000;
+
     // Default number of messages to store in eviction log
-    static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;
+    static const size_t DEFAULT_EVENT_LOG_LENGTH = 100;
 
     enum {
         // Default last user id
@@ -212,6 +202,10 @@
 
         // Get the PID of the application client using this
         virtual int getClientPid() const;
+
+        // Check what API level is used for this client. This is used to determine which
+        // superclass this can be cast to.
+        virtual bool canCastToApiClient(apiLevel level) const;
     protected:
         BasicClient(const sp<CameraService>& cameraService,
                 const sp<IBinder>& remoteCallback,
@@ -320,6 +314,10 @@
 
         virtual void         notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
                                          const CaptureResultExtras& resultExtras);
+
+        // Check what API level is used for this client. This is used to determine which
+        // superclass this can be cast to.
+        virtual bool canCastToApiClient(apiLevel level) const;
     protected:
         // Convert client from cookie.
         static sp<CameraService::Client> getClientFromCookie(void* user);
@@ -492,6 +490,7 @@
 
     // Circular buffer for storing event logging for dumps
     RingBuffer<String8> mEventLog;
+    Mutex mLogLock;
 
     // UID of last user.
     int mLastUserId;
@@ -546,14 +545,55 @@
     void doUserSwitch(int newUserId);
 
     /**
-     * Add a event log message that a client has been disconnected.
+     * Add an event log message.
      */
-    void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+    void logEvent(const char* event);
 
     /**
-     * Add a event log message that a client has been connected.
+     * Add an event log message that a client has been disconnected.
      */
-    void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+    void logDisconnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+    /**
+     * Add an event log message that a client has been connected.
+     */
+    void logConnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+    /**
+     * Add an event log message that a client's connect attempt has been rejected.
+     */
+    void logRejected(const char* cameraId, int clientPid, const char* clientPackage,
+            const char* reason);
+
+    /**
+     * Add an event log message that the current device user has been switched.
+     */
+    void logUserSwitch(int oldUserId, int newUserId);
+
+    /**
+     * Add an event log message that a device has been removed by the HAL
+     */
+    void logDeviceRemoved(const char* cameraId, const char* reason);
+
+    /**
+     * Add an event log message that a device has been added by the HAL
+     */
+    void logDeviceAdded(const char* cameraId, const char* reason);
+
+    /**
+     * Add an event log message that a client has unexpectedly died.
+     */
+    void logClientDied(int clientPid, const char* reason);
+
+    /**
+     * Add a event log message that a serious service-level error has occured
+     */
+    void logServiceError(const char* msg, int errorCode);
+
+    /**
+     * Dump the event log to an FD
+     */
+    void dumpEventLog(int fd);
 
     int                 mNumberOfCameras;
 
@@ -714,9 +754,10 @@
     String8 clientName8(clientPackageName);
     int clientPid = getCallingPid();
 
-    ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and "
+    ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
             "Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
-            halVersion, static_cast<int>(effectiveApiLevel));
+            (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
+            static_cast<int>(effectiveApiLevel));
 
     sp<CLIENT> client = nullptr;
     {
@@ -734,7 +775,15 @@
         if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
             return ret;
         }
-        mLastUserId = multiuser_get_user_id(clientUid);
+        int userId = multiuser_get_user_id(clientUid);
+
+        if (userId != mLastUserId && clientPid != getpid() ) {
+            // If no previous user ID had been set, set to the user of the caller.
+            logUserSwitch(mLastUserId, userId);
+            LOG_ALWAYS_FATAL_IF(mLastUserId != DEFAULT_LAST_USER_ID,
+                    "Invalid state: Should never update user ID here unless was default");
+            mLastUserId = userId;
+        }
 
         // Check the shim parameters after acquiring lock, if they have already been updated and
         // we were doing a shim update, return immediately
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 6f44aee..05ede92 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;
@@ -1709,6 +1710,40 @@
     return mStreamingProcessor->setRecordingBufferCount(count);
 }
 
+void Camera2Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
+        const CaptureResultExtras& resultExtras) {
+    int32_t err = CAMERA_ERROR_UNKNOWN;
+    switch(errorCode) {
+        case ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED:
+            err = CAMERA_ERROR_RELEASED;
+            break;
+        case ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE:
+            err = CAMERA_ERROR_UNKNOWN;
+            break;
+        case ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE:
+            err = CAMERA_ERROR_SERVER_DIED;
+            break;
+        case ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
+        case ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
+        case ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
+            ALOGW("%s: Received recoverable error %d from HAL - ignoring, requestId %" PRId32,
+                    __FUNCTION__, errorCode, resultExtras.requestId);
+            return;
+        default:
+            err = CAMERA_ERROR_UNKNOWN;
+            break;
+    }
+
+    ALOGE("%s: Error condition %d reported by HAL, requestId %" PRId32, __FUNCTION__, errorCode,
+              resultExtras.requestId);
+
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+    if (l.mRemoteCallback != nullptr) {
+        l.mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, err, 0);
+    }
+}
+
+
 /** Device-related methods */
 void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
     ALOGV("%s: Autofocus state now %d, last trigger %d",
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 5a8241f..a988037 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -77,6 +77,8 @@
     virtual status_t        setParameters(const String8& params);
     virtual String8         getParameters() const;
     virtual status_t        sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+    virtual void            notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
+                                        const CaptureResultExtras& resultExtras);
 
     /**
      * Interface used by CameraService
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 5c8f750..88c5811 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -395,7 +395,7 @@
 
         heapIdx = mCallbackHeapHead;
 
-        mCallbackHeapHead = (mCallbackHeapHead + 1) & kCallbackHeapCount;
+        mCallbackHeapHead = (mCallbackHeapHead + 1) % kCallbackHeapCount;
         mCallbackHeapFree--;
 
         // TODO: Get rid of this copy by passing the gralloc queue all the way
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 6b0f8b5..c3a6842 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2100,12 +2100,7 @@
 
     delete[] reqMeteringAreas;
 
-    /* don't include jpeg thumbnail size - it's valid for
-       it to be set to (0,0), meaning 'no thumbnail' */
-    CropRegion crop = calculateCropRegion( (CropRegion::Outputs)(
-            CropRegion::OUTPUT_PREVIEW     |
-            CropRegion::OUTPUT_VIDEO       |
-            CropRegion::OUTPUT_PICTURE    ));
+    CropRegion crop = calculateCropRegion(/*previewOnly*/ false);
     int32_t reqCropRegion[4] = {
         static_cast<int32_t>(crop.left),
         static_cast<int32_t>(crop.top),
@@ -2603,7 +2598,7 @@
     ALOG_ASSERT(x >= 0, "Crop-relative X coordinate = '%d' is out of bounds"
                          "(lower = 0)", x);
 
-    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
     ALOG_ASSERT(x < previewCrop.width, "Crop-relative X coordinate = '%d' "
                     "is out of bounds (upper = %f)", x, previewCrop.width);
 
@@ -2619,7 +2614,7 @@
     ALOG_ASSERT(y >= 0, "Crop-relative Y coordinate = '%d' is out of bounds "
         "(lower = 0)", y);
 
-    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
     ALOG_ASSERT(y < previewCrop.height, "Crop-relative Y coordinate = '%d' is "
                 "out of bounds (upper = %f)", y, previewCrop.height);
 
@@ -2634,12 +2629,12 @@
 }
 
 int Parameters::normalizedXToCrop(int x) const {
-    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
     return (x + 1000) * (previewCrop.width - 1) / 2000;
 }
 
 int Parameters::normalizedYToCrop(int y) const {
-    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true);
     return (y + 1000) * (previewCrop.height - 1) / 2000;
 }
 
@@ -2855,8 +2850,7 @@
     return jpegSizes;
 }
 
-Parameters::CropRegion Parameters::calculateCropRegion(
-                            Parameters::CropRegion::Outputs outputs) const {
+Parameters::CropRegion Parameters::calculateCropRegion(bool previewOnly) const {
 
     float zoomLeft, zoomTop, zoomWidth, zoomHeight;
 
@@ -2880,90 +2874,45 @@
           maxDigitalZoom.data.f[0], zoomIncrement, zoomRatio, previewWidth,
           previewHeight, fastInfo.arrayWidth, fastInfo.arrayHeight);
 
-    /*
-     * Assumption: On the HAL side each stream buffer calculates its crop
-     * rectangle as follows:
-     *   cropRect = (zoomLeft, zoomRight,
-     *               zoomWidth, zoomHeight * zoomWidth / outputWidth);
-     *
-     * Note that if zoomWidth > bufferWidth, the new cropHeight > zoomHeight
-     *      (we can then get into trouble if the cropHeight > arrayHeight).
-     * By selecting the zoomRatio based on the smallest outputRatio, we
-     * guarantee this will never happen.
-     */
+    if (previewOnly) {
+        // Calculate a tight crop region for the preview stream only
+        float previewRatio = static_cast<float>(previewWidth) / previewHeight;
 
-    // Enumerate all possible output sizes, select the one with the smallest
-    // aspect ratio
-    float minOutputWidth, minOutputHeight, minOutputRatio;
-    {
-        float outputSizes[][2] = {
-            { static_cast<float>(previewWidth),
-              static_cast<float>(previewHeight) },
-            { static_cast<float>(videoWidth),
-              static_cast<float>(videoHeight) },
-            { static_cast<float>(jpegThumbSize[0]),
-              static_cast<float>(jpegThumbSize[1]) },
-            { static_cast<float>(pictureWidth),
-              static_cast<float>(pictureHeight) },
-        };
+        /* Ensure that the width/height never go out of bounds
+         * by scaling across a diffent dimension if an out-of-bounds
+         * possibility exists.
+         *
+         * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by
+         * calculating the zoomWidth from zoomHeight we'll actually get a
+         * zoomheight > arrayheight
+         */
+        float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight;
+        if (previewRatio >= arrayRatio) {
+            // Adjust the height based on the width
+            zoomWidth =  fastInfo.arrayWidth / zoomRatio;
+            zoomHeight = zoomWidth *
+                    previewHeight / previewWidth;
 
-        minOutputWidth = outputSizes[0][0];
-        minOutputHeight = outputSizes[0][1];
-        minOutputRatio = minOutputWidth / minOutputHeight;
-        for (unsigned int i = 0;
-             i < sizeof(outputSizes) / sizeof(outputSizes[0]);
-             ++i) {
-
-            // skip over outputs we don't want to consider for the crop region
-            if ( !((1 << i) & outputs) ) {
-                continue;
-            }
-
-            float outputWidth = outputSizes[i][0];
-            float outputHeight = outputSizes[i][1];
-            float outputRatio = outputWidth / outputHeight;
-
-            if (minOutputRatio > outputRatio) {
-                minOutputRatio = outputRatio;
-                minOutputWidth = outputWidth;
-                minOutputHeight = outputHeight;
-            }
-
-            // and then use this output ratio instead of preview output ratio
-            ALOGV("Enumerating output ratio %f = %f / %f, min is %f",
-                  outputRatio, outputWidth, outputHeight, minOutputRatio);
+        } else {
+            // Adjust the width based on the height
+            zoomHeight = fastInfo.arrayHeight / zoomRatio;
+            zoomWidth = zoomHeight *
+                    previewWidth / previewHeight;
         }
-    }
-
-    /* Ensure that the width/height never go out of bounds
-     * by scaling across a diffent dimension if an out-of-bounds
-     * possibility exists.
-     *
-     * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by
-     * calculating the zoomWidth from zoomHeight we'll actually get a
-     * zoomheight > arrayheight
-     */
-    float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight;
-    if (minOutputRatio >= arrayRatio) {
-        // Adjust the height based on the width
-        zoomWidth =  fastInfo.arrayWidth / zoomRatio;
-        zoomHeight = zoomWidth *
-                minOutputHeight / minOutputWidth;
-
     } else {
-        // Adjust the width based on the height
+        // Calculate the global crop region with a shape matching the active
+        // array.
+        zoomWidth = fastInfo.arrayWidth / zoomRatio;
         zoomHeight = fastInfo.arrayHeight / zoomRatio;
-        zoomWidth = zoomHeight *
-                minOutputWidth / minOutputHeight;
     }
-    // centering the zoom area within the active area
+
+    // center the zoom area within the active area
     zoomLeft = (fastInfo.arrayWidth - zoomWidth) / 2;
     zoomTop = (fastInfo.arrayHeight - zoomHeight) / 2;
 
     ALOGV("Crop region calculated (x=%d,y=%d,w=%f,h=%f) for zoom=%d",
         (int32_t)zoomLeft, (int32_t)zoomTop, zoomWidth, zoomHeight, this->zoom);
 
-
     CropRegion crop = { zoomLeft, zoomTop, zoomWidth, zoomHeight };
     return crop;
 }
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index e628a7e..46d48bc 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -271,21 +271,16 @@
     // if video snapshot size is currently overridden
     bool isJpegSizeOverridden();
 
-    // Calculate the crop region rectangle based on current stream sizes
+    // Calculate the crop region rectangle, either tightly about the preview
+    // resolution, or a region just based on the active array; both take
+    // into account the current zoom level.
     struct CropRegion {
         float left;
         float top;
         float width;
         float height;
-
-        enum Outputs {
-            OUTPUT_PREVIEW         = 0x01,
-            OUTPUT_VIDEO           = 0x02,
-            OUTPUT_JPEG_THUMBNAIL  = 0x04,
-            OUTPUT_PICTURE         = 0x08,
-        };
     };
-    CropRegion calculateCropRegion(CropRegion::Outputs outputs) const;
+    CropRegion calculateCropRegion(bool previewOnly) const;
 
     // Calculate the field of view of the high-resolution JPEG capture
     status_t calculatePictureFovs(float *horizFov, float *vertFov) const;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 8587e0e..b6f6677 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -65,6 +65,7 @@
                                    int servicePid) :
     Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
                 cameraId, cameraFacing, clientPid, clientUid, servicePid),
+    mInputStream(),
     mRequestIdCounter(0) {
 
     ATRACE_CALL();
@@ -134,6 +135,15 @@
             ALOGE("%s: Camera %d: Sent null request.",
                     __FUNCTION__, mCameraId);
             return BAD_VALUE;
+        } else if (request->mIsReprocess) {
+            if (!mInputStream.configured) {
+                ALOGE("%s: Camera %d: no input stream is configured.", __FUNCTION__, mCameraId);
+                return BAD_VALUE;
+            } else if (streaming) {
+                ALOGE("%s: Camera %d: streaming reprocess requests not supported.", __FUNCTION__,
+                        mCameraId);
+                return BAD_VALUE;
+            }
         }
 
         CameraMetadata metadata(request->mMetadata);
@@ -182,6 +192,10 @@
         metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
                         outputStreamIds.size());
 
+        if (request->mIsReprocess) {
+            metadata.update(ANDROID_REQUEST_INPUT_STREAMS, &mInputStream.id, 1);
+        }
+
         metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
         loopCounter++; // loopCounter starts from 1
         ALOGV("%s: Camera %d: Creating request with ID %d (%d of %zu)",
@@ -260,8 +274,8 @@
 }
 
 status_t CameraDeviceClient::endConfigure() {
-    ALOGV("%s: ending configure (%zu streams)",
-            __FUNCTION__, mStreamMap.size());
+    ALOGV("%s: ending configure (%d input stream, %zu output streams)",
+            __FUNCTION__, mInputStream.configured ? 1 : 0, mStreamMap.size());
 
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
@@ -284,19 +298,25 @@
 
     if (!mDevice.get()) return DEAD_OBJECT;
 
-    // Guard against trying to delete non-created streams
+    bool isInput = false;
     ssize_t index = NAME_NOT_FOUND;
-    for (size_t i = 0; i < mStreamMap.size(); ++i) {
-        if (streamId == mStreamMap.valueAt(i)) {
-            index = i;
-            break;
-        }
-    }
 
-    if (index == NAME_NOT_FOUND) {
-        ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
-              "created yet", __FUNCTION__, mCameraId, streamId);
-        return BAD_VALUE;
+    if (mInputStream.configured && mInputStream.id == streamId) {
+        isInput = true;
+    } else {
+        // Guard against trying to delete non-created streams
+        for (size_t i = 0; i < mStreamMap.size(); ++i) {
+            if (streamId == mStreamMap.valueAt(i)) {
+                index = i;
+                break;
+            }
+        }
+
+        if (index == NAME_NOT_FOUND) {
+            ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
+                  "created yet", __FUNCTION__, mCameraId, streamId);
+            return BAD_VALUE;
+        }
     }
 
     // Also returns BAD_VALUE if stream ID was not valid
@@ -307,8 +327,11 @@
               " already checked and the stream ID (%d) should be valid.",
               __FUNCTION__, mCameraId, streamId);
     } else if (res == OK) {
-        mStreamMap.removeItemsAt(index);
-
+        if (isInput) {
+            mInputStream.configured = false;
+        } else {
+            mStreamMap.removeItemsAt(index);
+        }
     }
 
     return res;
@@ -450,6 +473,58 @@
 }
 
 
+status_t CameraDeviceClient::createInputStream(int width, int height,
+        int format) {
+
+    ATRACE_CALL();
+    ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+
+    status_t res;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    Mutex::Autolock icl(mBinderSerializationLock);
+    if (!mDevice.get()) return DEAD_OBJECT;
+
+    if (mInputStream.configured) {
+        ALOGE("%s: Camera %d: Already has an input stream "
+                " configuration. (ID %zd)", __FUNCTION__, mCameraId,
+                mInputStream.id);
+        return ALREADY_EXISTS;
+    }
+
+    int streamId = -1;
+    res = mDevice->createInputStream(width, height, format, &streamId);
+    if (res == OK) {
+        mInputStream.configured = true;
+        mInputStream.width = width;
+        mInputStream.height = height;
+        mInputStream.format = format;
+        mInputStream.id = streamId;
+
+        ALOGV("%s: Camera %d: Successfully created a new input stream ID %d",
+              __FUNCTION__, mCameraId, streamId);
+
+        return streamId;
+    }
+
+    return res;
+}
+
+status_t CameraDeviceClient::getInputBufferProducer(
+        /*out*/sp<IGraphicBufferProducer> *producer) {
+    status_t res;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    if (producer == NULL) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock icl(mBinderSerializationLock);
+    if (!mDevice.get()) return DEAD_OBJECT;
+
+    return mDevice->getInputBufferProducer(producer);
+}
+
 bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height,
         int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
         /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
@@ -592,6 +667,37 @@
     return mDevice->flush(lastFrameNumber);
 }
 
+status_t CameraDeviceClient::prepare(int streamId) {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    status_t res = OK;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    Mutex::Autolock icl(mBinderSerializationLock);
+
+    // Guard against trying to prepare non-created streams
+    ssize_t index = NAME_NOT_FOUND;
+    for (size_t i = 0; i < mStreamMap.size(); ++i) {
+        if (streamId == mStreamMap.valueAt(i)) {
+            index = i;
+            break;
+        }
+    }
+
+    if (index == NAME_NOT_FOUND) {
+        ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
+              "created yet", __FUNCTION__, mCameraId, streamId);
+        return BAD_VALUE;
+    }
+
+    // Also returns BAD_VALUE if stream ID was not valid, or stream already
+    // has been used
+    res = mDevice->prepare(streamId);
+
+    return res;
+}
+
 status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
     String8 result;
     result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n",
@@ -602,13 +708,19 @@
 
     result.append("  State:\n");
     result.appendFormat("    Request ID counter: %d\n", mRequestIdCounter);
+    if (mInputStream.configured) {
+        result.appendFormat("    Current input stream ID: %d\n",
+                    mInputStream.id);
+    } else {
+        result.append("    No input stream configured.\n");
+    }
     if (!mStreamMap.isEmpty()) {
-        result.append("    Current stream IDs:\n");
+        result.append("    Current output stream IDs:\n");
         for (size_t i = 0; i < mStreamMap.size(); i++) {
             result.appendFormat("      Stream %d\n", mStreamMap.valueAt(i));
         }
     } else {
-        result.append("    No streams configured.\n");
+        result.append("    No output streams configured.\n");
     }
     write(fd, result.string(), result.size());
     // TODO: print dynamic/request section from most recent requests
@@ -645,6 +757,14 @@
     }
 }
 
+void CameraDeviceClient::notifyPrepared(int streamId) {
+    // Thread safe. Don't bother locking.
+    sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+    if (remoteCb != 0) {
+        remoteCb->onPrepared(streamId);
+    }
+}
+
 void CameraDeviceClient::detachDevice() {
     if (mDevice == 0) return;
 
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index a3dbb90..b8d8bea 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -86,6 +86,13 @@
 
     virtual status_t      createStream(const OutputConfiguration &outputConfiguration);
 
+    // Create an input stream of width, height, and format.
+    virtual status_t      createInputStream(int width, int height, int format);
+
+    // Get the buffer producer of the input stream
+    virtual status_t      getInputBufferProducer(
+                                /*out*/sp<IGraphicBufferProducer> *producer);
+
     // Create a request object from a template.
     virtual status_t      createDefaultRequest(int templateId,
                                                /*out*/
@@ -102,6 +109,9 @@
     virtual status_t      flush(/*out*/
                                 int64_t* lastFrameNumber = NULL);
 
+    // Prepare stream by preallocating its buffers
+    virtual status_t      prepare(int streamId);
+
     /**
      * Interface used by CameraService
      */
@@ -128,6 +138,7 @@
     virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
                              const CaptureResultExtras& resultExtras);
     virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp);
+    virtual void notifyPrepared(int streamId);
 
     /**
      * Interface used by independent components of CameraDeviceClient.
@@ -161,10 +172,18 @@
             android_dataspace dataSpace, const CameraMetadata& info,
             /*out*/int32_t* outWidth, /*out*/int32_t* outHeight);
 
-    // IGraphicsBufferProducer binder -> Stream ID
+    // IGraphicsBufferProducer binder -> Stream ID for output streams
     KeyedVector<sp<IBinder>, int> mStreamMap;
 
-    // Stream ID
+    struct InputStreamConfiguration {
+        bool configured;
+        int32_t width;
+        int32_t height;
+        int32_t format;
+        int32_t id;
+    } mInputStream;
+
+    // Request ID
     Vector<int> mStreamingRequestList;
 
     int32_t mRequestIdCounter;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index c0c2314..ba0b264 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -280,6 +280,14 @@
 }
 
 template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyPrepared(int streamId) {
+    (void)streamId;
+
+    ALOGV("%s: Stream %d now prepared",
+            __FUNCTION__, streamId);
+}
+
+template <typename TClientBase>
 int Camera2ClientBase<TClientBase>::getCameraId() const {
     return TClientBase::mCameraId;
 }
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 168ea0a..f1cacdf 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -72,7 +72,7 @@
     virtual void          notifyAutoExposure(uint8_t newState, int triggerId);
     virtual void          notifyAutoWhitebalance(uint8_t newState,
                                                  int triggerId);
-
+    virtual void          notifyPrepared(int streamId);
 
     int                   getCameraId() const;
     const sp<CameraDeviceBase>&
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index fe55b9e..f02fc32 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -30,6 +30,7 @@
 #include "camera/CameraMetadata.h"
 #include "camera/CaptureResult.h"
 #include "common/CameraModule.h"
+#include "gui/IGraphicBufferProducer.h"
 
 namespace android {
 
@@ -110,6 +111,14 @@
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0;
 
     /**
+     * Create an input stream of width, height, and format.
+     *
+     * Return value is the stream ID if non-negative and an error if negative.
+     */
+    virtual status_t createInputStream(uint32_t width, uint32_t height,
+            int32_t format, /*out*/ int32_t *id) = 0;
+
+    /**
      * Create an input reprocess stream that uses buffers from an existing
      * output stream.
      */
@@ -150,6 +159,10 @@
      */
     virtual status_t configureStreams() = 0;
 
+    // get the buffer producer of the input stream
+    virtual status_t getInputBufferProducer(
+            sp<IGraphicBufferProducer> *producer) = 0;
+
     /**
      * Create a metadata buffer with fields that the HAL device believes are
      * best for the given use case
@@ -186,6 +199,7 @@
         virtual void notifyIdle() = 0;
         virtual void notifyShutter(const CaptureResultExtras &resultExtras,
                 nsecs_t timestamp) = 0;
+        virtual void notifyPrepared(int streamId) = 0;
 
         // Required only for API1
         virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
@@ -268,6 +282,12 @@
     virtual status_t flush(int64_t *lastFrameNumber = NULL) = 0;
 
     /**
+     * Prepare stream by preallocating buffers for it asynchronously.
+     * Calls notifyPrepared() once allocation is complete.
+     */
+    virtual status_t prepare(int streamId) = 0;
+
+    /**
      * Get the HAL device version.
      */
     virtual uint32_t getDeviceVersion() = 0;
diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp
index e5b12ae..c662853 100644
--- a/services/camera/libcameraservice/common/CameraModule.cpp
+++ b/services/camera/libcameraservice/common/CameraModule.cpp
@@ -31,18 +31,65 @@
 
     // Keys added in HAL3.3
     if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) {
+        const size_t NUM_DERIVED_KEYS_HAL3_3 = 3;
         Vector<uint8_t> controlModes;
         uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;
         chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1);
         data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE;
         chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1);
-        controlModes.push(ANDROID_CONTROL_MODE_OFF);
         controlModes.push(ANDROID_CONTROL_MODE_AUTO);
         camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
         if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) {
             controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE);
         }
+
+        // Only advertise CONTROL_OFF mode if 3A manual controls are supported.
+        bool isManualAeSupported = false;
+        bool isManualAfSupported = false;
+        bool isManualAwbSupported = false;
+        entry = chars.find(ANDROID_CONTROL_AE_AVAILABLE_MODES);
+        if (entry.count > 0) {
+            for (size_t i = 0; i < entry.count; i++) {
+                if (entry.data.u8[i] == ANDROID_CONTROL_AE_MODE_OFF) {
+                    isManualAeSupported = true;
+                    break;
+                }
+            }
+        }
+        entry = chars.find(ANDROID_CONTROL_AF_AVAILABLE_MODES);
+        if (entry.count > 0) {
+            for (size_t i = 0; i < entry.count; i++) {
+                if (entry.data.u8[i] == ANDROID_CONTROL_AF_MODE_OFF) {
+                    isManualAfSupported = true;
+                    break;
+                }
+            }
+        }
+        entry = chars.find(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
+        if (entry.count > 0) {
+            for (size_t i = 0; i < entry.count; i++) {
+                if (entry.data.u8[i] == ANDROID_CONTROL_AWB_MODE_OFF) {
+                    isManualAwbSupported = true;
+                    break;
+                }
+            }
+        }
+        if (isManualAeSupported && isManualAfSupported && isManualAwbSupported) {
+            controlModes.push(ANDROID_CONTROL_MODE_OFF);
+        }
+
         chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes);
+
+        entry = chars.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+        Vector<int32_t> availableCharsKeys;
+        availableCharsKeys.setCapacity(entry.count + NUM_DERIVED_KEYS_HAL3_3);
+        for (size_t i = 0; i < entry.count; i++) {
+            availableCharsKeys.push(entry.data.i32[i]);
+        }
+        availableCharsKeys.push(ANDROID_CONTROL_AE_LOCK_AVAILABLE);
+        availableCharsKeys.push(ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE);
+        availableCharsKeys.push(ANDROID_CONTROL_AVAILABLE_MODES);
+        chars.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, availableCharsKeys);
     }
     return;
 }
@@ -57,6 +104,14 @@
     mCameraInfoMap.setCapacity(getNumberOfCameras());
 }
 
+int CameraModule::init() {
+    if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
+            mModule->init != NULL) {
+        return mModule->init();
+    }
+    return OK;
+}
+
 int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
     Mutex::Autolock lock(mCameraInfoLock);
     if (cameraId < 0) {
@@ -78,6 +133,12 @@
         if (ret != 0) {
             return ret;
         }
+        int deviceVersion = rawInfo.device_version;
+        if (deviceVersion < CAMERA_DEVICE_API_VERSION_2_0) {
+            // static_camera_characteristics is invalid
+            *info = rawInfo;
+            return ret;
+        }
         CameraMetadata m;
         m = rawInfo.static_camera_characteristics;
         deriveCameraCharacteristicsKeys(rawInfo.device_version, m);
@@ -92,7 +153,7 @@
     assert(index != NAME_NOT_FOUND);
     // return the cached camera info
     *info = mCameraInfoMap[index];
-    return 0;
+    return OK;
 }
 
 int CameraModule::open(const char* id, struct hw_device_t** device) {
@@ -160,4 +221,3 @@
 }
 
 }; // namespace android
-
diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h
index e285b21..c21092e 100644
--- a/services/camera/libcameraservice/common/CameraModule.h
+++ b/services/camera/libcameraservice/common/CameraModule.h
@@ -34,6 +34,10 @@
 public:
     CameraModule(camera_module_t *module);
 
+    // Must be called after construction
+    // Returns OK on success, NO_INIT on failure
+    int init();
+
     int getCameraInfo(int cameraId, struct camera_info *info);
     int getNumberOfCameras(void);
     int open(const char* id, struct hw_device_t** device);
@@ -63,4 +67,3 @@
 } // namespace android
 
 #endif
-
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index 878986b..f6645f3 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -618,6 +618,12 @@
     return waitUntilDrained();
 }
 
+status_t Camera2Device::prepare(int streamId) {
+    ATRACE_CALL();
+    ALOGE("%s: Camera %d: unimplemented", __FUNCTION__, mId);
+    return NO_INIT;
+}
+
 uint32_t Camera2Device::getDeviceVersion() {
     ATRACE_CALL();
     return mDeviceVersion;
@@ -1581,4 +1587,18 @@
     return OK;
 }
 
+// camera 2 devices don't support reprocessing
+status_t Camera2Device::createInputStream(
+    uint32_t width, uint32_t height, int format, int *id) {
+    ALOGE("%s: camera 2 devices don't support reprocessing", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+// camera 2 devices don't support reprocessing
+status_t Camera2Device::getInputBufferProducer(
+        sp<IGraphicBufferProducer> *producer) {
+    ALOGE("%s: camera 2 devices don't support reprocessing", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index 9b32fa6..fd1240a 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -59,6 +59,8 @@
     virtual status_t createStream(sp<ANativeWindow> consumer,
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
+    virtual status_t createInputStream(
+            uint32_t width, uint32_t height, int format, int *id);
     virtual status_t createReprocessStreamFromStream(int outputId, int *id);
     virtual status_t getStreamInfo(int id,
             uint32_t *width, uint32_t *height, uint32_t *format);
@@ -67,6 +69,8 @@
     virtual status_t deleteReprocessStream(int id);
     // No-op on HAL2 devices
     virtual status_t configureStreams();
+    virtual status_t getInputBufferProducer(
+            sp<IGraphicBufferProducer> *producer);
     virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
     virtual status_t waitUntilDrained();
     virtual status_t setNotifyCallback(NotificationListener *listener);
@@ -80,6 +84,9 @@
             buffer_handle_t *buffer, wp<BufferReleasedListener> listener);
     // Flush implemented as just a wait
     virtual status_t flush(int64_t *lastFrameNumber = NULL);
+    // Prepare is a no-op
+    virtual status_t prepare(int streamId);
+
     virtual uint32_t getDeviceVersion();
     virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 8236788..445c9c2 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -62,6 +62,7 @@
         mUsePartialResult(false),
         mNumPartialResults(1),
         mNextResultFrameNumber(0),
+        mNextReprocessResultFrameNumber(0),
         mNextShutterFrameNumber(0),
         mListener(NULL)
 {
@@ -174,6 +175,8 @@
         return res;
     }
 
+    mPreparerThread = new PreparerThread();
+
     /** Everything is good to go */
 
     mDeviceVersion = device->common.version;
@@ -201,6 +204,17 @@
         }
     }
 
+    camera_metadata_entry configs =
+            mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+    for (uint32_t i = 0; i < configs.count; i += 4) {
+        if (configs.data.i32[i] == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+                configs.data.i32[i + 3] ==
+                ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
+            mSupportedOpaqueInputSizes.add(Size(configs.data.i32[i + 1],
+                    configs.data.i32[i + 2]));
+        }
+    }
+
     return OK;
 }
 
@@ -1019,6 +1033,20 @@
     return configureStreamsLocked();
 }
 
+status_t Camera3Device::getInputBufferProducer(
+        sp<IGraphicBufferProducer> *producer) {
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    if (producer == NULL) {
+        return BAD_VALUE;
+    } else if (mInputStream == NULL) {
+        return INVALID_OPERATION;
+    }
+
+    return mInputStream->getInputBufferProducer(producer);
+}
+
 status_t Camera3Device::createDefaultRequest(int templateId,
         CameraMetadata *request) {
     ATRACE_CALL();
@@ -1054,9 +1082,9 @@
         mHal3Device, templateId);
     ATRACE_END();
     if (rawRequest == NULL) {
-        SET_ERR_L("HAL is unable to construct default settings for template %d",
-                templateId);
-        return DEAD_OBJECT;
+        ALOGI("%s: template %d is not supported on this camera device",
+              __FUNCTION__, templateId);
+        return BAD_VALUE;
     }
     *request = rawRequest;
     mRequestTemplateCache[templateId] = rawRequest;
@@ -1164,7 +1192,8 @@
         ALOGW("%s: Replacing old callback listener", __FUNCTION__);
     }
     mListener = listener;
-    mRequestThread->setNotifyCallback(listener);
+    mRequestThread->setNotificationListener(listener);
+    mPreparerThread->setNotificationListener(listener);
 
     return OK;
 }
@@ -1310,6 +1339,34 @@
     return res;
 }
 
+status_t Camera3Device::prepare(int streamId) {
+    ATRACE_CALL();
+    ALOGV("%s: Camera %d: Preparing stream %d", __FUNCTION__, mId, streamId);
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    sp<Camera3StreamInterface> stream;
+    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
+    if (outputStreamIdx == NAME_NOT_FOUND) {
+        CLOGE("Stream %d does not exist", streamId);
+        return BAD_VALUE;
+    }
+
+    stream = mOutputStreams.editValueAt(outputStreamIdx);
+
+    if (stream->isUnpreparable() || stream->hasOutstandingBuffers() ) {
+        CLOGE("Stream %d has already been a request target", streamId);
+        return BAD_VALUE;
+    }
+
+    if (mRequestThread->isStreamPending(stream)) {
+        CLOGE("Stream %d is already a target in a pending request", streamId);
+        return BAD_VALUE;
+    }
+
+    return mPreparerThread->prepare(stream);
+}
+
 uint32_t Camera3Device::getDeviceVersion() {
     ATRACE_CALL();
     Mutex::Autolock il(mInterfaceLock);
@@ -1383,6 +1440,11 @@
                 return NULL;
             }
         }
+        // Check if stream is being prepared
+        if (mInputStream->isPreparing()) {
+            CLOGE("Request references an input stream that's being prepared!");
+            return NULL;
+        }
 
         newRequest->mInputStream = mInputStream;
         newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS);
@@ -1415,6 +1477,11 @@
                 return NULL;
             }
         }
+        // Check if stream is being prepared
+        if (stream->isPreparing()) {
+            CLOGE("Request references an output stream that's being prepared!");
+            return NULL;
+        }
 
         newRequest->mOutputStreams.push(stream);
     }
@@ -1423,6 +1490,17 @@
     return newRequest;
 }
 
+bool Camera3Device::isOpaqueInputSizeSupported(uint32_t width, uint32_t height) {
+    for (uint32_t i = 0; i < mSupportedOpaqueInputSizes.size(); i++) {
+        Size size = mSupportedOpaqueInputSizes[i];
+        if (size.width == width && size.height == height) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 status_t Camera3Device::configureStreamsLocked() {
     ATRACE_CALL();
     status_t res;
@@ -1879,7 +1957,6 @@
     return true;
 }
 
-
 void Camera3Device::returnOutputBuffers(
         const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
         nsecs_t timestamp) {
@@ -1947,20 +2024,31 @@
 void Camera3Device::sendCaptureResult(CameraMetadata &pendingMetadata,
         CaptureResultExtras &resultExtras,
         CameraMetadata &collectedPartialResult,
-        uint32_t frameNumber) {
+        uint32_t frameNumber,
+        bool reprocess) {
     if (pendingMetadata.isEmpty())
         return;
 
     Mutex::Autolock l(mOutputLock);
 
     // TODO: need to track errors for tighter bounds on expected frame number
-    if (frameNumber < mNextResultFrameNumber) {
-        SET_ERR("Out-of-order capture result metadata submitted! "
+    if (reprocess) {
+        if (frameNumber < mNextReprocessResultFrameNumber) {
+            SET_ERR("Out-of-order reprocess capture result metadata submitted! "
                 "(got frame number %d, expecting %d)",
-                frameNumber, mNextResultFrameNumber);
-        return;
+                frameNumber, mNextReprocessResultFrameNumber);
+            return;
+        }
+        mNextReprocessResultFrameNumber = frameNumber + 1;
+    } else {
+        if (frameNumber < mNextResultFrameNumber) {
+            SET_ERR("Out-of-order capture result metadata submitted! "
+                    "(got frame number %d, expecting %d)",
+                    frameNumber, mNextResultFrameNumber);
+            return;
+        }
+        mNextResultFrameNumber = frameNumber + 1;
     }
-    mNextResultFrameNumber = frameNumber + 1;
 
     CaptureResult captureResult;
     captureResult.mResultExtras = resultExtras;
@@ -2170,7 +2258,7 @@
                 CameraMetadata metadata;
                 metadata = result->result;
                 sendCaptureResult(metadata, request.resultExtras,
-                    collectedPartialResult, frameNumber);
+                    collectedPartialResult, frameNumber, hasInputBufferInRequest);
             }
         }
 
@@ -2332,7 +2420,8 @@
 
             // send pending result and buffers
             sendCaptureResult(r.pendingMetadata, r.resultExtras,
-                r.partialResult.collectedResult, msg.frame_number);
+                r.partialResult.collectedResult, msg.frame_number,
+                r.hasInputBuffer);
             returnOutputBuffers(r.pendingOutputBuffers.array(),
                 r.pendingOutputBuffers.size(), r.shutterTimestamp);
             r.pendingOutputBuffers.clear();
@@ -2367,7 +2456,7 @@
 Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
         sp<StatusTracker> statusTracker,
         camera3_device_t *hal3Device) :
-        Thread(false),
+        Thread(/*canCallJava*/false),
         mParent(parent),
         mStatusTracker(statusTracker),
         mHal3Device(hal3Device),
@@ -2383,7 +2472,7 @@
     mStatusId = statusTracker->addComponent();
 }
 
-void Camera3Device::RequestThread::setNotifyCallback(
+void Camera3Device::RequestThread::setNotificationListener(
         NotificationListener *listener) {
     Mutex::Autolock l(mRequestLock);
     mListener = listener;
@@ -2669,7 +2758,6 @@
     // Fill in buffers
 
     if (nextRequest->mInputStream != NULL) {
-        request.input_buffer = &inputBuffer;
         res = nextRequest->mInputStream->getInputBuffer(&inputBuffer);
         if (res != OK) {
             // Can't get input buffer from gralloc queue - this could be due to
@@ -2686,6 +2774,7 @@
             cleanUpFailedRequest(request, nextRequest, outputBuffers);
             return true;
         }
+        request.input_buffer = &inputBuffer;
         totalNumBuffers += 1;
     } else {
         request.input_buffer = NULL;
@@ -2797,6 +2886,26 @@
     return mLatestRequest;
 }
 
+bool Camera3Device::RequestThread::isStreamPending(
+        sp<Camera3StreamInterface>& stream) {
+    Mutex::Autolock l(mRequestLock);
+
+    for (const auto& request : mRequestQueue) {
+        for (const auto& s : request->mOutputStreams) {
+            if (stream == s) return true;
+        }
+        if (stream == request->mInputStream) return true;
+    }
+
+    for (const auto& request : mRepeatingRequests) {
+        for (const auto& s : request->mOutputStreams) {
+            if (stream == s) return true;
+        }
+        if (stream == request->mInputStream) return true;
+    }
+
+    return false;
+}
 
 void Camera3Device::RequestThread::cleanUpFailedRequest(
         camera3_capture_request_t &request,
@@ -3144,6 +3253,138 @@
     return OK;
 }
 
+/**
+ * PreparerThread inner class methods
+ */
+
+Camera3Device::PreparerThread::PreparerThread() :
+        Thread(/*canCallJava*/false), mActive(false), mCancelNow(false) {
+}
+
+Camera3Device::PreparerThread::~PreparerThread() {
+    Thread::requestExitAndWait();
+    if (mCurrentStream != nullptr) {
+        mCurrentStream->cancelPrepare();
+        ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
+        mCurrentStream.clear();
+    }
+    clear();
+}
+
+status_t Camera3Device::PreparerThread::prepare(sp<Camera3StreamInterface>& stream) {
+    status_t res;
+
+    Mutex::Autolock l(mLock);
+
+    res = stream->startPrepare();
+    if (res == OK) {
+        // No preparation needed, fire listener right off
+        ALOGV("%s: Stream %d already prepared", __FUNCTION__, stream->getId());
+        if (mListener) {
+            mListener->notifyPrepared(stream->getId());
+        }
+        return OK;
+    } else if (res != NOT_ENOUGH_DATA) {
+        return res;
+    }
+
+    // Need to prepare, start up thread if necessary
+    if (!mActive) {
+        // mRunning will change to false before the thread fully shuts down, so wait to be sure it
+        // isn't running
+        Thread::requestExitAndWait();
+        res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
+        if (res != OK) {
+            ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__, res, strerror(-res));
+            if (mListener) {
+                mListener->notifyPrepared(stream->getId());
+            }
+            return res;
+        }
+        mCancelNow = false;
+        mActive = true;
+        ALOGV("%s: Preparer stream started", __FUNCTION__);
+    }
+
+    // queue up the work
+    mPendingStreams.push_back(stream);
+    ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId());
+
+    return OK;
+}
+
+status_t Camera3Device::PreparerThread::clear() {
+    status_t res;
+
+    Mutex::Autolock l(mLock);
+
+    for (const auto& stream : mPendingStreams) {
+        stream->cancelPrepare();
+    }
+    mPendingStreams.clear();
+    mCancelNow = true;
+
+    return OK;
+}
+
+void Camera3Device::PreparerThread::setNotificationListener(NotificationListener *listener) {
+    Mutex::Autolock l(mLock);
+    mListener = listener;
+}
+
+bool Camera3Device::PreparerThread::threadLoop() {
+    status_t res;
+    {
+        Mutex::Autolock l(mLock);
+        if (mCurrentStream == nullptr) {
+            // End thread if done with work
+            if (mPendingStreams.empty()) {
+                ALOGV("%s: Preparer stream out of work", __FUNCTION__);
+                // threadLoop _must not_ re-acquire mLock after it sets mActive to false; would
+                // cause deadlock with prepare()'s requestExitAndWait triggered by !mActive.
+                mActive = false;
+                return false;
+            }
+
+            // Get next stream to prepare
+            auto it = mPendingStreams.begin();
+            mCurrentStream = *it;
+            mPendingStreams.erase(it);
+            ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId());
+            ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId());
+        } else if (mCancelNow) {
+            mCurrentStream->cancelPrepare();
+            ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
+            ALOGV("%s: Cancelling stream %d prepare", __FUNCTION__, mCurrentStream->getId());
+            mCurrentStream.clear();
+            mCancelNow = false;
+            return true;
+        }
+    }
+
+    res = mCurrentStream->prepareNextBuffer();
+    if (res == NOT_ENOUGH_DATA) return true;
+    if (res != OK) {
+        // Something bad happened; try to recover by cancelling prepare and
+        // signalling listener anyway
+        ALOGE("%s: Stream %d returned error %d (%s) during prepare", __FUNCTION__,
+                mCurrentStream->getId(), res, strerror(-res));
+        mCurrentStream->cancelPrepare();
+    }
+
+    // This stream has finished, notify listener
+    Mutex::Autolock l(mLock);
+    if (mListener) {
+        ALOGV("%s: Stream %d prepare done, signaling listener", __FUNCTION__,
+                mCurrentStream->getId());
+        mListener->notifyPrepared(mCurrentStream->getId());
+    }
+
+    ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
+    mCurrentStream.clear();
+
+    return true;
+}
 
 /**
  * Static callback forwarding methods from HAL to instance
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index a77548d..4fbcb2e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -116,6 +116,8 @@
     virtual status_t deleteReprocessStream(int id);
 
     virtual status_t configureStreams();
+    virtual status_t getInputBufferProducer(
+            sp<IGraphicBufferProducer> *producer);
 
     virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
 
@@ -136,6 +138,8 @@
 
     virtual status_t flush(int64_t *lastFrameNumber = NULL);
 
+    virtual status_t prepare(int streamId);
+
     virtual uint32_t getDeviceVersion();
 
     virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
@@ -179,6 +183,14 @@
 
     uint32_t                   mDeviceVersion;
 
+    struct Size {
+        uint32_t width;
+        uint32_t height;
+        Size(uint32_t w = 0, uint32_t h = 0) : width(w), height(h){}
+    };
+    // Map from format to size.
+    Vector<Size>               mSupportedOpaqueInputSizes;
+
     enum Status {
         STATUS_ERROR,
         STATUS_UNINITIALIZED,
@@ -324,11 +336,11 @@
      */
     bool               tryLockSpinRightRound(Mutex& lock);
 
-    struct Size {
-        int width;
-        int height;
-        Size(int w, int h) : width(w), height(h){}
-    };
+    /**
+     * Helper function to determine if an input size for implementation defined
+     * format is supported.
+     */
+    bool isOpaqueInputSizeSupported(uint32_t width, uint32_t height);
 
     /**
      * Helper function to get the largest Jpeg resolution (in area)
@@ -364,7 +376,7 @@
                 sp<camera3::StatusTracker> statusTracker,
                 camera3_device_t *hal3Device);
 
-        void     setNotifyCallback(NotificationListener *listener);
+        void     setNotificationListener(NotificationListener *listener);
 
         /**
          * Call after stream (re)-configuration is completed.
@@ -428,6 +440,12 @@
          */
         CameraMetadata getLatestRequest() const;
 
+        /**
+         * Returns true if the stream is a target of any queued or repeating
+         * capture request
+         */
+        bool isStreamPending(sp<camera3::Camera3StreamInterface>& stream);
+
       protected:
 
         virtual bool threadLoop();
@@ -549,7 +567,6 @@
         Vector<camera3_stream_buffer_t> pendingOutputBuffers;
 
 
-
         // Fields used by the partial result only
         struct PartialResultInFlight {
             // Set by process_capture_result once 3A has been sent to clients
@@ -600,7 +617,8 @@
                 resultExtras(extras),
                 hasInputBuffer(hasInput){
         }
-};
+    };
+
     // Map from frame number to the in-flight request state
     typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
 
@@ -632,6 +650,45 @@
     sp<camera3::StatusTracker> mStatusTracker;
 
     /**
+     * Thread for preparing streams
+     */
+    class PreparerThread : private Thread, public virtual RefBase {
+      public:
+        PreparerThread();
+        ~PreparerThread();
+
+        void setNotificationListener(NotificationListener *listener);
+
+        /**
+         * Queue up a stream to be prepared. Streams are processed by
+         * a background thread in FIFO order
+         */
+        status_t prepare(sp<camera3::Camera3StreamInterface>& stream);
+
+        /**
+         * Cancel all current and pending stream preparation
+         */
+        status_t clear();
+
+      private:
+        Mutex mLock;
+
+        virtual bool threadLoop();
+
+        // Guarded by mLock
+
+        NotificationListener *mListener;
+        List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
+        bool mActive;
+        bool mCancelNow;
+
+        // Only accessed by threadLoop and the destructor
+
+        sp<camera3::Camera3StreamInterface> mCurrentStream;
+    };
+    sp<PreparerThread> mPreparerThread;
+
+    /**
      * Output result queue and current HAL device 3A state
      */
 
@@ -639,8 +696,10 @@
     Mutex                  mOutputLock;
 
     /**** Scope for mOutputLock ****/
-
+    // the minimal frame number of the next non-reprocess result
     uint32_t               mNextResultFrameNumber;
+    // the minimal frame number of the next reprocess result
+    uint32_t               mNextReprocessResultFrameNumber;
     uint32_t               mNextShutterFrameNumber;
     List<CaptureResult>   mResultQueue;
     Condition              mResultSignal;
@@ -669,7 +728,8 @@
     // partial results, and the frame number to the result queue.
     void sendCaptureResult(CameraMetadata &pendingMetadata,
             CaptureResultExtras &resultExtras,
-            CameraMetadata &collectedPartialResult, uint32_t frameNumber);
+            CameraMetadata &collectedPartialResult, uint32_t frameNumber,
+            bool reprocess);
 
     /**** Scope for mInFlightLock ****/
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 01edfff..ecb8ac8 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -87,7 +87,7 @@
     return OK;
 }
 
-status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) const {
     *usage = DUMMY_USAGE;
     return OK;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index d023c57..3a3dbf4 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -89,7 +89,7 @@
 
     virtual status_t configureQueueLocked();
 
-    virtual status_t getEndpointUsage(uint32_t *usage);
+    virtual status_t getEndpointUsage(uint32_t *usage) const;
 
 }; // class Camera3DummyStream
 
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 8696413..23b1c45 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -67,13 +67,18 @@
 void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const {
     (void) args;
     String8 lines;
+
+    uint32_t consumerUsage = 0;
+    status_t res = getEndpointUsage(&consumerUsage);
+    if (res != OK) consumerUsage = 0;
+
     lines.appendFormat("      State: %d\n", mState);
-    lines.appendFormat("      Dims: %d x %d, format 0x%x\n",
+    lines.appendFormat("      Dims: %d x %d, format 0x%x, dataspace 0x%x\n",
             camera3_stream::width, camera3_stream::height,
-            camera3_stream::format);
+            camera3_stream::format, camera3_stream::data_space);
     lines.appendFormat("      Max size: %zu\n", mMaxSize);
-    lines.appendFormat("      Usage: %d, max HAL buffers: %d\n",
-            camera3_stream::usage, camera3_stream::max_buffers);
+    lines.appendFormat("      Combined usage: %d, max HAL buffers: %d\n",
+            camera3_stream::usage | consumerUsage, camera3_stream::max_buffers);
     lines.appendFormat("      Frames produced: %d, last timestamp: %" PRId64 " ns\n",
             mFrameCount, mLastTimestamp);
     lines.appendFormat("      Total buffers: %zu, currently dequeued: %zu\n",
@@ -156,13 +161,11 @@
 
     // Inform tracker about becoming busy
     if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG &&
-            mState != STATE_IN_RECONFIG) {
+            mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) {
         /**
          * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers
          * before/after register_stream_buffers during initial configuration
-         * or re-configuration.
-         *
-         * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2
+         * or re-configuration, or during prepare pre-allocation
          */
         sp<StatusTracker> statusTracker = mStatusTracker.promote();
         if (statusTracker != 0) {
@@ -177,9 +180,11 @@
 }
 
 status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const {
-    // Allow dequeue during IN_[RE]CONFIG for registration
+    // Allow dequeue during IN_[RE]CONFIG for registration, in
+    // PREPARING for pre-allocation
     if (mState != STATE_CONFIGURED &&
-            mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) {
+            mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG &&
+            mState != STATE_PREPARING) {
         ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d",
                 __FUNCTION__, mId, mState);
         return INVALID_OPERATION;
@@ -240,13 +245,11 @@
 
     mHandoutTotalBufferCount--;
     if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG &&
-            mState != STATE_IN_RECONFIG) {
+            mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) {
         /**
          * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers
          * before/after register_stream_buffers during initial configuration
-         * or re-configuration.
-         *
-         * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2
+         * or re-configuration, or during prepare pre-allocation
          */
         ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__,
                 mId);
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index abcf2b1..f5727e8 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -84,7 +84,7 @@
 
     virtual size_t   getHandoutInputBufferCountLocked();
 
-    virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+    virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
 
     status_t getBufferPreconditionCheckLocked() const;
     status_t returnBufferPreconditionCheckLocked() const;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 6bf671e..2504bfd 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -65,8 +65,8 @@
     assert(mConsumer != 0);
 
     BufferItem bufferItem;
-    res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
 
+    res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
     if (res != OK) {
         ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
                 __FUNCTION__, mId, strerror(-res), res);
@@ -162,6 +162,21 @@
     return returnAnyBufferLocked(buffer, /*timestamp*/0, /*output*/false);
 }
 
+status_t Camera3InputStream::getInputBufferProducerLocked(
+            sp<IGraphicBufferProducer> *producer) {
+    ATRACE_CALL();
+
+    if (producer == NULL) {
+        return BAD_VALUE;
+    } else if (mProducer == NULL) {
+        ALOGE("%s: No input stream is configured");
+        return INVALID_OPERATION;
+    }
+
+    *producer = mProducer;
+    return OK;
+}
+
 status_t Camera3InputStream::disconnectLocked() {
 
     status_t res;
@@ -172,6 +187,8 @@
 
     assert(mBuffersInFlight.size() == 0);
 
+    mConsumer->abandon();
+
     /**
      *  no-op since we can't disconnect the producer from the consumer-side
      */
@@ -212,10 +229,17 @@
         res = producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
         if (res != OK || minUndequeuedBuffers < 0) {
             ALOGE("%s: Stream %d: Could not query min undequeued buffers (error %d, bufCount %d)",
-                  __FUNCTION__, mId, res, minUndequeuedBuffers);
+                    __FUNCTION__, mId, res, minUndequeuedBuffers);
             return res;
         }
         size_t minBufs = static_cast<size_t>(minUndequeuedBuffers);
+
+        if (camera3_stream::max_buffers == 0) {
+            ALOGE("%s: %d: HAL sets max_buffer to 0. Must be at least 1.",
+                    __FUNCTION__, __LINE__);
+            return INVALID_OPERATION;
+        }
+
         /*
          * We promise never to 'acquire' more than camera3_stream::max_buffers
          * at any one time.
@@ -232,6 +256,8 @@
         mConsumer = new BufferItemConsumer(consumer, camera3_stream::usage,
                                            mTotalBufferCount);
         mConsumer->setName(String8::format("Camera3-InputStream-%d", mId));
+
+        mProducer = producer;
     }
 
     res = mConsumer->setDefaultBufferSize(camera3_stream::width,
@@ -251,7 +277,7 @@
     return OK;
 }
 
-status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) const {
     // Per HAL3 spec, input streams have 0 for their initial usage field.
     *usage = 0;
     return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index fd17f4f..9f3de10 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -49,6 +49,7 @@
   private:
 
     sp<BufferItemConsumer> mConsumer;
+    sp<IGraphicBufferProducer> mProducer;
     Vector<BufferItem> mBuffersInFlight;
 
     /**
@@ -68,11 +69,13 @@
     virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
     virtual status_t returnInputBufferLocked(
             const camera3_stream_buffer &buffer);
+    virtual status_t getInputBufferProducerLocked(
+            sp<IGraphicBufferProducer> *producer);
     virtual status_t disconnectLocked();
 
     virtual status_t configureQueueLocked();
 
-    virtual status_t getEndpointUsage(uint32_t *usage);
+    virtual status_t getEndpointUsage(uint32_t *usage) const;
 
 }; // class Camera3InputStream
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 0c739e9..7a0331b 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -209,6 +209,13 @@
         }
     }
     mLock.lock();
+
+    // Once a valid buffer has been returned to the queue, can no longer
+    // dequeue all buffers for preallocation.
+    if (buffer.status != CAMERA3_BUFFER_STATUS_ERROR) {
+        mStreamUnpreparable = true;
+    }
+
     if (res != OK) {
         close(anwReleaseFence);
     }
@@ -390,14 +397,28 @@
     return OK;
 }
 
-status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) {
+status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) const {
 
     status_t res;
     int32_t u = 0;
     res = mConsumer->query(mConsumer.get(),
             NATIVE_WINDOW_CONSUMER_USAGE_BITS, &u);
-    *usage = u;
 
+    // If an opaque output stream's endpoint is ImageReader, add
+    // GRALLOC_USAGE_HW_CAMERA_ZSL to the usage so HAL knows it will be used
+    // for the ZSL use case.
+    // Assume it's for ImageReader if the consumer usage doesn't have any of these bits set:
+    //     1. GRALLOC_USAGE_HW_TEXTURE
+    //     2. GRALLOC_USAGE_HW_RENDER
+    //     3. GRALLOC_USAGE_HW_COMPOSER
+    //     4. GRALLOC_USAGE_HW_VIDEO_ENCODER
+    if (camera3_stream::format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+            (u & (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER |
+            GRALLOC_USAGE_HW_VIDEO_ENCODER)) == 0) {
+        u |= GRALLOC_USAGE_HW_CAMERA_ZSL;
+    }
+
+    *usage = u;
     return res;
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 12b2ebb..513b695 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -99,7 +99,7 @@
 
     virtual status_t configureQueueLocked();
 
-    virtual status_t getEndpointUsage(uint32_t *usage);
+    virtual status_t getEndpointUsage(uint32_t *usage) const;
 
 }; // class Camera3OutputStream
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 4acbce3..4c40bb6 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -109,11 +109,7 @@
             // oldUsage/oldMaxBuffers
             return this;
         case STATE_CONFIGURED:
-            if (stream_type == CAMERA3_STREAM_INPUT) {
-                ALOGE("%s: Cannot configure an input stream twice",
-                        __FUNCTION__);
-                return NULL;
-            } else if (hasOutstandingBuffersLocked()) {
+            if (hasOutstandingBuffersLocked()) {
                 ALOGE("%s: Cannot configure stream; has outstanding buffers",
                         __FUNCTION__);
                 return NULL;
@@ -194,6 +190,11 @@
         return OK;
     }
 
+    // Reset prepared state, since buffer config has changed, and existing
+    // allocations are no longer valid
+    mPrepared = false;
+    mStreamUnpreparable = false;
+
     status_t res;
     res = configureQueueLocked();
     if (res != OK) {
@@ -244,6 +245,125 @@
     return OK;
 }
 
+bool Camera3Stream::isUnpreparable() {
+    ATRACE_CALL();
+
+    Mutex::Autolock l(mLock);
+    return mStreamUnpreparable;
+}
+
+status_t Camera3Stream::startPrepare() {
+    ATRACE_CALL();
+
+    Mutex::Autolock l(mLock);
+    status_t res = OK;
+
+    // This function should be only called when the stream is configured already.
+    if (mState != STATE_CONFIGURED) {
+        ALOGE("%s: Stream %d: Can't prepare stream if stream is not in CONFIGURED "
+                "state %d", __FUNCTION__, mId, mState);
+        return INVALID_OPERATION;
+    }
+
+    // This function can't be called if the stream has already received filled
+    // buffers
+    if (mStreamUnpreparable) {
+        ALOGE("%s: Stream %d: Can't prepare stream that's already in use",
+                __FUNCTION__, mId);
+        return INVALID_OPERATION;
+    }
+
+    if (getHandoutOutputBufferCountLocked() > 0) {
+        ALOGE("%s: Stream %d: Can't prepare stream that has outstanding buffers",
+                __FUNCTION__, mId);
+        return INVALID_OPERATION;
+    }
+
+    if (mPrepared) return OK;
+
+    size_t bufferCount = getBufferCountLocked();
+
+    mPreparedBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount);
+    mPreparedBufferIdx = 0;
+
+    mState = STATE_PREPARING;
+
+    return NOT_ENOUGH_DATA;
+}
+
+bool Camera3Stream::isPreparing() const {
+    Mutex::Autolock l(mLock);
+    return mState == STATE_PREPARING;
+}
+
+status_t Camera3Stream::prepareNextBuffer() {
+    ATRACE_CALL();
+
+    Mutex::Autolock l(mLock);
+    status_t res = OK;
+
+    // This function should be only called when the stream is preparing
+    if (mState != STATE_PREPARING) {
+        ALOGE("%s: Stream %d: Can't prepare buffer if stream is not in PREPARING "
+                "state %d", __FUNCTION__, mId, mState);
+        return INVALID_OPERATION;
+    }
+
+    // Get next buffer - this may allocate, and take a while for large buffers
+    res = getBufferLocked( &mPreparedBuffers.editItemAt(mPreparedBufferIdx) );
+    if (res != OK) {
+        ALOGE("%s: Stream %d: Unable to allocate buffer %d during preparation",
+                __FUNCTION__, mId, mPreparedBufferIdx);
+        return NO_INIT;
+    }
+
+    mPreparedBufferIdx++;
+
+    // Check if we still have buffers left to allocate
+    if (mPreparedBufferIdx < mPreparedBuffers.size()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    // Done with prepare - mark stream as such, and return all buffers
+    // via cancelPrepare
+    mPrepared = true;
+
+    return cancelPrepareLocked();
+}
+
+status_t Camera3Stream::cancelPrepare() {
+    ATRACE_CALL();
+
+    Mutex::Autolock l(mLock);
+
+    return cancelPrepareLocked();
+}
+
+status_t Camera3Stream::cancelPrepareLocked() {
+    status_t res = OK;
+
+    // This function should be only called when the stream is mid-preparing.
+    if (mState != STATE_PREPARING) {
+        ALOGE("%s: Stream %d: Can't cancel prepare stream if stream is not in "
+                "PREPARING state %d", __FUNCTION__, mId, mState);
+        return INVALID_OPERATION;
+    }
+
+    // Return all valid buffers to stream, in ERROR state to indicate
+    // they weren't filled.
+    for (size_t i = 0; i < mPreparedBufferIdx; i++) {
+        mPreparedBuffers.editItemAt(i).release_fence = -1;
+        mPreparedBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
+        returnBufferLocked(mPreparedBuffers[i], 0);
+    }
+    mPreparedBuffers.clear();
+    mPreparedBufferIdx = 0;
+
+    mState = STATE_CONFIGURED;
+
+    return res;
+}
+
 status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
@@ -346,6 +466,13 @@
     return res;
 }
 
+status_t Camera3Stream::getInputBufferProducer(sp<IGraphicBufferProducer> *producer) {
+    ATRACE_CALL();
+    Mutex::Autolock l(mLock);
+
+    return getInputBufferProducerLocked(producer);
+}
+
 void Camera3Stream::fireBufferListenersLocked(
         const camera3_stream_buffer& /*buffer*/, bool acquired, bool output) {
     List<wp<Camera3StreamBufferListener> >::iterator it, end;
@@ -420,15 +547,13 @@
             ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "
                     "must be set to NULL in camera3_device::ops", __FUNCTION__);
             return INVALID_OPERATION;
-        } else {
-            ALOGD("%s: Skipping NULL check for deprecated register_stream_buffers", __FUNCTION__);
         }
 
         return OK;
-    } else {
-        ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);
     }
 
+    ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);
+
     status_t res;
 
     size_t bufferCount = getBufferCountLocked();
@@ -484,6 +609,8 @@
         returnBufferLocked(streamBuffers[i], 0);
     }
 
+    mPrepared = true;
+
     return res;
 }
 
@@ -505,6 +632,10 @@
     ALOGE("%s: This type of stream does not support input", __FUNCTION__);
     return INVALID_OPERATION;
 }
+status_t Camera3Stream::getInputBufferProducerLocked(sp<IGraphicBufferProducer> *producer) {
+    ALOGE("%s: This type of stream does not support input", __FUNCTION__);
+    return INVALID_OPERATION;
+}
 
 void Camera3Stream::addBufferListener(
         wp<Camera3StreamBufferListener> listener) {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index aba27fe..0543c66 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -57,8 +57,15 @@
  *    re-registering buffers with HAL.
  *
  *  STATE_CONFIGURED: Stream is configured, and has registered buffers with the
- *    HAL. The stream's getBuffer/returnBuffer work. The priv pointer may still be
- *    modified.
+ *    HAL (if necessary). The stream's getBuffer/returnBuffer work. The priv
+ *    pointer may still be modified.
+ *
+ *  STATE_PREPARING: The stream's buffers are being pre-allocated for use.  On
+ *    older HALs, this is done as part of configuration, but in newer HALs
+ *    buffers may be allocated at time of first use. But some use cases require
+ *    buffer allocation upfront, to minmize disruption due to lengthy allocation
+ *    duration.  In this state, only prepareNextBuffer() and cancelPrepare()
+ *    may be called.
  *
  * Transition table:
  *
@@ -82,6 +89,12 @@
  *    STATE_CONFIGURED     => STATE_CONSTRUCTED:
  *        When disconnect() is called after making sure stream is idle with
  *        waitUntilIdle().
+ *    STATE_CONFIGURED     => STATE_PREPARING:
+ *        When startPrepare is called before the stream has a buffer
+ *        queued back into it for the first time.
+ *    STATE_PREPARING      => STATE_CONFIGURED:
+ *        When sufficient prepareNextBuffer calls have been made to allocate
+ *        all stream buffers, or cancelPrepare is called.
  *
  * Status Tracking:
  *    Each stream is tracked by StatusTracker as a separate component,
@@ -167,6 +180,73 @@
     status_t         cancelConfiguration();
 
     /**
+     * Determine whether the stream has already become in-use (has received
+     * a valid filled buffer), which determines if a stream can still have
+     * prepareNextBuffer called on it.
+     */
+    bool             isUnpreparable();
+
+    /**
+     * Start stream preparation. May only be called in the CONFIGURED state,
+     * when no valid buffers have yet been returned to this stream.
+     *
+     * If no prepartion is necessary, returns OK and does not transition to
+     * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions
+     * to PREPARING.
+     *
+     * This call performs no allocation, so is quick to call.
+     *
+     * Returns:
+     *    OK if no more buffers need to be preallocated
+     *    NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish
+     *        buffer pre-allocation, and transitions to the PREPARING state.
+     *    NO_INIT in case of a serious error from the HAL device
+     *    INVALID_OPERATION if called when not in CONFIGURED state, or a
+     *        valid buffer has already been returned to this stream.
+     */
+    status_t         startPrepare();
+
+    /**
+     * Check if the stream is mid-preparing.
+     */
+    bool             isPreparing() const;
+
+    /**
+     * Continue stream buffer preparation by allocating the next
+     * buffer for this stream.  May only be called in the PREPARED state.
+     *
+     * Returns OK and transitions to the CONFIGURED state if all buffers
+     * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA.
+     *
+     * This call allocates one buffer, which may take several milliseconds for
+     * large buffers.
+     *
+     * Returns:
+     *    OK if no more buffers need to be preallocated, and transitions
+     *        to the CONFIGURED state.
+     *    NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish
+     *        buffer pre-allocation.
+     *    NO_INIT in case of a serious error from the HAL device
+     *    INVALID_OPERATION if called when not in CONFIGURED state, or a
+     *        valid buffer has already been returned to this stream.
+     */
+    status_t         prepareNextBuffer();
+
+    /**
+     * Cancel stream preparation early. In case allocation needs to be
+     * stopped, this method transitions the stream back to the CONFIGURED state.
+     * Buffers that have been allocated with prepareNextBuffer remain that way,
+     * but a later use of prepareNextBuffer will require just as many
+     * calls as if the earlier prepare attempt had not existed.
+     *
+     * Returns:
+     *    OK if cancellation succeeded, and transitions to the CONFIGURED state
+     *    INVALID_OPERATION if not in the PREPARING state
+     *    NO_INIT in case of a serious error from the HAL device
+     */
+    status_t        cancelPrepare();
+
+    /**
      * Fill in the camera3_stream_buffer with the next valid buffer for this
      * stream, to hand over to the HAL.
      *
@@ -205,6 +285,10 @@
      */
     status_t         returnInputBuffer(const camera3_stream_buffer &buffer);
 
+    // get the buffer producer of the input buffer queue.
+    // only apply to input streams.
+    status_t         getInputBufferProducer(sp<IGraphicBufferProducer> *producer);
+
     /**
      * Whether any of the stream's buffers are currently in use by the HAL,
      * including buffers that have been returned but not yet had their
@@ -259,7 +343,8 @@
         STATE_CONSTRUCTED,
         STATE_IN_CONFIG,
         STATE_IN_RECONFIG,
-        STATE_CONFIGURED
+        STATE_CONFIGURED,
+        STATE_PREPARING
     } mState;
 
     mutable Mutex mLock;
@@ -285,6 +370,9 @@
     virtual status_t returnInputBufferLocked(
             const camera3_stream_buffer &buffer);
     virtual bool     hasOutstandingBuffersLocked() const = 0;
+    // Get the buffer producer of the input buffer queue. Only apply to input streams.
+    virtual status_t getInputBufferProducerLocked(sp<IGraphicBufferProducer> *producer);
+
     // Can return -ENOTCONN when we are already disconnected (not an error)
     virtual status_t disconnectLocked() = 0;
 
@@ -305,13 +393,17 @@
 
     // Get the usage flags for the other endpoint, or return
     // INVALID_OPERATION if they cannot be obtained.
-    virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+    virtual status_t getEndpointUsage(uint32_t *usage) const = 0;
 
     // Tracking for idle state
     wp<StatusTracker> mStatusTracker;
     // Status tracker component ID
     int mStatusId;
 
+    // Tracking for stream prepare - whether this stream can still have
+    // prepareNextBuffer called on it.
+    bool mStreamUnpreparable;
+
   private:
     uint32_t oldUsage;
     uint32_t oldMaxBuffers;
@@ -326,6 +418,18 @@
                                   bool acquired, bool output);
     List<wp<Camera3StreamBufferListener> > mBufferListenerList;
 
+    status_t        cancelPrepareLocked();
+
+    // Tracking for PREPARING state
+
+    // State of buffer preallocation. Only true if either prepareNextBuffer
+    // has been called sufficient number of times, or stream configuration
+    // had to register buffers with the HAL
+    bool mPrepared;
+
+    Vector<camera3_stream_buffer_t> mPreparedBuffers;
+    size_t mPreparedBufferIdx;
+
 }; // class Camera3Stream
 
 }; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index da989cd..d177b57 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -89,6 +89,68 @@
     virtual status_t cancelConfiguration() = 0;
 
     /**
+     * Determine whether the stream has already become in-use (has received
+     * a valid filled buffer), which determines if a stream can still have
+     * prepareNextBuffer called on it.
+     */
+    virtual bool     isUnpreparable() = 0;
+
+    /**
+     * Start stream preparation. May only be called in the CONFIGURED state,
+     * when no valid buffers have yet been returned to this stream.
+     *
+     * If no prepartion is necessary, returns OK and does not transition to
+     * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions
+     * to PREPARING.
+     *
+     * Returns:
+     *    OK if no more buffers need to be preallocated
+     *    NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish
+     *        buffer pre-allocation, and transitions to the PREPARING state.
+     *    NO_INIT in case of a serious error from the HAL device
+     *    INVALID_OPERATION if called when not in CONFIGURED state, or a
+     *        valid buffer has already been returned to this stream.
+     */
+    virtual status_t startPrepare() = 0;
+
+    /**
+     * Check if the stream is mid-preparing.
+     */
+    virtual bool     isPreparing() const = 0;
+
+    /**
+     * Continue stream buffer preparation by allocating the next
+     * buffer for this stream.  May only be called in the PREPARED state.
+     *
+     * Returns OK and transitions to the CONFIGURED state if all buffers
+     * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA.
+     *
+     * Returns:
+     *    OK if no more buffers need to be preallocated, and transitions
+     *        to the CONFIGURED state.
+     *    NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish
+     *        buffer pre-allocation.
+     *    NO_INIT in case of a serious error from the HAL device
+     *    INVALID_OPERATION if called when not in CONFIGURED state, or a
+     *        valid buffer has already been returned to this stream.
+     */
+    virtual status_t prepareNextBuffer() = 0;
+
+    /**
+     * Cancel stream preparation early. In case allocation needs to be
+     * stopped, this method transitions the stream back to the CONFIGURED state.
+     * Buffers that have been allocated with prepareNextBuffer remain that way,
+     * but a later use of prepareNextBuffer will require just as many
+     * calls as if the earlier prepare attempt had not existed.
+     *
+     * Returns:
+     *    OK if cancellation succeeded, and transitions to the CONFIGURED state
+     *    INVALID_OPERATION if not in the PREPARING state
+     *    NO_INIT in case of a serious error from the HAL device
+     */
+    virtual status_t cancelPrepare() = 0;
+
+    /**
      * Fill in the camera3_stream_buffer with the next valid buffer for this
      * stream, to hand over to the HAL.
      *
@@ -128,6 +190,13 @@
     virtual status_t returnInputBuffer(const camera3_stream_buffer &buffer) = 0;
 
     /**
+     * Get the buffer producer of the input buffer queue.
+     *
+     * This method only applies to input streams.
+     */
+    virtual status_t getInputBufferProducer(sp<IGraphicBufferProducer> *producer) = 0;
+
+    /**
      * Whether any of the stream's buffers are currently in use by the HAL,
      * including buffers that have been returned but not yet had their
      * release fence signaled.
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index ad5486d..aa40a2d 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -17,7 +17,9 @@
 #ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
 #define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
 
+#include <utils/Condition.h>
 #include <utils/Mutex.h>
+#include <utils/Timers.h>
 
 #include <algorithm>
 #include <utility>
@@ -263,6 +265,16 @@
      */
     std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
 
+    /**
+     * Block until the given client is no longer in the active clients list, or the timeout
+     * occurred.
+     *
+     * Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on
+     * failure.
+     */
+    status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
+            nsecs_t timeout) const;
+
 protected:
     ~ClientManager();
 
@@ -284,6 +296,7 @@
     int64_t getCurrentCostLocked() const;
 
     mutable Mutex mLock;
+    mutable Condition mRemovedCondition;
     int32_t mMaxCost;
     // LRU ordered, most recent at end
     std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
@@ -430,6 +443,7 @@
         }), mClients.end());
 
     mClients.push_back(client);
+    mRemovedCondition.broadcast();
 
     return evicted;
 }
@@ -487,6 +501,7 @@
 void ClientManager<KEY, VALUE>::removeAll() {
     Mutex::Autolock lock(mLock);
     mClients.clear();
+    mRemovedCondition.broadcast();
 }
 
 template<class KEY, class VALUE>
@@ -505,6 +520,39 @@
             return false;
         }), mClients.end());
 
+    mRemovedCondition.broadcast();
+    return ret;
+}
+
+template<class KEY, class VALUE>
+status_t ClientManager<KEY, VALUE>::waitUntilRemoved(
+        const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
+        nsecs_t timeout) const {
+    status_t ret = NO_ERROR;
+    Mutex::Autolock lock(mLock);
+
+    bool isRemoved = false;
+
+    // Figure out what time in the future we should hit the timeout
+    nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout;
+
+    while (!isRemoved) {
+        isRemoved = true;
+        for (const auto& i : mClients) {
+            if (i == client) {
+                isRemoved = false;
+            }
+        }
+
+        if (!isRemoved) {
+            ret = mRemovedCondition.waitRelative(mLock, timeout);
+            if (ret != NO_ERROR) {
+                break;
+            }
+            timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
+        }
+    }
+
     return ret;
 }
 
@@ -520,6 +568,7 @@
             }
             return false;
         }), mClients.end());
+    mRemovedCondition.broadcast();
 }
 
 template<class KEY, class VALUE>
diff --git a/services/mediaresourcemanager/Android.mk b/services/mediaresourcemanager/Android.mk
index 84218cf..b72230f 100644
--- a/services/mediaresourcemanager/Android.mk
+++ b/services/mediaresourcemanager/Android.mk
@@ -2,7 +2,7 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := ResourceManagerService.cpp
+LOCAL_SRC_FILES := ResourceManagerService.cpp ServiceLog.cpp
 
 LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog
 
@@ -13,6 +13,9 @@
 LOCAL_C_INCLUDES += \
     $(TOPDIR)frameworks/av/include
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 7296d47..e2b6695 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -29,6 +29,7 @@
 #include <unistd.h>
 
 #include "ResourceManagerService.h"
+#include "ServiceLog.h"
 
 namespace android {
 
@@ -88,29 +89,76 @@
     return infos.editItemAt(infos.size() - 1);
 }
 
+status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */) {
+    Mutex::Autolock lock(mLock);
+
+    String8 result;
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+
+    snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this);
+    result.append(buffer);
+    result.append("  Policies:\n");
+    snprintf(buffer, SIZE, "    SupportsMultipleSecureCodecs: %d\n", mSupportsMultipleSecureCodecs);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "    SupportsSecureWithNonSecureCodec: %d\n", mSupportsSecureWithNonSecureCodec);
+    result.append(buffer);
+
+    result.append("  Processes:\n");
+    for (size_t i = 0; i < mMap.size(); ++i) {
+        snprintf(buffer, SIZE, "    Pid: %d\n", mMap.keyAt(i));
+        result.append(buffer);
+
+        const ResourceInfos &infos = mMap.valueAt(i);
+        for (size_t j = 0; j < infos.size(); ++j) {
+            result.append("      Client:\n");
+            snprintf(buffer, SIZE, "        Id: %lld\n", (long long)infos[j].clientId);
+            result.append(buffer);
+
+            snprintf(buffer, SIZE, "        Name: %s\n", infos[j].client->getName().string());
+            result.append(buffer);
+
+            Vector<MediaResource> resources = infos[j].resources;
+            result.append("        Resources:\n");
+            for (size_t k = 0; k < resources.size(); ++k) {
+                snprintf(buffer, SIZE, "          %s\n", resources[k].toString().string());
+                result.append(buffer);
+            }
+        }
+    }
+    result.append("  Events logs (most recent at top):\n");
+    result.append(mServiceLog->toString("    " /* linePrefix */));
+
+    write(fd, result.string(), result.size());
+    return OK;
+}
+
 ResourceManagerService::ResourceManagerService()
     : mProcessInfo(new ProcessInfo()),
+      mServiceLog(new ServiceLog()),
       mSupportsMultipleSecureCodecs(true),
       mSupportsSecureWithNonSecureCodec(true) {}
 
 ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo)
     : mProcessInfo(processInfo),
+      mServiceLog(new ServiceLog()),
       mSupportsMultipleSecureCodecs(true),
       mSupportsSecureWithNonSecureCodec(true) {}
 
 ResourceManagerService::~ResourceManagerService() {}
 
 void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) {
-    ALOGV("config(%s)", getString(policies).string());
+    String8 log = String8::format("config(%s)", getString(policies).string());
+    mServiceLog->add(log);
 
     Mutex::Autolock lock(mLock);
     for (size_t i = 0; i < policies.size(); ++i) {
         String8 type = policies[i].mType;
-        uint64_t value = policies[i].mValue;
+        String8 value = policies[i].mValue;
         if (type == kPolicySupportsMultipleSecureCodecs) {
-            mSupportsMultipleSecureCodecs = (value != 0);
+            mSupportsMultipleSecureCodecs = (value == "true");
         } else if (type == kPolicySupportsSecureWithNonSecureCodec) {
-            mSupportsSecureWithNonSecureCodec = (value != 0);
+            mSupportsSecureWithNonSecureCodec = (value == "true");
         }
     }
 }
@@ -120,17 +168,20 @@
         int64_t clientId,
         const sp<IResourceManagerClient> client,
         const Vector<MediaResource> &resources) {
-    ALOGV("addResource(pid %d, clientId %lld, resources %s)",
+    String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)",
             pid, (long long) clientId, getString(resources).string());
+    mServiceLog->add(log);
 
     Mutex::Autolock lock(mLock);
     ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
     ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos);
+    // TODO: do the merge instead of append.
     info.resources.appendVector(resources);
 }
 
 void ResourceManagerService::removeResource(int64_t clientId) {
-    ALOGV("removeResource(%lld)", (long long) clientId);
+    String8 log = String8::format("removeResource(%lld)", (long long) clientId);
+    mServiceLog->add(log);
 
     Mutex::Autolock lock(mLock);
     bool found = false;
@@ -155,8 +206,9 @@
 
 bool ResourceManagerService::reclaimResource(
         int callingPid, const Vector<MediaResource> &resources) {
-    ALOGV("reclaimResource(callingPid %d, resources %s)",
+    String8 log = String8::format("reclaimResource(callingPid %d, resources %s)",
             callingPid, getString(resources).string());
+    mServiceLog->add(log);
 
     Vector<sp<IResourceManagerClient>> clients;
     {
@@ -197,19 +249,59 @@
                 }
             }
         }
+
+        if (clients.size() == 0) {
+            // if we are here, run the third pass to free one codec with the same type.
+            for (size_t i = 0; i < resources.size(); ++i) {
+                String8 type = resources[i].mType;
+                if (type == kResourceSecureCodec || type == kResourceNonSecureCodec) {
+                    sp<IResourceManagerClient> client;
+                    if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) {
+                        return false;
+                    }
+                    clients.push_back(client);
+                }
+            }
+        }
     }
 
     if (clients.size() == 0) {
         return false;
     }
 
+    sp<IResourceManagerClient> failedClient;
     for (size_t i = 0; i < clients.size(); ++i) {
-        ALOGV("reclaimResource from client %p", clients[i].get());
+        log = String8::format("reclaimResource from client %p", clients[i].get());
+        mServiceLog->add(log);
         if (!clients[i]->reclaimResource()) {
-            return false;
+            failedClient = clients[i];
+            break;
         }
     }
-    return true;
+
+    {
+        Mutex::Autolock lock(mLock);
+        bool found = false;
+        for (size_t i = 0; i < mMap.size(); ++i) {
+            ResourceInfos &infos = mMap.editValueAt(i);
+            for (size_t j = 0; j < infos.size();) {
+                if (infos[j].client == failedClient) {
+                    j = infos.removeAt(j);
+                    found = true;
+                } else {
+                    ++j;
+                }
+            }
+            if (found) {
+                break;
+            }
+        }
+        if (!found) {
+            ALOGV("didn't find failed client");
+        }
+    }
+
+    return (failedClient == NULL);
 }
 
 bool ResourceManagerService::getAllClients_l(
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 2ed9bf8..0d9d878 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -30,6 +30,7 @@
 
 namespace android {
 
+class ServiceLog;
 struct ProcessInfoInterface;
 
 struct ResourceInfo {
@@ -48,6 +49,8 @@
 public:
     static char const *getServiceName() { return "media.resource_manager"; }
 
+    virtual status_t dump(int fd, const Vector<String16>& args);
+
     ResourceManagerService();
     ResourceManagerService(sp<ProcessInfoInterface> processInfo);
 
@@ -94,6 +97,7 @@
 
     mutable Mutex mLock;
     sp<ProcessInfoInterface> mProcessInfo;
+    sp<ServiceLog> mServiceLog;
     PidResourceInfosMap mMap;
     bool mSupportsMultipleSecureCodecs;
     bool mSupportsSecureWithNonSecureCodec;
diff --git a/services/mediaresourcemanager/ServiceLog.cpp b/services/mediaresourcemanager/ServiceLog.cpp
new file mode 100644
index 0000000..791e797
--- /dev/null
+++ b/services/mediaresourcemanager/ServiceLog.cpp
@@ -0,0 +1,63 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ServiceLog"
+#include <utils/Log.h>
+
+#include <time.h>
+
+#include "ServiceLog.h"
+
+static const size_t kDefaultMaxNum = 100;
+
+namespace android {
+
+ServiceLog::ServiceLog() : mMaxNum(kDefaultMaxNum), mLogs(mMaxNum) {}
+ServiceLog::ServiceLog(size_t maxNum) : mMaxNum(maxNum), mLogs(mMaxNum) {}
+
+void ServiceLog::add(const String8 &log) {
+    Mutex::Autolock lock(mLock);
+    time_t now = time(0);
+    char buf[64];
+    strftime(buf, sizeof(buf), "%m-%d %T", localtime(&now));
+    mLogs.add(String8::format("%s %s", buf, log.string()));
+}
+
+String8 ServiceLog::toString(const char *linePrefix) const {
+    Mutex::Autolock lock(mLock);
+    String8 result;
+    for (const auto& log : mLogs) {
+        addLine(log.string(), linePrefix, &result);
+    }
+    if (mLogs.size() == mMaxNum) {
+        addLine("...", linePrefix, &result);
+    } else if (mLogs.size() == 0) {
+        addLine("[no events yet]", linePrefix, &result);
+    }
+    return result;
+}
+
+void ServiceLog::addLine(const char *log, const char *prefix, String8 *result) const {
+    if (prefix != NULL) {
+        result->append(prefix);
+    }
+    result->append(log);
+    result->append("\n");
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ServiceLog.h b/services/mediaresourcemanager/ServiceLog.h
new file mode 100644
index 0000000..a6f16eb
--- /dev/null
+++ b/services/mediaresourcemanager/ServiceLog.h
@@ -0,0 +1,50 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_SERVICELOG_H
+#define ANDROID_SERVICELOG_H
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+#include "media/RingBuffer.h"
+
+namespace android {
+
+class ServiceLog : public RefBase {
+public:
+    ServiceLog();
+    ServiceLog(size_t maxNum);
+
+    void add(const String8 &log);
+    String8 toString(const char *linePrefix = NULL) const;
+
+private:
+    size_t mMaxNum;
+    mutable Mutex mLock;
+    RingBuffer<String8> mLogs;
+
+    void addLine(const char *log, const char *prefix, String8 *result) const;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_SERVICELOG_H
diff --git a/services/mediaresourcemanager/test/Android.mk b/services/mediaresourcemanager/test/Android.mk
index 228b62a..3b4ef0d 100644
--- a/services/mediaresourcemanager/test/Android.mk
+++ b/services/mediaresourcemanager/test/Android.mk
@@ -20,6 +20,35 @@
   frameworks/av/include \
   frameworks/av/services/mediaresourcemanager \
 
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
+LOCAL_32_BIT_ONLY := true
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := ServiceLog_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+  ServiceLog_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+  liblog \
+  libmedia \
+  libresourcemanagerservice \
+  libutils \
+
+LOCAL_C_INCLUDES := \
+  frameworks/av/include \
+  frameworks/av/services/mediaresourcemanager \
+
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+
 LOCAL_32_BIT_ONLY := true
 
 include $(BUILD_NATIVE_TEST)
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index b73e1bc..3d53f1f 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,6 +55,10 @@
         return true;
     }
 
+    virtual String8 getName() {
+        return String8("test_client");
+    }
+
     bool reclaimed() const {
         return mReclaimed;
     }
@@ -118,6 +122,20 @@
         client3->reset();
     }
 
+    // test set up
+    // ---------------------------------------------------------------------------------
+    //   pid                priority         client           type               number
+    // ---------------------------------------------------------------------------------
+    //   kTestPid1(30)      30               mTestClient1     secure codec       1
+    //                                                        graphic memory     200
+    //                                                        graphic memory     200
+    // ---------------------------------------------------------------------------------
+    //   kTestPid2(20)      20               mTestClient2     non-secure codec   1
+    //                                                        graphic memory     300
+    //                                       -------------------------------------------
+    //                                       mTestClient3     secure codec       1
+    //                                                        graphic memory     100
+    // ---------------------------------------------------------------------------------
     void addResource() {
         // kTestPid1 mTestClient1
         Vector<MediaResource> resources1;
@@ -162,17 +180,27 @@
         EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
 
         Vector<MediaResourcePolicy> policies1;
-        policies1.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 1));
         policies1.push_back(
-                MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 0));
+                MediaResourcePolicy(
+                        String8(kPolicySupportsMultipleSecureCodecs),
+                        String8("true")));
+        policies1.push_back(
+                MediaResourcePolicy(
+                        String8(kPolicySupportsSecureWithNonSecureCodec),
+                        String8("false")));
         mService->config(policies1);
         EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs);
         EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec);
 
         Vector<MediaResourcePolicy> policies2;
-        policies2.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 0));
         policies2.push_back(
-                MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 1));
+                MediaResourcePolicy(
+                        String8(kPolicySupportsMultipleSecureCodecs),
+                        String8("false")));
+        policies2.push_back(
+                MediaResourcePolicy(
+                        String8(kPolicySupportsSecureWithNonSecureCodec),
+                        String8("true")));
         mService->config(policies2);
         EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs);
         EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
@@ -202,10 +230,12 @@
         int lowPriorityPid = 100;
         EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients));
         int midPriorityPid = 25;
-        EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients));
+        // some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l
+        // will fail.
+        EXPECT_FALSE(mService->getAllClients_l(midPriorityPid, type, &clients));
         int highPriorityPid = 10;
-        EXPECT_TRUE(mService->getAllClients_l(10, unknowType, &clients));
-        EXPECT_TRUE(mService->getAllClients_l(10, type, &clients));
+        EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, unknowType, &clients));
+        EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, type, &clients));
 
         EXPECT_EQ(2u, clients.size());
         EXPECT_EQ(mTestClient3, clients[0]);
@@ -308,6 +338,30 @@
             // nothing left
             EXPECT_FALSE(mService->reclaimResource(10, resources));
         }
+
+        // ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
+        {
+            addResource();
+            mService->mSupportsMultipleSecureCodecs = true;
+            mService->mSupportsSecureWithNonSecureCodec = true;
+
+            Vector<MediaResource> resources;
+            resources.push_back(MediaResource(String8(kResourceSecureCodec), 1));
+
+            EXPECT_TRUE(mService->reclaimResource(10, resources));
+            // secure codec from lowest process got reclaimed
+            verifyClients(true, false, false);
+
+            // call again should reclaim another secure codec from lowest process
+            EXPECT_TRUE(mService->reclaimResource(10, resources));
+            verifyClients(false, false, true);
+
+            // nothing left
+            EXPECT_FALSE(mService->reclaimResource(10, resources));
+
+            // clean up client 2 which still has non secure codec left
+            mService->removeResource((int64_t) mTestClient2.get());
+        }
     }
 
     void testReclaimResourceNonSecure() {
@@ -360,6 +414,26 @@
             // nothing left
             EXPECT_FALSE(mService->reclaimResource(10, resources));
         }
+
+        // ### secure codec can coexist with non-secure codec ###
+        {
+            addResource();
+            mService->mSupportsSecureWithNonSecureCodec = true;
+
+            Vector<MediaResource> resources;
+            resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1));
+
+            EXPECT_TRUE(mService->reclaimResource(10, resources));
+            // one non secure codec from lowest process got reclaimed
+            verifyClients(false, true, false);
+
+            // nothing left
+            EXPECT_FALSE(mService->reclaimResource(10, resources));
+
+            // clean up client 1 and 3 which still have secure codec left
+            mService->removeResource((int64_t) mTestClient1.get());
+            mService->removeResource((int64_t) mTestClient3.get());
+        }
     }
 
     void testGetLowestPriorityBiggestClient() {
diff --git a/services/mediaresourcemanager/test/ServiceLog_test.cpp b/services/mediaresourcemanager/test/ServiceLog_test.cpp
new file mode 100644
index 0000000..9172499
--- /dev/null
+++ b/services/mediaresourcemanager/test/ServiceLog_test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ServiceLog_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "ServiceLog.h"
+
+namespace android {
+
+class ServiceLogTest : public ::testing::Test {
+public:
+    ServiceLogTest() : mServiceLog(new ServiceLog(3)) {
+    }
+
+protected:
+    sp<ServiceLog> mServiceLog;
+};
+
+TEST_F(ServiceLogTest, addThenToString) {
+    String8 logString;
+
+    mServiceLog->add(String8("log1"));
+    logString = mServiceLog->toString();
+    EXPECT_TRUE(logString.contains("log1"));
+    ALOGV("toString:\n%s", logString.string());
+
+    static const char kTestLogPrefix[] = "testlogprefix: ";
+    logString = mServiceLog->toString(kTestLogPrefix);
+    EXPECT_TRUE(logString.contains(kTestLogPrefix));
+    EXPECT_TRUE(logString.contains("log1"));
+    ALOGV("toString:\n%s", logString.string());
+
+    mServiceLog->add(String8("log2"));
+    logString = mServiceLog->toString();
+    EXPECT_TRUE(logString.contains("log1"));
+    EXPECT_TRUE(logString.contains("log2"));
+    ALOGV("toString:\n%s", logString.string());
+
+    mServiceLog->add(String8("log3"));
+    logString = mServiceLog->toString();
+    EXPECT_TRUE(logString.contains("log1"));
+    EXPECT_TRUE(logString.contains("log2"));
+    EXPECT_TRUE(logString.contains("log3"));
+    ALOGV("toString:\n%s", logString.string());
+
+    mServiceLog->add(String8("log4"));
+    logString = mServiceLog->toString();
+    EXPECT_FALSE(logString.contains("log1"));
+    EXPECT_TRUE(logString.contains("log2"));
+    EXPECT_TRUE(logString.contains("log3"));
+    EXPECT_TRUE(logString.contains("log4"));
+    ALOGV("toString:\n%s", logString.string());
+
+    mServiceLog->add(String8("log5"));
+    logString = mServiceLog->toString();
+    EXPECT_FALSE(logString.contains("log1"));
+    EXPECT_FALSE(logString.contains("log2"));
+    EXPECT_TRUE(logString.contains("log3"));
+    EXPECT_TRUE(logString.contains("log4"));
+    EXPECT_TRUE(logString.contains("log5"));
+    ALOGV("toString:\n%s", logString.string());
+}
+
+} // namespace android
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index a6c2bdf..cd0f5f3 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -146,7 +146,7 @@
     radio = module->addClient(client, config, withAudio);
 
     if (radio == 0) {
-        NO_INIT;
+        return NO_INIT;
     }
     return NO_ERROR;
 }
@@ -500,13 +500,12 @@
         if (audio) {
             notifyDeviceConnection(true, "");
         }
+        ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
     } else {
+        ALOGW("%s open_tuner failed with error %d", __FUNCTION__, ret);
         moduleClient.clear();
     }
 
-
-    ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
-
     return moduleClient;
 }
 
diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp
index 75f68b8..e14a771 100644
--- a/soundtrigger/ISoundTriggerHwService.cpp
+++ b/soundtrigger/ISoundTriggerHwService.cpp
@@ -40,6 +40,8 @@
     SET_CAPTURE_STATE,
 };
 
+#define MAX_ITEMS_PER_LIST 1024
+
 class BpSoundTriggerHwService: public BpInterface<ISoundTriggerHwService>
 {
 public:
@@ -116,10 +118,18 @@
         case LIST_MODULES: {
             CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
             unsigned int numModulesReq = data.readInt32();
+            if (numModulesReq > MAX_ITEMS_PER_LIST) {
+                numModulesReq = MAX_ITEMS_PER_LIST;
+            }
             unsigned int numModules = numModulesReq;
             struct sound_trigger_module_descriptor *modules =
                     (struct sound_trigger_module_descriptor *)calloc(numModulesReq,
                                                    sizeof(struct sound_trigger_module_descriptor));
+            if (modules == NULL) {
+                reply->writeInt32(NO_MEMORY);
+                reply->writeInt32(0);
+                return NO_ERROR;
+            }
             status_t status = listModules(modules, &numModules);
             reply->writeInt32(status);
             reply->writeInt32(numModules);