Merge "audio policy: add new input audio device"
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 22199fa..85f44f0 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -77,6 +77,32 @@
     return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
 }
 
+status_t Camera::connectLegacy(int cameraId, int halVersion,
+        const String16& clientPackageName,
+        int clientUid,
+        sp<Camera>& camera)
+{
+    ALOGV("%s: connect legacy camera device", __FUNCTION__);
+    sp<Camera> c = new Camera(cameraId);
+    sp<ICameraClient> cl = c;
+    status_t status = NO_ERROR;
+    const sp<ICameraService>& cs = CameraBaseT::getCameraService();
+
+    if (cs != 0) {
+        status = cs.get()->connectLegacy(cl, cameraId, halVersion, clientPackageName,
+                                        clientUid, /*out*/c->mCamera);
+    }
+    if (status == OK && c->mCamera != 0) {
+        c->mCamera->asBinder()->linkToDeath(c);
+        c->mStatus = NO_ERROR;
+        camera = c;
+    } else {
+        ALOGW("An error occurred while connecting to camera: %d", cameraId);
+        c.clear();
+    }
+    return status;
+}
+
 status_t Camera::reconnect()
 {
     ALOGV("reconnect");
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index 79c33f9..5485205 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -186,6 +186,29 @@
         return status;
     }
 
+    // connect to camera service (android.hardware.Camera)
+    virtual status_t connectLegacy(const sp<ICameraClient>& cameraClient, int cameraId,
+                             int halVersion,
+                             const String16 &clientPackageName, int clientUid,
+                             /*out*/sp<ICamera>& device)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+        data.writeStrongBinder(cameraClient->asBinder());
+        data.writeInt32(cameraId);
+        data.writeInt32(halVersion);
+        data.writeString16(clientPackageName);
+        data.writeInt32(clientUid);
+        remote()->transact(BnCameraService::CONNECT_LEGACY, data, &reply);
+
+        if (readExceptionCode(reply)) return -EPROTO;
+        status_t status = reply.readInt32();
+        if (reply.readInt32() != 0) {
+            device = interface_cast<ICamera>(reply.readStrongBinder());
+        }
+        return status;
+    }
+
     // connect to camera service (pro client)
     virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, int cameraId,
                                 const String16 &clientPackageName, int clientUid,
@@ -446,6 +469,27 @@
             reply->writeInt32(supportsCameraApi(cameraId, apiVersion));
             return NO_ERROR;
         } break;
+        case CONNECT_LEGACY: {
+            CHECK_INTERFACE(ICameraService, data, reply);
+            sp<ICameraClient> cameraClient =
+                    interface_cast<ICameraClient>(data.readStrongBinder());
+            int32_t cameraId = data.readInt32();
+            int32_t halVersion = data.readInt32();
+            const String16 clientName = data.readString16();
+            int32_t clientUid = data.readInt32();
+            sp<ICamera> camera;
+            status_t status = connectLegacy(cameraClient, cameraId, halVersion,
+                    clientName, clientUid, /*out*/camera);
+            reply->writeNoException();
+            reply->writeInt32(status);
+            if (camera != NULL) {
+                reply->writeInt32(1);
+                reply->writeStrongBinder(camera->asBinder());
+            } else {
+                reply->writeInt32(0);
+            }
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
index 4da16bc..03e0062 100644
--- a/cmds/screenrecord/FrameOutput.cpp
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -71,7 +71,7 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
     mGlConsumer = new GLConsumer(consumer, mExtTextureName,
-                GL_TEXTURE_EXTERNAL_OES);
+                GL_TEXTURE_EXTERNAL_OES, true, false);
     mGlConsumer->setName(String8("virtual display"));
     mGlConsumer->setDefaultBufferSize(width, height);
     mGlConsumer->setDefaultMaxBufferCount(5);
diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
index c2a8f1b..7fef53d 100644
--- a/cmds/screenrecord/Overlay.cpp
+++ b/cmds/screenrecord/Overlay.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
 #define LOG_TAG "ScreenRecord"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
@@ -27,9 +31,6 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include <stdlib.h>
-#include <assert.h>
-
 #include "screenrecord.h"
 #include "Overlay.h"
 #include "TextRenderer.h"
@@ -172,7 +173,7 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&mProducer, &consumer);
     mGlConsumer = new GLConsumer(consumer, mExtTextureName,
-                GL_TEXTURE_EXTERNAL_OES);
+                GL_TEXTURE_EXTERNAL_OES, true, false);
     mGlConsumer->setName(String8("virtual display"));
     mGlConsumer->setDefaultBufferSize(width, height);
     mGlConsumer->setDefaultMaxBufferCount(5);
@@ -235,7 +236,7 @@
 
     char textBuf[64];
     getTimeString_l(monotonicNsec, textBuf, sizeof(textBuf));
-    String8 timeStr(String8::format("%s f=%lld (%zd)",
+    String8 timeStr(String8::format("%s f=%" PRId64 " (%zd)",
             textBuf, frameNumber, mTotalDroppedFrames));
     mTextRenderer.drawString(mTexProgram, Program::kIdentity, 0, 0, timeStr);
 
diff --git a/cmds/screenrecord/TextRenderer.cpp b/cmds/screenrecord/TextRenderer.cpp
index 784055c..6a9176b 100644
--- a/cmds/screenrecord/TextRenderer.cpp
+++ b/cmds/screenrecord/TextRenderer.cpp
@@ -353,6 +353,6 @@
         }
     }
 
-    ALOGV("goodPos=%d for str='%s'", goodPos, str);
+    ALOGV("goodPos=%zu for str='%s'", goodPos, str);
     return const_cast<char*>(str + goodPos);
 }
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 02ed53a..02df1d2 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -14,6 +14,19 @@
  * limitations under the License.
  */
 
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
 #define LOG_TAG "ScreenRecord"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
@@ -36,18 +49,6 @@
 #include <media/stagefright/MediaMuxer.h>
 #include <media/ICrypto.h>
 
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <getopt.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <assert.h>
-
 #include "screenrecord.h"
 #include "Overlay.h"
 #include "FrameOutput.h"
@@ -354,7 +355,7 @@
         case NO_ERROR:
             // got a buffer
             if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
-                ALOGV("Got codec config buffer (%u bytes)", size);
+                ALOGV("Got codec config buffer (%zu bytes)", size);
                 if (muxer != NULL) {
                     // ignore this -- we passed the CSD into MediaMuxer when
                     // we got the format change notification
@@ -362,7 +363,7 @@
                 }
             }
             if (size != 0) {
-                ALOGV("Got data in buffer %d, size=%d, pts=%lld",
+                ALOGV("Got data in buffer %zu, size=%zu, pts=%" PRId64,
                         bufIndex, size, ptsUsec);
 
                 { // scope
@@ -473,7 +474,7 @@
 
     ALOGV("Encoder stopping (req=%d)", gStopRequested);
     if (gVerbose) {
-        printf("Encoder stopping; recorded %u frames in %lld seconds\n",
+        printf("Encoder stopping; recorded %u frames in %" PRId64 " seconds\n",
                 debugNumFrames, nanoseconds_to_seconds(
                         systemTime(CLOCK_MONOTONIC) - startWhenNsec));
     }
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 3c0c7ec..0f729a3 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -211,28 +211,28 @@
                 int32_t what;
                 CHECK(msg->findInt32("what", &what));
 
-                if (what == ACodec::kWhatFillThisBuffer) {
+                if (what == CodecBase::kWhatFillThisBuffer) {
                     onFillThisBuffer(msg);
-                } else if (what == ACodec::kWhatDrainThisBuffer) {
+                } else if (what == CodecBase::kWhatDrainThisBuffer) {
                     if ((mNumOutputBuffersReceived++ % 16) == 0) {
                         printf(".");
                         fflush(stdout);
                     }
 
                     onDrainThisBuffer(msg);
-                } else if (what == ACodec::kWhatEOS
-                        || what == ACodec::kWhatError) {
-                    printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n");
+                } else if (what == CodecBase::kWhatEOS
+                        || what == CodecBase::kWhatError) {
+                    printf((what == CodecBase::kWhatEOS) ? "$\n" : "E\n");
 
                     printStatistics();
                     (new AMessage(kWhatStop, id()))->post();
-                } else if (what == ACodec::kWhatFlushCompleted) {
+                } else if (what == CodecBase::kWhatFlushCompleted) {
                     mSeekState = SEEK_FLUSH_COMPLETED;
                     mCodec->signalResume();
 
                     (new AMessage(kWhatSeek, id()))->post(5000000ll);
-                } else if (what == ACodec::kWhatOutputFormatChanged) {
-                } else if (what == ACodec::kWhatShutdownCompleted) {
+                } else if (what == CodecBase::kWhatOutputFormatChanged) {
+                } else if (what == CodecBase::kWhatShutdownCompleted) {
                     mDecodeLooper->unregisterHandler(mCodec->id());
 
                     if (mDecodeLooper != looper()) {
@@ -240,12 +240,6 @@
                     }
 
                     looper()->stop();
-                } else if (what == ACodec::kWhatError) {
-                    ALOGE("something went wrong, codec reported an error.");
-
-                    printf("E\n");
-
-                    (new AMessage(kWhatStop, id()))->post();
                 }
                 break;
             }
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index b70afe6..81edcb4 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -942,7 +942,9 @@
             sp<IGraphicBufferProducer> producer;
             sp<IGraphicBufferConsumer> consumer;
             BufferQueue::createBufferQueue(&producer, &consumer);
-            sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */);
+            sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */,
+                    GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */,
+                    false /* isControlledByApp */);
             gSurface = new Surface(producer);
         }
 
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 2b71904..63341e0 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -34,7 +34,18 @@
 static Vector<uid_t> trustedUids;
 
 static bool isProtectedCallAllowed() {
-    return true;
+    // TODO
+    // Following implementation is just for reference.
+    // Each OEM manufacturer should implement/replace with their own solutions.
+    IPCThreadState* ipcState = IPCThreadState::self();
+    uid_t uid = ipcState->getCallingUid();
+
+    for (unsigned int i = 0; i < trustedUids.size(); ++i) {
+        if (trustedUids[i] == uid) {
+            return true;
+        }
+    }
+    return false;
 }
 
 void DrmManagerService::instantiate() {
diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp
new file mode 100644
index 0000000..01f8d65
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <openssl/aes.h>
+
+#include "AesCtrDecryptor.h"
+
+namespace clearkeydrm {
+
+static const size_t kBlockBitCount = kBlockSize * 8;
+
+android::status_t AesCtrDecryptor::decrypt(const android::Vector<uint8_t>& key,
+        const Iv iv, const uint8_t* source,
+        uint8_t* destination,
+        const SubSample* subSamples,
+        size_t numSubSamples,
+        size_t* bytesDecryptedOut) {
+    uint32_t blockOffset = 0;
+    uint8_t previousEncryptedCounter[kBlockSize];
+    memset(previousEncryptedCounter, 0, kBlockSize);
+
+    size_t offset = 0;
+    AES_KEY opensslKey;
+    AES_set_encrypt_key(key.array(), kBlockBitCount, &opensslKey);
+    Iv opensslIv;
+    memcpy(opensslIv, iv, sizeof(opensslIv));
+
+    for (size_t i = 0; i < numSubSamples; ++i) {
+        const SubSample& subSample = subSamples[i];
+
+        if (subSample.mNumBytesOfClearData > 0) {
+            memcpy(destination + offset, source + offset,
+                    subSample.mNumBytesOfClearData);
+            offset += subSample.mNumBytesOfClearData;
+        }
+
+        if (subSample.mNumBytesOfEncryptedData > 0) {
+            AES_ctr128_encrypt(source + offset, destination + offset,
+                    subSample.mNumBytesOfEncryptedData, &opensslKey,
+                    opensslIv, previousEncryptedCounter,
+                    &blockOffset);
+            offset += subSample.mNumBytesOfEncryptedData;
+        }
+    }
+
+    *bytesDecryptedOut = offset;
+    return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h
new file mode 100644
index 0000000..b416266
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_AES_CTR_DECRYPTOR_H_
+#define CLEARKEY_AES_CTR_DECRYPTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <Utils.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+class AesCtrDecryptor {
+public:
+    AesCtrDecryptor() {}
+
+    android::status_t decrypt(const android::Vector<uint8_t>& key, const Iv iv,
+            const uint8_t* source, uint8_t* destination,
+            const SubSample* subSamples, size_t numSubSamples,
+            size_t* bytesDecryptedOut);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(AesCtrDecryptor);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_AES_CTR_DECRYPTOR_H_
diff --git a/drm/mediadrm/plugins/clearkey/Android.mk b/drm/mediadrm/plugins/clearkey/Android.mk
new file mode 100644
index 0000000..22a85b4
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Android.mk
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2014 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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    AesCtrDecryptor.cpp \
+    ClearKeyUUID.cpp \
+    CreatePluginFactories.cpp \
+    CryptoFactory.cpp \
+    CryptoPlugin.cpp \
+    DrmFactory.cpp \
+    DrmPlugin.cpp \
+    InitDataParser.cpp \
+    JsonWebKey.cpp \
+    Session.cpp \
+    SessionLibrary.cpp \
+    Utils.cpp \
+
+LOCAL_C_INCLUDES := \
+    bionic \
+    external/jsmn \
+    external/openssl/include \
+    frameworks/av/drm/mediadrm/plugins/clearkey \
+    frameworks/av/include \
+    frameworks/native/include \
+
+LOCAL_MODULE := libdrmclearkeyplugin
+
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := mediadrm
+
+LOCAL_SHARED_LIBRARIES := \
+    libcrypto \
+    liblog \
+    libstagefright_foundation \
+    libutils \
+
+LOCAL_STATIC_LIBRARIES := \
+    libjsmn \
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+#########################################################################
+# Build unit tests
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h
new file mode 100644
index 0000000..a28959a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_TYPES_H_
+#define CLEARKEY_TYPES_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <openssl/aes.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+
+namespace clearkeydrm {
+
+const uint8_t kBlockSize = AES_BLOCK_SIZE;
+typedef uint8_t KeyId[kBlockSize];
+typedef uint8_t Iv[kBlockSize];
+
+typedef android::CryptoPlugin::SubSample SubSample;
+
+typedef android::KeyedVector<android::Vector<uint8_t>,
+        android::Vector<uint8_t> > KeyMap;
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_TYPES_H_
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
new file mode 100644
index 0000000..ed050f7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include "ClearKeyUUID.h"
+
+namespace clearkeydrm {
+
+bool isClearKeyUUID(const uint8_t uuid[16]) {
+    static const uint8_t kClearKeyUUID[16] = {
+        0x10,0x77,0xEF,0xEC,0xC0,0xB2,0x4D,0x02,
+        0xAC,0xE3,0x3C,0x1E,0x52,0xE2,0xFB,0x4B
+    };
+
+    return !memcmp(uuid, kClearKeyUUID, sizeof(kClearKeyUUID));
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
new file mode 100644
index 0000000..ac99418
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_UUID_H_
+#define CLEARKEY_UUID_H_
+
+#include <stdint.h>
+
+namespace clearkeydrm {
+
+bool isClearKeyUUID(const uint8_t uuid[16]);
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_UUID_H_
diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
new file mode 100644
index 0000000..ec1420e
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CreatePluginFactories.h"
+
+#include "CryptoFactory.h"
+#include "DrmFactory.h"
+
+extern "C" {
+
+android::DrmFactory* createDrmFactory() {
+    return new clearkeydrm::DrmFactory();
+}
+
+android::CryptoFactory* createCryptoFactory() {
+    return new clearkeydrm::CryptoFactory();
+}
+
+} // extern "C"
diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
new file mode 100644
index 0000000..d9acec1
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
+#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/hardware/CryptoAPI.h>
+
+extern "C" {
+    android::DrmFactory* createDrmFactory();
+    android::CryptoFactory* createCryptoFactory();
+}
+
+#endif // CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
new file mode 100644
index 0000000..ee3189b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "CryptoFactory.h"
+
+#include "ClearKeyUUID.h"
+#include "CryptoPlugin.h"
+#include "Session.h"
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+bool CryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const {
+    return isClearKeyUUID(uuid);
+}
+
+android::status_t CryptoFactory::createPlugin(
+        const uint8_t uuid[16],
+        const void* data, size_t size,
+        android::CryptoPlugin** plugin) {
+    if (!isCryptoSchemeSupported(uuid)) {
+        *plugin = NULL;
+        return android::BAD_VALUE;
+    }
+
+    android::sp<Session> session = SessionLibrary::get()->findSession(
+            data, size);
+    *plugin = new CryptoPlugin(session);
+    return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/CryptoFactory.h
new file mode 100644
index 0000000..568bc4b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_CRYPTO_FACTORY_H_
+#define CLEARKEY_CRYPTO_FACTORY_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace clearkeydrm {
+
+class CryptoFactory : public android::CryptoFactory {
+public:
+    CryptoFactory() {}
+    virtual ~CryptoFactory() {}
+
+    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const;
+
+    virtual android::status_t createPlugin(
+            const uint8_t uuid[16],
+            const void* data, size_t size,
+            android::CryptoPlugin** plugin);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoFactory);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_CRYPTO_FACTORY_H_
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
new file mode 100644
index 0000000..adad136
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+
+#include "CryptoPlugin.h"
+
+namespace clearkeydrm {
+
+using android::Vector;
+using android::AString;
+using android::status_t;
+
+// Returns negative values for error code and positive values for the size of
+// decrypted data.  In theory, the output size can be larger than the input
+// size, but in practice this will never happen for AES-CTR.
+ssize_t CryptoPlugin::decrypt(bool secure, const KeyId keyId, const Iv iv,
+                              Mode mode, const void* srcPtr,
+                              const SubSample* subSamples, size_t numSubSamples,
+                              void* dstPtr, AString* errorDetailMsg) {
+    if (secure) {
+        errorDetailMsg->setTo("Secure decryption is not supported with "
+                              "ClearKey.");
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    if (mode == kMode_Unencrypted) {
+        size_t offset = 0;
+        for (size_t i = 0; i < numSubSamples; ++i) {
+            const SubSample& subSample = subSamples[i];
+
+            if (subSample.mNumBytesOfEncryptedData != 0) {
+                errorDetailMsg->setTo(
+                        "Encrypted subsamples found in allegedly unencrypted "
+                        "data.");
+                return android::ERROR_DRM_DECRYPT;
+            }
+
+            if (subSample.mNumBytesOfClearData != 0) {
+                memcpy(reinterpret_cast<uint8_t*>(dstPtr) + offset,
+                       reinterpret_cast<const uint8_t*>(srcPtr) + offset,
+                       subSample.mNumBytesOfClearData);
+                offset += subSample.mNumBytesOfClearData;
+            }
+        }
+        return static_cast<ssize_t>(offset);
+    } else if (mode == kMode_AES_CTR) {
+        size_t bytesDecrypted;
+        status_t res = mSession->decrypt(keyId, iv, srcPtr, dstPtr, subSamples,
+                                         numSubSamples, &bytesDecrypted);
+        if (res == android::OK) {
+            return static_cast<ssize_t>(bytesDecrypted);
+        } else {
+            errorDetailMsg->setTo("Decryption Error");
+            return static_cast<ssize_t>(res);
+        }
+    } else {
+        errorDetailMsg->setTo(
+                "Selected encryption mode is not supported by the ClearKey DRM "
+                "Plugin.");
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+}
+
+}  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
new file mode 100644
index 0000000..002d9e0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_CRYPTO_PLUGIN_H_
+#define CLEARKEY_CRYPTO_PLUGIN_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "ClearKeyTypes.h"
+#include "Session.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class CryptoPlugin : public android::CryptoPlugin {
+public:
+    CryptoPlugin(const android::sp<Session>& session) : mSession(session) {}
+    virtual ~CryptoPlugin() {}
+
+    virtual bool requiresSecureDecoderComponent(const char* mime) const {
+        UNUSED(mime);
+        return false;
+    }
+
+    virtual ssize_t decrypt(
+            bool secure, const KeyId keyId, const Iv iv,
+            Mode mode, const void* srcPtr,
+            const SubSample* subSamples, size_t numSubSamples,
+            void* dstPtr, android::AString* errorDetailMsg);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoPlugin);
+
+    android::sp<Session> mSession;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_CRYPTO_PLUGIN_H_
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
new file mode 100644
index 0000000..40275cf
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+
+#include "DrmFactory.h"
+
+#include "DrmPlugin.h"
+#include "ClearKeyUUID.h"
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+bool DrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) {
+    return isClearKeyUUID(uuid);
+}
+
+bool DrmFactory::isContentTypeSupported(const android::String8 &initDataType) {
+    // This should match the types handed by InitDataParser.
+    return initDataType == "cenc" ||
+           initDataType == "webm";
+}
+
+android::status_t DrmFactory::createDrmPlugin(
+        const uint8_t uuid[16], android::DrmPlugin** plugin) {
+    if (!isCryptoSchemeSupported(uuid)) {
+        *plugin = NULL;
+        return android::BAD_VALUE;
+    }
+
+    *plugin = new DrmPlugin(SessionLibrary::get());
+    return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.h b/drm/mediadrm/plugins/clearkey/DrmFactory.h
new file mode 100644
index 0000000..164d3d0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_DRM_FACTORY_H_
+#define CLEARKEY_DRM_FACTORY_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class DrmFactory : public android::DrmFactory {
+public:
+    DrmFactory() {}
+    virtual ~DrmFactory() {}
+
+    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
+
+    virtual bool isContentTypeSupported(const android::String8 &initDataType);
+
+    virtual android::status_t createDrmPlugin(
+            const uint8_t uuid[16], android::DrmPlugin** plugin);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(DrmFactory);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_DRM_FACTORY_H_
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
new file mode 100644
index 0000000..96fca94
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/StrongPointer.h>
+
+#include "DrmPlugin.h"
+
+#include "Session.h"
+
+namespace clearkeydrm {
+
+using android::sp;
+
+status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
+    sp<Session> session = mSessionLibrary->createSession();
+    sessionId = session->sessionId();
+    return android::OK;
+}
+
+status_t DrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
+    sp<Session> session = mSessionLibrary->findSession(sessionId);
+    mSessionLibrary->destroySession(session);
+    return android::OK;
+}
+
+status_t DrmPlugin::getKeyRequest(
+        const Vector<uint8_t>& scope,
+        const Vector<uint8_t>& initData,
+        const String8& initDataType,
+        KeyType keyType,
+        const KeyedVector<String8, String8>& optionalParameters,
+        Vector<uint8_t>& request,
+        String8& defaultUrl) {
+    UNUSED(optionalParameters);
+    if (keyType != kKeyType_Streaming) {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    sp<Session> session = mSessionLibrary->findSession(scope);
+    defaultUrl.clear();
+    return session->getKeyRequest(initData, initDataType, &request);
+}
+
+status_t DrmPlugin::provideKeyResponse(
+        const Vector<uint8_t>& scope,
+        const Vector<uint8_t>& response,
+        Vector<uint8_t>& keySetId) {
+    sp<Session> session = mSessionLibrary->findSession(scope);
+    status_t res = session->provideKeyResponse(response);
+    if (res == android::OK) {
+        keySetId.clear();
+    }
+    return res;
+}
+
+status_t DrmPlugin::getPropertyString(
+        const String8& name, String8& value) const {
+    if (name == "vendor") {
+        value = "Google";
+    } else if (name == "version") {
+        value = "1.0";
+    } else if (name == "description") {
+        value = "ClearKey CDM";
+    } else if (name == "algorithms") {
+        value = "";
+    } else {
+        ALOGE("App requested unknown string property %s", name.string());
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+    return android::OK;
+}
+
+}  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
new file mode 100644
index 0000000..bfbc6bf
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_DRM_PLUGIN_H_
+#define CLEARKEY_DRM_PLUGIN_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "SessionLibrary.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+using android::KeyedVector;
+using android::List;
+using android::status_t;
+using android::String8;
+using android::Vector;
+
+class DrmPlugin : public android::DrmPlugin {
+public:
+    DrmPlugin(SessionLibrary* sessionLibrary)
+            : mSessionLibrary(sessionLibrary) {}
+    virtual ~DrmPlugin() {}
+
+    virtual status_t openSession(Vector<uint8_t>& sessionId);
+
+    virtual status_t closeSession(const Vector<uint8_t>& sessionId);
+
+    virtual status_t getKeyRequest(
+            const Vector<uint8_t>& scope,
+            const Vector<uint8_t>& initData,
+            const String8& initDataType,
+            KeyType keyType,
+            const KeyedVector<String8, String8>& optionalParameters,
+            Vector<uint8_t>& request,
+            String8& defaultUrl);
+
+    virtual status_t provideKeyResponse(
+            const Vector<uint8_t>& scope,
+            const Vector<uint8_t>& response,
+            Vector<uint8_t>& keySetId);
+
+    virtual status_t removeKeys(const Vector<uint8_t>& sessionId) {
+        UNUSED(sessionId);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t restoreKeys(
+            const Vector<uint8_t>& sessionId,
+            const Vector<uint8_t>& keySetId) {
+        UNUSED(sessionId);
+        UNUSED(keySetId);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t queryKeyStatus(
+            const Vector<uint8_t>& sessionId,
+            KeyedVector<String8, String8>& infoMap) const {
+        UNUSED(sessionId);
+        UNUSED(infoMap);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t getProvisionRequest(
+            const String8& cert_type,
+            const String8& cert_authority,
+            Vector<uint8_t>& request,
+            String8& defaultUrl) {
+        UNUSED(cert_type);
+        UNUSED(cert_authority);
+        UNUSED(request);
+        UNUSED(defaultUrl);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t provideProvisionResponse(
+            const Vector<uint8_t>& response,
+            Vector<uint8_t>& certificate,
+            Vector<uint8_t>& wrappedKey) {
+        UNUSED(response);
+        UNUSED(certificate);
+        UNUSED(wrappedKey);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t getSecureStops(List<Vector<uint8_t> >& secureStops) {
+        UNUSED(secureStops);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t releaseSecureStops(const Vector<uint8_t>& ssRelease) {
+        UNUSED(ssRelease);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t getPropertyString(
+            const String8& name, String8& value) const;
+
+    virtual status_t getPropertyByteArray(
+            const String8& name, Vector<uint8_t>& value) const {
+        UNUSED(name);
+        UNUSED(value);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t setPropertyString(
+            const String8& name, const String8& value) {
+        UNUSED(name);
+        UNUSED(value);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t setPropertyByteArray(
+            const String8& name, const Vector<uint8_t>& value) {
+        UNUSED(name);
+        UNUSED(value);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t setCipherAlgorithm(
+            const Vector<uint8_t>& sessionId, const String8& algorithm) {
+        UNUSED(sessionId);
+        UNUSED(algorithm);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t setMacAlgorithm(
+            const Vector<uint8_t>& sessionId, const String8& algorithm) {
+        UNUSED(sessionId);
+        UNUSED(algorithm);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t encrypt(
+            const Vector<uint8_t>& sessionId,
+            const Vector<uint8_t>& keyId,
+            const Vector<uint8_t>& input,
+            const Vector<uint8_t>& iv,
+            Vector<uint8_t>& output) {
+        UNUSED(sessionId);
+        UNUSED(keyId);
+        UNUSED(input);
+        UNUSED(iv);
+        UNUSED(output);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t decrypt(
+            const Vector<uint8_t>& sessionId,
+            const Vector<uint8_t>& keyId,
+            const Vector<uint8_t>& input,
+            const Vector<uint8_t>& iv,
+            Vector<uint8_t>& output) {
+        UNUSED(sessionId);
+        UNUSED(keyId);
+        UNUSED(input);
+        UNUSED(iv);
+        UNUSED(output);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t sign(
+            const Vector<uint8_t>& sessionId,
+            const Vector<uint8_t>& keyId,
+            const Vector<uint8_t>& message,
+            Vector<uint8_t>& signature) {
+        UNUSED(sessionId);
+        UNUSED(keyId);
+        UNUSED(message);
+        UNUSED(signature);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t verify(
+            const Vector<uint8_t>& sessionId,
+            const Vector<uint8_t>& keyId,
+            const Vector<uint8_t>& message,
+            const Vector<uint8_t>& signature, bool& match) {
+        UNUSED(sessionId);
+        UNUSED(keyId);
+        UNUSED(message);
+        UNUSED(signature);
+        UNUSED(match);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t signRSA(
+            const Vector<uint8_t>& sessionId,
+            const String8& algorithm,
+            const Vector<uint8_t>& message,
+            const Vector<uint8_t>& wrappedKey,
+            Vector<uint8_t>& signature) {
+        UNUSED(sessionId);
+        UNUSED(algorithm);
+        UNUSED(message);
+        UNUSED(wrappedKey);
+        UNUSED(signature);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
+
+    SessionLibrary* mSessionLibrary;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_DRM_PLUGIN_H_
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
new file mode 100644
index 0000000..c22d73a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <endian.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/MediaErrors.h>
+#include <string.h>
+
+#include "InitDataParser.h"
+
+#include "ClearKeyUUID.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+using android::AString;
+using android::String8;
+using android::Vector;
+
+namespace {
+    const size_t kKeyIdSize = 16;
+    const size_t kSystemIdSize = 16;
+}
+
+android::status_t InitDataParser::parse(const Vector<uint8_t>& initData,
+        const String8& initDataType,
+        Vector<uint8_t>* licenseRequest) {
+    // Build a list of the key IDs
+    Vector<const uint8_t*> keyIds;
+    if (initDataType == "cenc") {
+        android::status_t res = parsePssh(initData, &keyIds);
+        if (res != android::OK) {
+            return res;
+        }
+    } else if (initDataType == "webm") {
+        // WebM "init data" is just a single key ID
+        if (initData.size() != kKeyIdSize) {
+            return android::ERROR_DRM_CANNOT_HANDLE;
+        }
+        keyIds.push(initData.array());
+    } else {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    // Build the request
+    String8 requestJson = generateRequest(keyIds);
+    licenseRequest->clear();
+    licenseRequest->appendArray(
+            reinterpret_cast<const uint8_t*>(requestJson.string()),
+            requestJson.size());
+    return android::OK;
+}
+
+android::status_t InitDataParser::parsePssh(const Vector<uint8_t>& initData,
+        Vector<const uint8_t*>* keyIds) {
+    size_t readPosition = 0;
+
+    // Validate size field
+    uint32_t expectedSize = initData.size();
+    expectedSize = htonl(expectedSize);
+    if (memcmp(&initData[readPosition], &expectedSize,
+               sizeof(expectedSize)) != 0) {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+    readPosition += sizeof(expectedSize);
+
+    // Validate PSSH box identifier
+    const char psshIdentifier[4] = {'p', 's', 's', 'h'};
+    if (memcmp(&initData[readPosition], psshIdentifier,
+               sizeof(psshIdentifier)) != 0) {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+    readPosition += sizeof(psshIdentifier);
+
+    // Validate EME version number
+    const uint8_t psshVersion1[4] = {1, 0, 0, 0};
+    if (memcmp(&initData[readPosition], psshVersion1,
+               sizeof(psshVersion1)) != 0) {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+    readPosition += sizeof(psshVersion1);
+
+    // Validate system ID
+    if (!isClearKeyUUID(&initData[readPosition])) {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+    readPosition += kSystemIdSize;
+
+    // Read key ID count
+    uint32_t keyIdCount;
+    memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount));
+    keyIdCount = ntohl(keyIdCount);
+    readPosition += sizeof(keyIdCount);
+    if (readPosition + (keyIdCount * kKeyIdSize) !=
+            initData.size() - sizeof(uint32_t)) {
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    // Calculate the key ID offsets
+    for (uint32_t i = 0; i < keyIdCount; ++i) {
+        size_t keyIdPosition = readPosition + (i * kKeyIdSize);
+        keyIds->push(&initData[keyIdPosition]);
+    }
+    return android::OK;
+}
+
+String8 InitDataParser::generateRequest(const Vector<const uint8_t*>& keyIds) {
+    const String8 kRequestPrefix("{\"kids\":[");
+    const String8 kRequestSuffix("],\"type\":\"temporary\"}");
+    const String8 kBase64Padding("=");
+
+    String8 request(kRequestPrefix);
+    AString encodedId;
+    for (size_t i = 0; i < keyIds.size(); ++i) {
+        encodedId.clear();
+        android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId);
+        if (i != 0) {
+            request.append(",");
+        }
+        request.appendFormat("\"%s\"", encodedId.c_str());
+    }
+    request.append(kRequestSuffix);
+
+    // Android's Base64 encoder produces padding. EME forbids padding.
+    request.removeAll(kBase64Padding);
+    return request;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.h b/drm/mediadrm/plugins/clearkey/InitDataParser.h
new file mode 100644
index 0000000..9505d2a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_INIT_DATA_PARSER_H_
+#define CLEARKEY_INIT_DATA_PARSER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace clearkeydrm {
+
+class InitDataParser {
+public:
+    InitDataParser() {}
+
+    android::status_t parse(const android::Vector<uint8_t>& initData,
+            const android::String8& initDataType,
+            android::Vector<uint8_t>* licenseRequest);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(InitDataParser);
+
+    android::status_t parsePssh(const android::Vector<uint8_t>& initData,
+            android::Vector<const uint8_t*>* keyIds);
+
+    android::String8 generateRequest(
+            const android::Vector<const uint8_t*>& keyIds);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_INIT_DATA_PARSER_H_
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp
new file mode 100644
index 0000000..53ffae4
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 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 "JsonWebKey"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+namespace {
+const android::String8 kKeysTag("keys");
+const android::String8 kKeyTypeTag("kty");
+const android::String8 kSymmetricKeyValue("oct");
+const android::String8 kKeyTag("k");
+const android::String8 kKeyIdTag("kid");
+const android::String8 kBase64Padding("=");
+}
+
+namespace clearkeydrm {
+
+using android::ABuffer;
+using android::AString;
+
+JsonWebKey::JsonWebKey() {
+}
+
+JsonWebKey::~JsonWebKey() {
+}
+
+/*
+ * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key
+ * pairs from the JSON Web Key Set. Both key ids and keys are base64url
+ * encoded. The KeyMap contains base64url decoded key id:key pairs.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet,
+        KeyMap* keys) {
+
+    keys->clear();
+    if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) {
+        return false;
+    }
+
+    // mJsonObjects[0] contains the entire JSON Web Key Set, including
+    // all the base64 encoded keys. Each key is also stored separately as
+    // a JSON object in mJsonObjects[1..n] where n is the total
+    // number of keys in the set.
+    if (!isJsonWebKeySet(mJsonObjects[0])) {
+        return false;
+    }
+
+    String8 encodedKey, encodedKeyId;
+    Vector<uint8_t> decodedKey, decodedKeyId;
+
+    // mJsonObjects[1] contains the first JSON Web Key in the set
+    for (size_t i = 1; i < mJsonObjects.size(); ++i) {
+        encodedKeyId.clear();
+        encodedKey.clear();
+
+        if (!parseJsonObject(mJsonObjects[i], &mTokens))
+            return false;
+
+        if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) {
+            if (encodedKeyId.isEmpty() || encodedKey.isEmpty()) {
+                ALOGE("Must have both key id and key in the JsonWebKey set.");
+                continue;
+            }
+
+            if (!decodeBase64String(encodedKeyId, &decodedKeyId)) {
+                ALOGE("Failed to decode key id(%s)", encodedKeyId.string());
+                continue;
+            }
+
+            if (!decodeBase64String(encodedKey, &decodedKey)) {
+                ALOGE("Failed to decode key(%s)", encodedKey.string());
+                continue;
+            }
+
+            keys->add(decodedKeyId, decodedKey);
+        }
+    }
+    return true;
+}
+
+bool JsonWebKey::decodeBase64String(const String8& encodedText,
+        Vector<uint8_t>* decodedText) {
+
+    decodedText->clear();
+
+    // encodedText should not contain padding characters as per EME spec.
+    if (encodedText.find(kBase64Padding) != -1) {
+        return false;
+    }
+
+    // Since android::decodeBase64() requires padding characters,
+    // add them so length of encodedText is exactly a multiple of 4.
+    int remainder = encodedText.length() % 4;
+    String8 paddedText(encodedText);
+    if (remainder > 0) {
+        for (int i = 0; i < 4 - remainder; ++i) {
+            paddedText.append(kBase64Padding);
+        }
+    }
+
+    android::sp<ABuffer> buffer =
+            android::decodeBase64(AString(paddedText.string()));
+    if (buffer == NULL) {
+        ALOGE("Malformed base64 encoded content found.");
+        return false;
+    }
+
+    decodedText->appendArray(buffer->base(), buffer->size());
+    return true;
+}
+
+bool JsonWebKey::findKey(const String8& jsonObject, String8* keyId,
+        String8* encodedKey) {
+
+    String8 key, value;
+
+    // Only allow symmetric key, i.e. "kty":"oct" pair.
+    if (jsonObject.find(kKeyTypeTag) >= 0) {
+        findValue(kKeyTypeTag, &value);
+        if (0 != value.compare(kSymmetricKeyValue))
+            return false;
+    }
+
+    if (jsonObject.find(kKeyIdTag) >= 0) {
+        findValue(kKeyIdTag, keyId);
+    }
+
+    if (jsonObject.find(kKeyTag) >= 0) {
+        findValue(kKeyTag, encodedKey);
+    }
+    return true;
+}
+
+void JsonWebKey::findValue(const String8 &key, String8* value) {
+    value->clear();
+    const char* valueToken;
+    for (Vector<String8>::const_iterator nextToken = mTokens.begin();
+        nextToken != mTokens.end(); ++nextToken) {
+        if (0 == (*nextToken).compare(key)) {
+            if (nextToken + 1 == mTokens.end())
+                break;
+            valueToken = (*(nextToken + 1)).string();
+            value->setTo(valueToken);
+            nextToken++;
+            break;
+        }
+    }
+}
+
+bool JsonWebKey::isJsonWebKeySet(const String8& jsonObject) const {
+    if (jsonObject.find(kKeysTag) == -1) {
+        ALOGE("JSON Web Key does not contain keys.");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Parses a JSON objects string and initializes a vector of tokens.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::parseJsonObject(const String8& jsonObject,
+        Vector<String8>* tokens) {
+    jsmn_parser parser;
+
+    jsmn_init(&parser);
+    int numTokens = jsmn_parse(&parser,
+        jsonObject.string(), jsonObject.size(), NULL, 0);
+    if (numTokens < 0) {
+        ALOGE("Parser returns error code=%d", numTokens);
+        return false;
+    }
+
+    unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
+    mJsmnTokens.clear();
+    mJsmnTokens.setCapacity(jsmnTokensSize);
+
+    jsmn_init(&parser);
+    int status = jsmn_parse(&parser, jsonObject.string(),
+        jsonObject.size(), mJsmnTokens.editArray(), numTokens);
+    if (status < 0) {
+        ALOGE("Parser returns error code=%d", status);
+        return false;
+    }
+
+    tokens->clear();
+    String8 token;
+    const char *pjs;
+    for (int j = 0; j < numTokens; ++j) {
+        pjs = jsonObject.string() + mJsmnTokens[j].start;
+        if (mJsmnTokens[j].type == JSMN_STRING ||
+                mJsmnTokens[j].type == JSMN_PRIMITIVE) {
+            token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start);
+            tokens->add(token);
+        }
+    }
+    return true;
+}
+
+/*
+ * Parses JSON Web Key Set string and initializes a vector of JSON objects.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::parseJsonWebKeySet(const String8& jsonWebKeySet,
+        Vector<String8>* jsonObjects) {
+    if (jsonWebKeySet.isEmpty()) {
+        ALOGE("Empty JSON Web Key");
+        return false;
+    }
+
+    // The jsmn parser only supports unicode encoding.
+    jsmn_parser parser;
+
+    // Computes number of tokens. A token marks the type, offset in
+    // the original string.
+    jsmn_init(&parser);
+    int numTokens = jsmn_parse(&parser,
+            jsonWebKeySet.string(), jsonWebKeySet.size(), NULL, 0);
+    if (numTokens < 0) {
+        ALOGE("Parser returns error code=%d", numTokens);
+        return false;
+    }
+
+    unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
+    mJsmnTokens.setCapacity(jsmnTokensSize);
+
+    jsmn_init(&parser);
+    int status = jsmn_parse(&parser, jsonWebKeySet.string(),
+            jsonWebKeySet.size(), mJsmnTokens.editArray(), numTokens);
+    if (status < 0) {
+        ALOGE("Parser returns error code=%d", status);
+        return false;
+    }
+
+    String8 token;
+    const char *pjs;
+    for (int i = 0; i < numTokens; ++i) {
+        pjs = jsonWebKeySet.string() + mJsmnTokens[i].start;
+        if (mJsmnTokens[i].type == JSMN_OBJECT) {
+            token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start);
+            jsonObjects->add(token);
+        }
+    }
+    return true;
+}
+
+}  // clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/JsonWebKey.h
new file mode 100644
index 0000000..6ae50ee
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_JSON_WEB_KEY_H_
+#define CLEARKEY_JSON_WEB_KEY_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "jsmn.h"
+#include "Utils.h"
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+using android::KeyedVector;
+using android::sp;
+using android::String8;
+using android::Vector;
+
+class JsonWebKey {
+ public:
+    JsonWebKey();
+    virtual ~JsonWebKey();
+
+    bool extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet,
+            KeyMap* keys);
+
+ private:
+    Vector<jsmntok_t> mJsmnTokens;
+    Vector<String8> mJsonObjects;
+    Vector<String8> mTokens;
+
+    bool decodeBase64String(const String8& encodedText,
+            Vector<uint8_t>* decodedText);
+    bool findKey(const String8& jsonObject, String8* keyId,
+            String8* encodedKey);
+    void findValue(const String8 &key, String8* value);
+    bool isJsonWebKeySet(const String8& jsonObject) const;
+    bool parseJsonObject(const String8& jsonObject, Vector<String8>* tokens);
+    bool parseJsonWebKeySet(const String8& jsonWebKeySet, Vector<String8>* jsonObjects);
+
+    DISALLOW_EVIL_CONSTRUCTORS(JsonWebKey);
+};
+
+}  // namespace clearkeydrm
+
+#endif  // CLEARKEY_JSON_WEB_KEY_H_
diff --git a/drm/mediadrm/plugins/clearkey/Session.cpp b/drm/mediadrm/plugins/clearkey/Session.cpp
new file mode 100644
index 0000000..95016f5
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Session.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+#include "Session.h"
+
+#include "AesCtrDecryptor.h"
+#include "InitDataParser.h"
+#include "JsonWebKey.h"
+
+namespace clearkeydrm {
+
+using android::Mutex;
+using android::String8;
+using android::Vector;
+using android::status_t;
+
+status_t Session::getKeyRequest(
+        const Vector<uint8_t>& initData,
+        const String8& initDataType,
+        Vector<uint8_t>* keyRequest) const {
+    InitDataParser parser;
+    return parser.parse(initData, initDataType, keyRequest);
+}
+
+status_t Session::provideKeyResponse(const Vector<uint8_t>& response) {
+    String8 responseString(
+            reinterpret_cast<const char*>(response.array()), response.size());
+    KeyMap keys;
+
+    Mutex::Autolock lock(mMapLock);
+    JsonWebKey parser;
+    if (parser.extractKeysFromJsonWebKeySet(responseString, &keys)) {
+        for (size_t i = 0; i < keys.size(); ++i) {
+            const KeyMap::key_type& keyId = keys.keyAt(i);
+            const KeyMap::value_type& key = keys.valueAt(i);
+            mKeyMap.add(keyId, key);
+        }
+        return android::OK;
+    } else {
+        return android::ERROR_DRM_UNKNOWN;
+    }
+}
+
+status_t Session::decrypt(
+        const KeyId keyId, const Iv iv, const void* source,
+        void* destination, const SubSample* subSamples,
+        size_t numSubSamples, size_t* bytesDecryptedOut) {
+    Mutex::Autolock lock(mMapLock);
+
+    Vector<uint8_t> keyIdVector;
+    keyIdVector.appendArray(keyId, kBlockSize);
+    if (mKeyMap.indexOfKey(keyIdVector) < 0) {
+        return android::ERROR_DRM_NO_LICENSE;
+    }
+
+    const Vector<uint8_t>& key = mKeyMap.valueFor(keyIdVector);
+    AesCtrDecryptor decryptor;
+    return decryptor.decrypt(
+            key, iv,
+            reinterpret_cast<const uint8_t*>(source),
+            reinterpret_cast<uint8_t*>(destination), subSamples,
+            numSubSamples, bytesDecryptedOut);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/Session.h b/drm/mediadrm/plugins/clearkey/Session.h
new file mode 100644
index 0000000..cab0dc3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Session.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_SESSION_H_
+#define CLEARKEY_SESSION_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "ClearKeyTypes.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class Session : public android::RefBase {
+public:
+    explicit Session(const android::Vector<uint8_t>& sessionId)
+            : mSessionId(sessionId) {}
+    virtual ~Session() {}
+
+    const android::Vector<uint8_t>& sessionId() const { return mSessionId; }
+
+    android::status_t getKeyRequest(
+            const android::Vector<uint8_t>& initData,
+            const android::String8& initDataType,
+            android::Vector<uint8_t>* keyRequest) const;
+
+    android::status_t provideKeyResponse(
+            const android::Vector<uint8_t>& response);
+
+    android::status_t decrypt(
+            const KeyId keyId, const Iv iv, const void* source,
+            void* destination, const SubSample* subSamples,
+            size_t numSubSamples, size_t* bytesDecryptedOut);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(Session);
+
+    const android::Vector<uint8_t> mSessionId;
+
+    android::Mutex mMapLock;
+    KeyMap mKeyMap;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_SESSION_H_
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
new file mode 100644
index 0000000..d047c53
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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 "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/String8.h>
+
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+using android::Mutex;
+using android::sp;
+using android::String8;
+using android::Vector;
+
+Mutex SessionLibrary::sSingletonLock;
+SessionLibrary* SessionLibrary::sSingleton = NULL;
+
+SessionLibrary* SessionLibrary::get() {
+    Mutex::Autolock lock(sSingletonLock);
+
+    if (sSingleton == NULL) {
+        ALOGD("Instantiating Session Library Singleton.");
+        sSingleton = new SessionLibrary();
+    }
+
+    return sSingleton;
+}
+
+const sp<Session>& SessionLibrary::createSession() {
+    Mutex::Autolock lock(mSessionsLock);
+
+    String8 sessionIdString = String8::format("%u", mNextSessionId);
+    mNextSessionId += 1;
+    Vector<uint8_t> sessionId;
+    sessionId.appendArray(
+            reinterpret_cast<const uint8_t*>(sessionIdString.string()),
+            sessionIdString.size());
+
+    mSessions.add(sessionId, new Session(sessionId));
+    return mSessions.valueFor(sessionId);
+}
+
+const sp<Session>& SessionLibrary::findSession(
+        const Vector<uint8_t>& sessionId) {
+    Mutex::Autolock lock(mSessionsLock);
+    return mSessions.valueFor(sessionId);
+}
+
+const sp<Session>& SessionLibrary::findSession(
+        const void* data, size_t size) {
+    Vector<uint8_t> sessionId;
+    sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size);
+    return findSession(sessionId);
+}
+
+void SessionLibrary::destroySession(const sp<Session>& session) {
+    Mutex::Autolock lock(mSessionsLock);\
+    mSessions.removeItem(session->sessionId());
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
new file mode 100644
index 0000000..56c8828
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_SESSION_LIBRARY_H_
+#define CLEARKEY_SESSION_LIBRARY_H_
+
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "Session.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class SessionLibrary {
+public:
+    static SessionLibrary* get();
+
+    const android::sp<Session>& createSession();
+
+    const android::sp<Session>& findSession(
+            const android::Vector<uint8_t>& sessionId);
+
+    const android::sp<Session>& findSession(const void* data, size_t size);
+
+    void destroySession(const android::sp<Session>& session);
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(SessionLibrary);
+
+    SessionLibrary() : mNextSessionId(1) {}
+
+    static android::Mutex sSingletonLock;
+    static SessionLibrary* sSingleton;
+
+    android::Mutex mSessionsLock;
+    uint32_t mNextSessionId;
+    android::KeyedVector<android::Vector<uint8_t>, android::sp<Session> >
+            mSessions;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_SESSION_LIBRARY_H_
diff --git a/drm/mediadrm/plugins/clearkey/Utils.cpp b/drm/mediadrm/plugins/clearkey/Utils.cpp
new file mode 100644
index 0000000..93c643b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Utils.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+namespace android {
+
+bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
+    if (lhs.size() < rhs.size()) {
+        return true;
+    } else if (lhs.size() > rhs.size()) {
+        return false;
+    }
+    return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
+}
+
+} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/Utils.h b/drm/mediadrm/plugins/clearkey/Utils.h
new file mode 100644
index 0000000..2543124
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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 CLEARKEY_UTILS_H_
+#define CLEARKEY_UTILS_H_
+
+#include <utils/Vector.h>
+
+// Add a comparison operator for this Vector specialization so that it can be
+// used as a key in a KeyedVector.
+namespace android {
+
+bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs);
+
+} // namespace android
+
+#define UNUSED(x) (void)(x);
+
+#endif // CLEARKEY_UTILS_H_
diff --git a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp
new file mode 100644
index 0000000..039e402
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "AesCtrDecryptor.h"
+
+namespace clearkeydrm {
+
+using namespace android;
+
+class AesCtrDecryptorTest : public ::testing::Test {
+  protected:
+    typedef uint8_t Key[kBlockSize];
+
+    status_t attemptDecrypt(const Key& key, const Iv& iv, const uint8_t* source,
+                            uint8_t* destination, const SubSample* subSamples,
+                            size_t numSubSamples, size_t* bytesDecryptedOut) {
+        Vector<uint8_t> keyVector;
+        keyVector.appendArray(key, kBlockSize);
+
+        AesCtrDecryptor decryptor;
+        return decryptor.decrypt(keyVector, iv, source, destination, subSamples,
+                                 numSubSamples, bytesDecryptedOut);
+    }
+
+    template <size_t totalSize>
+    void attemptDecryptExpectingSuccess(const Key& key, const Iv& iv,
+                                        const uint8_t* encrypted,
+                                        const uint8_t* decrypted,
+                                        const SubSample* subSamples,
+                                        size_t numSubSamples) {
+        uint8_t outputBuffer[totalSize] = {};
+        size_t bytesDecrypted = 0;
+        ASSERT_EQ(android::OK, attemptDecrypt(key, iv, encrypted, outputBuffer,
+                                              subSamples, numSubSamples,
+                                              &bytesDecrypted));
+        EXPECT_EQ(totalSize, bytesDecrypted);
+        EXPECT_EQ(0, memcmp(outputBuffer, decrypted, totalSize));
+    }
+};
+
+TEST_F(AesCtrDecryptorTest, DecryptsContiguousEncryptedBlock) {
+    const size_t kTotalSize = 64;
+    const size_t kNumSubsamples = 1;
+
+    // Test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+        0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+        0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+        0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+        0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {0, 64}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsAlignedBifurcatedEncryptedBlock) {
+    const size_t kTotalSize = 64;
+    const size_t kNumSubsamples = 2;
+
+    // Test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+        0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+        0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+        0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+        0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {0, 32},
+        {0, 32}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsUnalignedBifurcatedEncryptedBlock) {
+    const size_t kTotalSize = 64;
+    const size_t kNumSubsamples = 2;
+
+    // Test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+        0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+        0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+        0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+        0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {0, 29},
+        {0, 35}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsOneMixedSubSample) {
+    const size_t kTotalSize = 72;
+    const size_t kNumSubsamples = 1;
+
+    // Based on test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        // 8 clear bytes
+        0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+        // 64 encrypted bytes
+        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+        0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+        0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+        0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+        0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {8, 64}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsAlignedMixedSubSamples) {
+    const size_t kTotalSize = 80;
+    const size_t kNumSubsamples = 2;
+
+    // Based on test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        // 8 clear bytes
+        0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+        // 32 encrypted bytes
+        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+        // 8 clear bytes
+        0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+        // 32 encrypted bytes
+        0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+        0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+        0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+        0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+        0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {8, 32},
+        {8, 32}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsUnalignedMixedSubSamples) {
+    const size_t kTotalSize = 80;
+    const size_t kNumSubsamples = 2;
+
+    // Based on test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        // 8 clear bytes
+        0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+        // 30 encrypted bytes
+        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff,
+        // 8 clear bytes
+        0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+        // 34 encrypted bytes
+        0xfd, 0xff, 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5,
+        0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0,
+        0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe,
+        0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00,
+        0x9c, 0xee
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+        0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x94, 0xba,
+        0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55, 0x8e, 0x51,
+        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+        0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+        0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {8, 30},
+        {8, 34}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsComplexMixedSubSamples) {
+    const size_t kTotalSize = 72;
+    const size_t kNumSubsamples = 6;
+
+    // Based on test vectors from NIST-800-38A
+    Key key = {
+        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+        0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+    };
+
+    Iv iv = {
+        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+    };
+
+    uint8_t encrypted[kTotalSize] = {
+        // 4 clear bytes
+        0xf0, 0x13, 0xca, 0xc7,
+        // 1 encrypted bytes
+        0x87,
+        // 9 encrypted bytes
+        0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b,
+        0xef,
+        // 11 clear bytes
+        0x81, 0x4f, 0x24, 0x87, 0x0e, 0xde, 0xba, 0xad,
+        0x11, 0x9b, 0x46,
+        // 20 encrypted bytes
+        0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+        0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+        0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff,
+        // 8 clear bytes
+        0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+        // 3 clear bytes
+        0x10, 0xf5, 0x22,
+        // 14 encrypted bytes
+        0xfd, 0xff, 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5,
+        0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
+        // 2 clear bytes
+        0x02, 0x01
+    };
+
+    uint8_t decrypted[kTotalSize] = {
+        0xf0, 0x13, 0xca, 0xc7, 0x6b, 0xc1, 0xbe, 0xe2,
+        0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x81, 0x4f,
+        0x24, 0x87, 0x0e, 0xde, 0xba, 0xad, 0x11, 0x9b,
+        0x46, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae,
+        0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e,
+        0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x94, 0xba, 0x88,
+        0x2e, 0x0e, 0x12, 0x11, 0x55, 0x10, 0xf5, 0x22,
+        0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c,
+        0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x02, 0x01
+    };
+
+    SubSample subSamples[kNumSubsamples] = {
+        {4, 1},
+        {0, 9},
+        {11, 20},
+        {8, 0},
+        {3, 14},
+        {2, 0}
+    };
+
+    attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+                                               subSamples, kNumSubsamples);
+}
+
+}  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.mk b/drm/mediadrm/plugins/clearkey/tests/Android.mk
new file mode 100644
index 0000000..ac5bb21
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2014 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.
+#
+# ----------------------------------------------------------------
+# Builds ClearKey Drm Tests
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ClearKeyDrmUnitTest
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    AesCtrDecryptorUnittest.cpp \
+    InitDataParserUnittest.cpp \
+    JsonWebKeyUnittest.cpp \
+
+LOCAL_C_INCLUDES := \
+    bionic \
+    external/gtest/include \
+    external/jsmn \
+    external/openssl/include \
+    external/stlport/stlport \
+    frameworks/av/drm/mediadrm/plugins/clearkey \
+    frameworks/av/include \
+    frameworks/native/include \
+
+LOCAL_STATIC_LIBRARIES := \
+    libgtest \
+    libgtest_main \
+
+LOCAL_SHARED_LIBRARIES := \
+    libcrypto \
+    libdrmclearkeyplugin \
+    liblog \
+    libstagefright_foundation \
+    libstlport \
+    libutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
new file mode 100644
index 0000000..4ba65ed
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "InitDataParser.h"
+
+namespace clearkeydrm {
+
+using namespace android;
+
+namespace {
+    const size_t kKeyIdSize = 16;
+    const String8 kCencType("cenc");
+    const String8 kWebMType("webm");
+    const String8 kBase64Padding("=");
+}
+
+class InitDataParserTest : public ::testing::Test {
+  protected:
+    status_t attemptParse(const Vector<uint8_t>& initData,
+                          const String8& initDataType,
+                          Vector<uint8_t>* licenseRequest) {
+        InitDataParser parser;
+        return parser.parse(initData, initDataType, licenseRequest);
+    }
+
+    void attemptParseExpectingSuccess(const Vector<uint8_t>& initData,
+                                      const String8& initDataType,
+                                      const Vector<String8>& expectedKeys) {
+        const String8 kRequestPrefix("{\"kids\":[");
+        const String8 kRequestSuffix("],\"type\":\"temporary\"}");
+        Vector<uint8_t> request;
+        ASSERT_EQ(android::OK, attemptParse(initData, initDataType, &request));
+
+        String8 requestString(reinterpret_cast<const char*>(request.array()),
+                              request.size());
+        EXPECT_EQ(0, requestString.find(kRequestPrefix));
+        EXPECT_EQ(requestString.size() - kRequestSuffix.size(),
+                  requestString.find(kRequestSuffix));
+        for (size_t i = 0; i < expectedKeys.size(); ++i) {
+            AString encodedIdAString;
+            android::encodeBase64(expectedKeys[i], kKeyIdSize,
+                                  &encodedIdAString);
+            String8 encodedId(encodedIdAString.c_str());
+            encodedId.removeAll(kBase64Padding);
+            EXPECT_TRUE(requestString.contains(encodedId));
+        }
+    }
+
+    void attemptParseExpectingFailure(const Vector<uint8_t>& initData,
+                                      const String8& initDataType) {
+        Vector<uint8_t> request;
+        ASSERT_NE(android::OK, attemptParse(initData, initDataType, &request));
+        EXPECT_EQ(0, request.size());
+    }
+};
+
+TEST_F(InitDataParserTest, ParsesSingleKeyPssh) {
+    uint8_t pssh[52] = {
+        0, 0, 0, 52,                                    // Total Size
+        'p', 's', 's', 'h',                             // PSSH
+        1, 0, 0, 0,                                     // Version
+        0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+        0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+        0, 0, 0, 1,                                     // Key Count
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+        0, 0, 0, 0                                      // Data Size (always 0)
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 52);
+
+    Vector<String8> expectedKeys;
+    expectedKeys.push(String8("01234567890ABCDE"));
+
+    attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, ParsesMultipleKeyPssh) {
+    uint8_t pssh[84] = {
+        0, 0, 0, 84,                                    // Total Size
+        'p', 's', 's', 'h',                             // PSSH
+        1, 0, 0, 0,                                     // Version
+        0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+        0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+        0, 0, 0, 3,                                     // Key Count
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+        0x43, 0x6c, 0x65, 0x61, 0x72, 0x4b, 0x65, 0x79, // Key ID #2
+        0x43, 0x6c, 0x65, 0x61, 0x72, 0x4b, 0x65, 0x79, //   "ClearKeyClearKey"
+        0x20, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x20, // Key ID #3
+        0x20, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x20, //   " GOOGLE  GOOGLE "
+        0, 0, 0, 0                                      // Data Size (always 0)
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 84);
+
+    Vector<String8> expectedKeys;
+    expectedKeys.push(String8("01234567890ABCDE"));
+    expectedKeys.push(String8("ClearKeyClearKey"));
+    expectedKeys.push(String8(" GOOGLE  GOOGLE "));
+
+    attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, ParsesWebM) {
+    uint8_t initDataRaw[16] = {
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(initDataRaw, 16);
+
+    Vector<String8> expectedKeys;
+    expectedKeys.push(String8("01234567890ABCDE"));
+
+    attemptParseExpectingSuccess(initData, kWebMType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshTooSmall) {
+    uint8_t pssh[16] = {
+        0, 0, 0, 52,
+        'p', 's', 's', 'h',
+        1, 0, 0, 0,
+        0x10, 0x77, 0xef, 0xec
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 16);
+
+    attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForWebMTooSmall) {
+    uint8_t initDataRaw[8] = {
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(initDataRaw, 8);
+
+    attemptParseExpectingFailure(initData, kWebMType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadSystemId) {
+    uint8_t pssh[52] = {
+        0, 0, 0, 52,                                    // Total Size
+        'p', 's', 's', 'h',                             // PSSH
+        1, 0, 0, 0,                                     // Version
+        0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, // System ID
+        0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+        0, 0, 0, 1,                                     // Key Count
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+        0, 0, 0, 0                                      // Data Size (always 0)
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 52);
+
+    attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadSize) {
+    uint8_t pssh[52] = {
+        0, 0, 70, 200,                                  // Total Size
+        'p', 's', 's', 'h',                             // PSSH
+        1, 0, 0, 0,                                     // Version
+        0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+        0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+        0, 0, 0, 1,                                     // Key Count
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+        0, 0, 0, 0                                      // Data Size (always 0)
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 52);
+
+    attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshWrongVersion) {
+    uint8_t pssh[52] = {
+        0, 0, 0, 52,                                    // Total Size
+        'p', 's', 's', 'h',                             // PSSH
+        0, 0, 0, 0,                                     // Version
+        0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+        0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+        0, 0, 0, 1,                                     // Key Count
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+        0, 0, 0, 0                                      // Data Size (always 0)
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 52);
+
+    attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadKeyCount) {
+    uint8_t pssh[52] = {
+        0, 0, 0, 52,                                    // Total Size
+        'p', 's', 's', 'h',                             // PSSH
+        1, 0, 0, 0,                                     // Version
+        0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+        0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+        0, 0, 0, 7,                                     // Key Count
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+        0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, //   "01234567890ABCDE"
+        0, 0, 0, 0                                      // Data Size (always 0)
+    };
+    Vector<uint8_t> initData;
+    initData.appendArray(pssh, 52);
+
+    attemptParseExpectingFailure(initData, kCencType);
+}
+
+}  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
new file mode 100644
index 0000000..c3b0d84
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+#include "gtest/gtest.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+using android::String8;
+using android::Vector;
+
+class JsonWebKeyTest : public ::testing::Test {
+ protected:
+    JsonWebKey* jwk;
+
+    JsonWebKeyTest() {
+        jwk = new JsonWebKey;
+    }
+
+    virtual ~JsonWebKeyTest() {
+        if (jwk)
+            delete jwk;
+    }
+};
+
+void stringFromVector(const Vector<uint8_t>& input,
+        String8* converted) {
+    converted->clear();
+    if (input.isEmpty()) {
+        return;
+    }
+
+    for (size_t i = 0; i < input.size(); ++i) {
+        converted->appendFormat("%c", input.itemAt(i));
+    }
+}
+
+void verifyKeys(const KeyMap& keys, const String8* clearKeys) {
+    if (keys.isEmpty()) {
+        return;
+    }
+
+    String8 keyString;
+    for (size_t i = 0; i < keys.size(); ++i) {
+        stringFromVector(keys.valueAt(i), &keyString);
+        EXPECT_EQ(keyString, clearKeys[i]);
+    }
+}
+
+TEST_F(JsonWebKeyTest, NoSymmetricKey) {
+    const String8 js(
+            "{"
+                "[{"
+                    "\"kty\":\"rsa\","
+                    "\"alg\":\"A128KW1\","
+                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\","
+                    "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\""
+                "}]"
+          "}");
+
+    KeyMap keys;
+    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, NoKeysTag) {
+    const String8 js(
+            "{"
+                "[{"
+                    "\"kty\":\"oct\","
+                    "\"alg\":\"A128KW1\","
+                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\","
+                    "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\""
+                "},"
+                "{"
+                    "\"kty\":\"oct\","
+                    "\"alg\":\"A128KW2\","
+                    "\"k\":\"R29vZCBkYXkh\","
+                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+                "}]"
+            "}");
+
+    KeyMap keys;
+    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, NoKeyId) {
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "[{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kISE=\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW2\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+                    "}]"
+            "}");
+
+    KeyMap keys;
+    EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.size() == 1);
+
+    const String8 clearKeys("Good day!");
+    verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, NoKey) {
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "[{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"kid\":\"`\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW2\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+                    "}]"
+            "}");
+
+    KeyMap keys;
+    EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.size() == 1);
+
+    const String8 clearKeys("Good day!");
+    verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, MalformedKey) {
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "[{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"k\":\"GawgguFyGrWKav7AX4V???\""
+                        "\"kid\":\"67ef0gd8pvfd0=\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"k\":\"GawgguFyGrWKav7AX4V???\""
+                        "\"kid\":"
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        ":\"GawgguFyGrWKav7AX4V???\""
+                        "\"kid\":\"67ef0gd8pvfd0=\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW3\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                    "}]"
+            "}");
+
+    KeyMap keys;
+    EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.size() == 1);
+
+    const String8 clearKeys("Good day!");
+    verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, EmptyJsonWebKey) {
+    const String8 js;
+    KeyMap keys;
+    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, MalformedJsonWebKey) {
+    // Missing begin array '['
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"k\":\"GawgguFyGrWKav7AX4VKUg\""
+                        "\"kid\":\"67ef0gd8pvfd0=\""
+                    "}"
+            "]"
+            "}");
+
+    KeyMap keys;
+    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+    EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, SameKeyId) {
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "[{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW3\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                    "}]"
+            "}");
+
+    KeyMap keys;
+    jwk->extractKeysFromJsonWebKeySet(js, &keys);
+    EXPECT_TRUE(keys.size() == 2);
+
+    const String8 clearKeys[] =
+            { String8("Hello Friend!"), String8("Good day!") };
+    verifyKeys(keys, clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, ExtractWellFormedKeys) {
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "[{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW2\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW3\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                    "}]"
+            "}");
+
+    KeyMap keys;
+    jwk->extractKeysFromJsonWebKeySet(js, &keys);
+    EXPECT_TRUE(keys.size() == 2);
+
+    const String8 clearKeys[] =
+            { String8("Hello Friend!"), String8("Good day!") };
+    verifyKeys(keys, clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, ExtractKeys) {
+    const String8 js(
+            "{"
+                "\"keys\":"
+                    "[{"
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW1\""
+                    "}"
+                    "{"
+                        "\"kty\":\"oct\""
+                        "\"alg\":\"A128KW2\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+                    "}"
+                    "{"
+                        "\"kty\":\"rsa\""
+                        "\"alg\":\"A128KW-rsa\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                        "\"kid\":\"rsa-67ef0gd8pvfd0=\""
+                    "}"
+                    "{"
+                        "\"alg\":\"A128KW3\""
+                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+                        "\"k\":\"R29vZCBkYXkh\""
+                        "\"kty\":\"oct\""
+                    "}]"
+            "}");
+
+    KeyMap keys;
+    jwk->extractKeysFromJsonWebKeySet(js, &keys);
+    EXPECT_TRUE(keys.size() == 3);
+
+    const String8 clearKeys[] =
+            { String8("Hello Friend!!"), String8("Hello Friend!"),
+              String8("Good day!") };
+    verifyKeys(keys, clearKeys);
+}
+
+}  // namespace clearkeydrm
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 79682b8..2b60842 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -74,6 +74,10 @@
                                 const String16& clientPackageName,
                                 int clientUid);
 
+    static  status_t  connectLegacy(int cameraId, int halVersion,
+                                     const String16& clientPackageName,
+                                     int clientUid, sp<Camera>& camera);
+
             virtual     ~Camera();
 
             status_t    reconnect();
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index d7e5dac..f7f06bb 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -52,6 +52,7 @@
         GET_CAMERA_VENDOR_TAG_DESCRIPTOR,
         GET_LEGACY_PARAMETERS,
         SUPPORTS_CAMERA_API,
+        CONNECT_LEGACY,
     };
 
     enum {
@@ -63,6 +64,10 @@
         API_VERSION_2 = 2,
     };
 
+    enum {
+        CAMERA_HAL_API_VERSION_UNSPECIFIED = -1
+      };
+
 public:
     DECLARE_META_INTERFACE(CameraService);
 
@@ -125,6 +130,18 @@
      */
     virtual status_t supportsCameraApi(
             int cameraId, int apiVersion) = 0;
+
+    /**
+     * Connect the device as a legacy device for a given HAL version.
+     * For halVersion, use CAMERA_API_DEVICE_VERSION_* for a particular
+     * version, or CAMERA_HAL_API_VERSION_UNSPECIFIED for a service-selected version.
+     */
+    virtual status_t connectLegacy(const sp<ICameraClient>& cameraClient,
+            int cameraId, int halVersion,
+            const String16& clientPackageName,
+            int clientUid,
+            /*out*/
+            sp<ICamera>& device) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 6a68c94..f9c7efd 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -240,6 +240,11 @@
      */
             uint32_t    getSampleRate() const   { return mSampleRate; }
 
+    /* Return the notification frame count.
+     * This is approximately how often the callback is invoked, for transfer type TRANSFER_CALLBACK.
+     */
+            size_t      notificationFrames() const  { return mNotificationFramesAct; }
+
     /* Sets marker position. When record reaches the number of frames specified,
      * a callback with event type EVENT_MARKER is called. Calling setMarkerPosition
      * with marker == 0 cancels marker notification callback.
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index a4722cb..e1aab41 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -99,6 +99,8 @@
     // to be non-zero if status == NO_ERROR
     static status_t getOutputSamplingRate(uint32_t* samplingRate,
             audio_stream_type_t stream);
+    static status_t getOutputSamplingRateForAttr(uint32_t* samplingRate,
+                const audio_attributes_t *attr);
     static status_t getOutputFrameCount(size_t* frameCount,
             audio_stream_type_t stream);
     static status_t getOutputLatency(uint32_t* latency,
@@ -106,7 +108,7 @@
     static status_t getSamplingRate(audio_io_handle_t output,
                                           uint32_t* samplingRate);
     // returns the number of frames per audio HAL write buffer. Corresponds to
-    // audio_stream->get_buffer_size()/audio_stream_frame_size()
+    // audio_stream->get_buffer_size()/audio_stream_out_frame_size()
     static status_t getFrameCount(audio_io_handle_t output,
                                   size_t* frameCount);
     // returns the audio output stream latency in ms. Corresponds to
@@ -212,7 +214,12 @@
                                         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);
-
+    static audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+                                        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);
     static status_t startOutput(audio_io_handle_t output,
                                 audio_stream_type_t stream,
                                 int session);
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 79db323..a3cc396 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -188,7 +188,8 @@
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
                                     int uid = -1,
-                                    pid_t pid = -1);
+                                    pid_t pid = -1,
+                                    const audio_attributes_t* pAttributes = NULL);
 
     /* Creates an audio track and registers it with AudioFlinger.
      * With this constructor, the track is configured for static buffer mode.
@@ -214,7 +215,8 @@
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
                                     int uid = -1,
-                                    pid_t pid = -1);
+                                    pid_t pid = -1,
+                                    const audio_attributes_t* pAttributes = NULL);
 
     /* Terminates the AudioTrack and unregisters it from AudioFlinger.
      * Also destroys all resources associated with the AudioTrack.
@@ -253,7 +255,8 @@
                             transfer_type transferType = TRANSFER_DEFAULT,
                             const audio_offload_info_t *offloadInfo = NULL,
                             int uid = -1,
-                            pid_t pid = -1);
+                            pid_t pid = -1,
+                            const audio_attributes_t* pAttributes = NULL);
 
     /* Result of constructing the AudioTrack. This must be checked for successful initialization
      * before using any AudioTrack API (except for set()), because using
@@ -586,6 +589,11 @@
                         AudioTrack(const AudioTrack& other);
             AudioTrack& operator = (const AudioTrack& other);
 
+            void        setAttributesFromStreamType(audio_stream_type_t streamType);
+            void        setStreamTypeFromAttributes(audio_attributes_t& aa);
+    /* paa is guaranteed non-NULL */
+            bool        isValidAttributes(const audio_attributes_t *paa);
+
     /* a small internal class to handle the callback */
     class AudioTrackThread : public Thread
     {
@@ -626,6 +634,8 @@
             nsecs_t processAudioBuffer();
 
             bool     isOffloaded() const;
+            bool     isDirect() const;
+            bool     isOffloadedOrDirect() const;
 
             // caller must hold lock on mLock for all _l methods
 
@@ -642,6 +652,13 @@
             bool     isOffloaded_l() const
                 { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
 
+            bool     isOffloadedOrDirect_l() const
+                { return (mFlags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|
+                                                AUDIO_OUTPUT_FLAG_DIRECT)) != 0; }
+
+            bool     isDirect_l() const
+                { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
+
     // Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
     sp<IAudioTrack>         mAudioTrack;
     sp<IMemory>             mCblkMemory;
@@ -667,6 +684,7 @@
     transfer_type           mTransfer;
     audio_offload_info_t    mOffloadInfoCopy;
     const audio_offload_info_t* mOffloadInfo;
+    audio_attributes_t      mAttributes;
 
     // mFrameSize is equal to mFrameSizeAF for non-PCM or 16-bit PCM data.  For 8-bit PCM data, it's
     // twice as large as mFrameSize because data is expanded to 16-bit before it's stored in buffer.
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index c742810..a8f4605 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -50,6 +50,7 @@
         TRACK_TIMED   = 1,  // client requests a TimedAudioTrack
         TRACK_FAST    = 2,  // client requests a fast AudioTrack or AudioRecord
         TRACK_OFFLOAD = 4,  // client requests offload to hw codec
+        TRACK_DIRECT = 8,   // client requests a direct output
     };
     typedef uint32_t track_flags_t;
 
@@ -88,6 +89,7 @@
                                 track_flags_t *flags,
                                 pid_t tid,  // -1 means unused, otherwise must be valid non-0
                                 int *sessionId,
+                                size_t *notificationFrames,
                                 sp<IMemory>& cblk,
                                 sp<IMemory>& buffers,   // return value 0 means it follows cblk
                                 status_t *status) = 0;
@@ -96,9 +98,6 @@
      * and therefore can be cached.
      */
     virtual     uint32_t    sampleRate(audio_io_handle_t output) const = 0;
-#if 0
-    virtual     int         channelCount(audio_io_handle_t output) const = 0;
-#endif
     virtual     audio_format_t format(audio_io_handle_t output) const = 0;
     virtual     size_t      frameCount(audio_io_handle_t output) const = 0;
 
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index d422aa3..959e4c3 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -56,6 +56,12 @@
                                         audio_channel_mask_t channelMask = 0,
                                         audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                         const audio_offload_info_t *offloadInfo = NULL) = 0;
+    virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+                                            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,
+                                            const audio_offload_info_t *offloadInfo = NULL) = 0;
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
                                  int session = 0) = 0;
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 3ca3095..e756368 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -160,6 +160,9 @@
     // Playback rate expressed in permille (1000 is normal speed), saved as int32_t, with negative
     // values used for rewinding or reverse playback.
     KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300,                // set only
+
+    // Set a Parcel containing the value of a parcelled Java AudioAttribute instance
+    KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400                       // set only
 };
 
 // Keep INVOKE_ID_* in sync with MediaPlayer.java.
@@ -169,7 +172,7 @@
     INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
     INVOKE_ID_SELECT_TRACK = 4,
     INVOKE_ID_UNSELECT_TRACK = 5,
-    INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
+    INVOKE_ID_SET_VIDEO_SCALING_MODE = 6
 };
 
 // Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
@@ -259,6 +262,7 @@
             status_t        attachNewPlayer(const sp<IMediaPlayer>& player);
             status_t        reset_l();
             status_t        doSetRetransmitEndpoint(const sp<IMediaPlayer>& player);
+            status_t        checkStateForKeySet_l(int key);
 
     sp<IMediaPlayer>            mPlayer;
     thread_id_t                 mLockThreadId;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 8ec7f1c..142b7cb 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -22,6 +22,7 @@
 #include <android/native_window.h>
 #include <media/IOMX.h>
 #include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <media/stagefright/CodecBase.h>
 #include <media/stagefright/SkipCutBuffer.h>
 #include <OMX_Audio.h>
 
@@ -32,42 +33,32 @@
 struct ABuffer;
 struct MemoryDealer;
 
-struct ACodec : public AHierarchicalStateMachine {
-    enum {
-        kWhatFillThisBuffer      = 'fill',
-        kWhatDrainThisBuffer     = 'drai',
-        kWhatEOS                 = 'eos ',
-        kWhatShutdownCompleted   = 'scom',
-        kWhatFlushCompleted      = 'fcom',
-        kWhatOutputFormatChanged = 'outC',
-        kWhatError               = 'erro',
-        kWhatComponentAllocated  = 'cAll',
-        kWhatComponentConfigured = 'cCon',
-        kWhatInputSurfaceCreated = 'isfc',
-        kWhatSignaledInputEOS    = 'seos',
-        kWhatBuffersAllocated    = 'allc',
-        kWhatOMXDied             = 'OMXd',
-    };
-
+struct ACodec : public AHierarchicalStateMachine, public CodecBase {
     ACodec();
 
-    void setNotificationMessage(const sp<AMessage> &msg);
+    virtual void setNotificationMessage(const sp<AMessage> &msg);
+
     void initiateSetup(const sp<AMessage> &msg);
-    void signalFlush();
-    void signalResume();
-    void initiateShutdown(bool keepComponentAllocated = false);
 
-    void signalSetParameters(const sp<AMessage> &msg);
-    void signalEndOfInputStream();
+    virtual void initiateAllocateComponent(const sp<AMessage> &msg);
+    virtual void initiateConfigureComponent(const sp<AMessage> &msg);
+    virtual void initiateCreateInputSurface();
+    virtual void initiateStart();
+    virtual void initiateShutdown(bool keepComponentAllocated = false);
 
-    void initiateAllocateComponent(const sp<AMessage> &msg);
-    void initiateConfigureComponent(const sp<AMessage> &msg);
-    void initiateCreateInputSurface();
-    void initiateStart();
+    virtual void signalFlush();
+    virtual void signalResume();
 
-    void signalRequestIDRFrame();
+    virtual void signalSetParameters(const sp<AMessage> &msg);
+    virtual void signalEndOfInputStream();
+    virtual void signalRequestIDRFrame();
 
-    struct PortDescription : public RefBase {
+    // AHierarchicalStateMachine implements the message handling
+    virtual void onMessageReceived(const sp<AMessage> &msg) {
+        handleMessage(msg);
+    }
+
+    struct PortDescription : public CodecBase::PortDescription {
         size_t countBuffers();
         IOMX::buffer_id bufferIDAt(size_t index) const;
         sp<ABuffer> bufferAt(size_t index) const;
@@ -117,6 +108,7 @@
         kWhatRequestIDRFrame         = 'ridr',
         kWhatSetParameters           = 'setP',
         kWhatSubmitOutputMetaDataBufferIfEOS = 'subm',
+        kWhatOMXDied                 = 'OMXd',
     };
 
     enum {
@@ -278,6 +270,7 @@
     status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
     status_t setupH263EncoderParameters(const sp<AMessage> &msg);
     status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
+    status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
     status_t setupVPXEncoderParameters(const sp<AMessage> &msg);
 
     status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
new file mode 100644
index 0000000..1bf27a6
--- /dev/null
+++ b/include/media/stagefright/CodecBase.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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 CODEC_BASE_H_
+
+#define CODEC_BASE_H_
+
+#include <stdint.h>
+#include <media/IOMX.h>
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct CodecBase : public AHandler {
+    enum {
+        kWhatFillThisBuffer      = 'fill',
+        kWhatDrainThisBuffer     = 'drai',
+        kWhatEOS                 = 'eos ',
+        kWhatShutdownCompleted   = 'scom',
+        kWhatFlushCompleted      = 'fcom',
+        kWhatOutputFormatChanged = 'outC',
+        kWhatError               = 'erro',
+        kWhatComponentAllocated  = 'cAll',
+        kWhatComponentConfigured = 'cCon',
+        kWhatInputSurfaceCreated = 'isfc',
+        kWhatSignaledInputEOS    = 'seos',
+        kWhatBuffersAllocated    = 'allc',
+    };
+
+    virtual void setNotificationMessage(const sp<AMessage> &msg) = 0;
+
+    virtual void initiateAllocateComponent(const sp<AMessage> &msg) = 0;
+    virtual void initiateConfigureComponent(const sp<AMessage> &msg) = 0;
+    virtual void initiateCreateInputSurface() = 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 void signalFlush() = 0;
+    virtual void signalResume() = 0;
+
+    virtual void signalRequestIDRFrame() = 0;
+    virtual void signalSetParameters(const sp<AMessage> &msg) = 0;
+    virtual void signalEndOfInputStream() = 0;
+
+    struct PortDescription : public RefBase {
+        virtual size_t countBuffers() = 0;
+        virtual IOMX::buffer_id bufferIDAt(size_t index) const = 0;
+        virtual sp<ABuffer> bufferAt(size_t index) const = 0;
+
+    protected:
+        PortDescription();
+        virtual ~PortDescription();
+
+    private:
+        DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
+    };
+
+protected:
+    CodecBase();
+    virtual ~CodecBase();
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(CodecBase);
+};
+
+}  // namespace android
+
+#endif  // CODEC_BASE_H_
+
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index a1e32c9..3e6eefb 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -26,9 +26,9 @@
 namespace android {
 
 struct ABuffer;
-struct ACodec;
 struct AMessage;
 struct AString;
+struct CodecBase;
 struct ICrypto;
 struct SoftwareRenderer;
 struct Surface;
@@ -195,7 +195,7 @@
     State mState;
     sp<ALooper> mLooper;
     sp<ALooper> mCodecLooper;
-    sp<ACodec> mCodec;
+    sp<CodecBase> mCodec;
     AString mComponentName;
     uint32_t mReplyID;
     uint32_t mFlags;
diff --git a/include/media/stagefright/foundation/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
index 5510b12..c3bf0ff 100644
--- a/include/media/stagefright/foundation/ABitReader.h
+++ b/include/media/stagefright/foundation/ABitReader.h
@@ -25,8 +25,10 @@
 
 namespace android {
 
-struct ABitReader {
+class ABitReader {
+public:
     ABitReader(const uint8_t *data, size_t size);
+    virtual ~ABitReader();
 
     uint32_t getBits(size_t n);
     void skipBits(size_t n);
@@ -37,18 +39,32 @@
 
     const uint8_t *data() const;
 
-private:
+protected:
     const uint8_t *mData;
     size_t mSize;
 
     uint32_t mReservoir;  // left-aligned bits
     size_t mNumBitsLeft;
 
-    void fillReservoir();
+    virtual void fillReservoir();
 
     DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
 };
 
+class NALBitReader : public ABitReader {
+public:
+    NALBitReader(const uint8_t *data, size_t size);
+
+    bool atLeastNumBitsLeft(size_t n) const;
+
+private:
+    int32_t mNumZeros;
+
+    virtual void fillReservoir();
+
+    DISALLOW_EVIL_CONSTRUCTORS(NALBitReader);
+};
+
 }  // namespace android
 
 #endif  // A_BIT_READER_H_
diff --git a/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
index d2e6b28..3bb7d75 100644
--- a/include/media/stagefright/foundation/AHierarchicalStateMachine.h
+++ b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
@@ -43,13 +43,13 @@
     DISALLOW_EVIL_CONSTRUCTORS(AState);
 };
 
-struct AHierarchicalStateMachine : public AHandler {
+struct AHierarchicalStateMachine {
     AHierarchicalStateMachine();
 
 protected:
     virtual ~AHierarchicalStateMachine();
 
-    virtual void onMessageReceived(const sp<AMessage> &msg);
+    virtual void handleMessage(const sp<AMessage> &msg);
 
     // Only to be called in response to a message.
     void changeState(const sp<AState> &state);
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
index 622028e..0edaa1c 100644
--- a/include/media/stagefright/foundation/AString.h
+++ b/include/media/stagefright/foundation/AString.h
@@ -77,6 +77,8 @@
 
     bool startsWith(const char *prefix) const;
     bool endsWith(const char *suffix) const;
+    bool startsWithIgnoreCase(const char *prefix) const;
+    bool endsWithIgnoreCase(const char *suffix) const;
 
     void tolower();
 
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index db5c78f..695767d 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -19,11 +19,13 @@
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 //#define LOG_NDEBUG 0
 
-#include <cutils/log.h>
 #include <assert.h>
+#include <inttypes.h>
+#include <new>
 #include <stdlib.h>
 #include <string.h>
-#include <new>
+
+#include <cutils/log.h>
 #include "EffectBundle.h"
 
 
@@ -560,11 +562,12 @@
             MemTab.Region[i].pBaseAddress = malloc(MemTab.Region[i].Size);
 
             if (MemTab.Region[i].pBaseAddress == LVM_NULL){
-                ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate %ld bytes "
-                        "for region %u\n", MemTab.Region[i].Size, i );
+                ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate %" PRIu32
+                        " bytes for region %u\n", MemTab.Region[i].Size, i );
                 bMallocFailure = LVM_TRUE;
             }else{
-                ALOGV("\tLvmBundle_init CreateInstance allocated %ld bytes for region %u at %p\n",
+                ALOGV("\tLvmBundle_init CreateInstance allocated %" PRIu32
+                        " bytes for region %u at %p\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
             }
         }
@@ -576,11 +579,11 @@
     if(bMallocFailure == LVM_TRUE){
         for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
             if (MemTab.Region[i].pBaseAddress == LVM_NULL){
-                ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate %ld bytes "
-                        "for region %u Not freeing\n", MemTab.Region[i].Size, i );
+                ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed to allocate %" PRIu32
+                        " bytes for region %u Not freeing\n", MemTab.Region[i].Size, i );
             }else{
-                ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed: but allocated %ld bytes "
-                     "for region %u at %p- free\n",
+                ALOGV("\tLVM_ERROR :LvmBundle_init CreateInstance Failed: but allocated %" PRIu32
+                     " bytes for region %u at %p- free\n",
                      MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
                 free(MemTab.Region[i].pBaseAddress);
             }
@@ -889,16 +892,16 @@
     for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
         if (MemTab.Region[i].Size != 0){
             if (MemTab.Region[i].pBaseAddress != NULL){
-                ALOGV("\tLvmEffect_free - START freeing %ld bytes for region %u at %p\n",
+                ALOGV("\tLvmEffect_free - START freeing %" PRIu32 " bytes for region %u at %p\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
 
                 free(MemTab.Region[i].pBaseAddress);
 
-                ALOGV("\tLvmEffect_free - END   freeing %ld bytes for region %u at %p\n",
+                ALOGV("\tLvmEffect_free - END   freeing %" PRIu32 " bytes for region %u at %p\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
             }else{
-                ALOGV("\tLVM_ERROR : LvmEffect_free - trying to free with NULL pointer %ld bytes "
-                        "for region %u at %p ERROR\n",
+                ALOGV("\tLVM_ERROR : LvmEffect_free - trying to free with NULL pointer %" PRIu32
+                        " bytes for region %u at %p ERROR\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
             }
         }
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index c6d3759..13f1a0d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -19,11 +19,13 @@
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 //#define LOG_NDEBUG 0
 
-#include <cutils/log.h>
 #include <assert.h>
+#include <inttypes.h>
+#include <new>
 #include <stdlib.h>
 #include <string.h>
-#include <new>
+
+#include <cutils/log.h>
 #include "EffectReverb.h"
 // from Reverb/lib
 #include "LVREV.h"
@@ -269,7 +271,7 @@
     pContext->InFrames32  = (LVM_INT32 *)malloc(LVREV_MAX_FRAME_SIZE * sizeof(LVM_INT32) * 2);
     pContext->OutFrames32 = (LVM_INT32 *)malloc(LVREV_MAX_FRAME_SIZE * sizeof(LVM_INT32) * 2);
 
-    ALOGV("\tEffectCreate %p, size %d", pContext, sizeof(ReverbContext));
+    ALOGV("\tEffectCreate %p, size %zu", pContext, sizeof(ReverbContext));
     ALOGV("\tEffectCreate end\n");
     return 0;
 } /* end EffectCreate */
@@ -570,15 +572,15 @@
     for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
         if (MemTab.Region[i].Size != 0){
             if (MemTab.Region[i].pBaseAddress != NULL){
-                ALOGV("\tfree() - START freeing %ld bytes for region %u at %p\n",
+                ALOGV("\tfree() - START freeing %" PRIu32 " bytes for region %u at %p\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
 
                 free(MemTab.Region[i].pBaseAddress);
 
-                ALOGV("\tfree() - END   freeing %ld bytes for region %u at %p\n",
+                ALOGV("\tfree() - END   freeing %" PRIu32 " bytes for region %u at %p\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
             }else{
-                ALOGV("\tLVM_ERROR : free() - trying to free with NULL pointer %ld bytes "
+                ALOGV("\tLVM_ERROR : free() - trying to free with NULL pointer %" PRIu32 " bytes "
                         "for region %u at %p ERROR\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
             }
@@ -771,11 +773,12 @@
             MemTab.Region[i].pBaseAddress = malloc(MemTab.Region[i].Size);
 
             if (MemTab.Region[i].pBaseAddress == LVM_NULL){
-                ALOGV("\tLVREV_ERROR :Reverb_init CreateInstance Failed to allocate %ld "
-                        "bytes for region %u\n", MemTab.Region[i].Size, i );
+                ALOGV("\tLVREV_ERROR :Reverb_init CreateInstance Failed to allocate %" PRIu32
+                        " bytes for region %u\n", MemTab.Region[i].Size, i );
                 bMallocFailure = LVM_TRUE;
             }else{
-                ALOGV("\tReverb_init CreateInstance allocate %ld bytes for region %u at %p\n",
+                ALOGV("\tReverb_init CreateInstance allocate %" PRIu32
+                        " bytes for region %u at %p\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
             }
         }
@@ -787,11 +790,11 @@
     if(bMallocFailure == LVM_TRUE){
         for (int i=0; i<LVM_NR_MEMORY_REGIONS; i++){
             if (MemTab.Region[i].pBaseAddress == LVM_NULL){
-                ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed to allocate %ld bytes "
-                        "for region %u - Not freeing\n", MemTab.Region[i].Size, i );
+                ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed to allocate %" PRIu32
+                        " bytes for region %u - Not freeing\n", MemTab.Region[i].Size, i );
             }else{
-                ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed: but allocated %ld bytes "
-                        "for region %u at %p- free\n",
+                ALOGV("\tLVM_ERROR :Reverb_init CreateInstance Failed: but allocated %" PRIu32
+                        " bytes for region %u at %p- free\n",
                         MemTab.Region[i].Size, i, MemTab.Region[i].pBaseAddress);
                 free(MemTab.Region[i].pBaseAddress);
             }
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 50b444a..3ee5809 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -18,7 +18,9 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioRecord"
 
+#include <inttypes.h>
 #include <sys/resource.h>
+
 #include <binder/IPCThreadState.h>
 #include <media/AudioRecord.h>
 #include <utils/Log.h>
@@ -468,7 +470,7 @@
         if (frameCount == 0) {
             frameCount = minFrameCount;
         } else if (frameCount < minFrameCount) {
-            ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
+            ALOGE("frameCount %zu < minFrameCount %zu", frameCount, minFrameCount);
             return BAD_VALUE;
         }
 
@@ -493,6 +495,10 @@
     size_t temp = frameCount;   // temp may be replaced by a revised value of frameCount,
                                 // but we will still need the original value also
     int originalSessionId = mSessionId;
+
+    // The notification frame count is the period between callbacks, as suggested by the server.
+    size_t notificationFrames;
+
     sp<IMemory> iMem;           // for cblk
     sp<IMemory> bufferMem;
     sp<IAudioRecord> record = audioFlinger->openRecord(input,
@@ -502,6 +508,7 @@
                                                        &trackFlags,
                                                        tid,
                                                        &mSessionId,
+                                                       &notificationFrames,
                                                        iMem,
                                                        bufferMem,
                                                        &status);
@@ -555,17 +562,17 @@
     mCblk = cblk;
     // note that temp is the (possibly revised) value of frameCount
     if (temp < frameCount || (frameCount == 0 && temp == 0)) {
-        ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp);
+        ALOGW("Requested frameCount %zu but received frameCount %zu", frameCount, temp);
     }
     frameCount = temp;
 
     mAwaitBoost = false;
     if (mFlags & AUDIO_INPUT_FLAG_FAST) {
         if (trackFlags & IAudioFlinger::TRACK_FAST) {
-            ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %u", frameCount);
+            ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu", frameCount);
             mAwaitBoost = true;
         } else {
-            ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
+            ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %zu", frameCount);
             // once denied, do not request again if IAudioRecord is re-created
             mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
         }
@@ -740,7 +747,7 @@
     if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
         // sanity-check. user is most-likely passing an error code, and it would
         // make the return value ambiguous (actualSize vs error).
-        ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)", buffer, userSize, userSize);
+        ALOGE("AudioRecord::read(buffer=%p, size=%zu (%zu)", buffer, userSize, userSize);
         return BAD_VALUE;
     }
 
@@ -921,10 +928,10 @@
         size_t nonContig;
         status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
         LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0),
-                "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
+                "obtainBuffer() err=%d frameCount=%zu", err, audioBuffer.frameCount);
         requested = &ClientProxy::kNonBlocking;
         size_t avail = audioBuffer.frameCount + nonContig;
-        ALOGV("obtainBuffer(%u) returned %u = %u + %u err %d",
+        ALOGV("obtainBuffer(%u) returned %zu = %zu + %zu err %d",
                 mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err);
         if (err != NO_ERROR) {
             if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
@@ -952,8 +959,8 @@
 
         // Sanity check on returned size
         if (ssize_t(readSize) < 0 || readSize > reqSize) {
-            ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes",
-                    reqSize, (int) readSize);
+            ALOGE("EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes",
+                    reqSize, ssize_t(readSize));
             return NS_NEVER;
         }
 
@@ -1092,7 +1099,7 @@
         ns = 1000000000LL;
         // fall through
     default:
-        LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
+        LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns);
         pauseInternal(ns);
         return true;
     }
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 15b32ff..a47d45c 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -245,6 +245,19 @@
     return getSamplingRate(output, samplingRate);
 }
 
+status_t AudioSystem::getOutputSamplingRateForAttr(uint32_t* samplingRate,
+        const audio_attributes_t *attr)
+{
+    if (attr == NULL) {
+        return BAD_VALUE;
+    }
+    audio_io_handle_t output = getOutputForAttr(attr);
+    if (output == 0) {
+        return PERMISSION_DENIED;
+    }
+    return getSamplingRate(output, samplingRate);
+}
+
 status_t AudioSystem::getSamplingRate(audio_io_handle_t output,
                                       uint32_t* samplingRate)
 {
@@ -310,7 +323,7 @@
         return BAD_VALUE;
     }
 
-    ALOGV("getFrameCount() output %d, frameCount %d", output, *frameCount);
+    ALOGV("getFrameCount() output %d, frameCount %zu", output, *frameCount);
 
     return NO_ERROR;
 }
@@ -476,7 +489,7 @@
 
         OutputDescriptor *outputDesc =  new OutputDescriptor(*desc);
         gOutputs.add(ioHandle, outputDesc);
-        ALOGV("ioConfigChanged() new output samplingRate %u, format %#x channel mask %#x frameCount %u "
+        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);
@@ -501,7 +514,7 @@
         desc = (const OutputDescriptor *)param2;
 
         ALOGV("ioConfigChanged() new config for output %d samplingRate %u, format %#x channel mask %#x "
-                "frameCount %d latency %d",
+                "frameCount %zu latency %d",
                 ioHandle, desc->samplingRate, desc->format,
                 desc->channelMask, desc->frameCount, desc->latency);
         OutputDescriptor *outputDesc = gOutputs.valueAt(index);
@@ -633,6 +646,19 @@
     return aps->getOutput(stream, samplingRate, format, channelMask, flags, offloadInfo);
 }
 
+audio_io_handle_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr,
+                                    uint32_t samplingRate,
+                                    audio_format_t format,
+                                    audio_channel_mask_t channelMask,
+                                    audio_output_flags_t flags,
+                                    const audio_offload_info_t *offloadInfo)
+{
+    if (attr == NULL) return 0;
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return 0;
+    return aps->getOutputForAttr(attr, samplingRate, format, channelMask, flags, offloadInfo);
+}
+
 status_t AudioSystem::startOutput(audio_io_handle_t output,
                                   audio_stream_type_t stream,
                                   int session)
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index e6827ee..b5c9125 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -15,12 +15,13 @@
 ** limitations under the License.
 */
 
-
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioTrack"
 
+#include <inttypes.h>
 #include <math.h>
 #include <sys/resource.h>
+
 #include <audio_utils/primitives.h>
 #include <binder/IPCThreadState.h>
 #include <media/AudioTrack.h>
@@ -89,7 +90,7 @@
                 streamType, sampleRate);
         return BAD_VALUE;
     }
-    ALOGV("getMinFrameCount=%d: afFrameCount=%d, minBufCount=%d, afSampleRate=%d, afLatency=%d",
+    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%d, afSampleRate=%d, afLatency=%d",
             *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
     return NO_ERROR;
 }
@@ -103,6 +104,10 @@
       mPreviousSchedulingGroup(SP_DEFAULT),
       mPausedPosition(0)
 {
+    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
+    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
+    mAttributes.flags = 0x0;
+    strcpy(mAttributes.tags, "");
 }
 
 AudioTrack::AudioTrack(
@@ -119,7 +124,8 @@
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
         int uid,
-        pid_t pid)
+        pid_t pid,
+        const audio_attributes_t* pAttributes)
     : mStatus(NO_INIT),
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
@@ -129,7 +135,7 @@
     mStatus = set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
             0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
-            offloadInfo, uid, pid);
+            offloadInfo, uid, pid, pAttributes);
 }
 
 AudioTrack::AudioTrack(
@@ -146,7 +152,8 @@
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
         int uid,
-        pid_t pid)
+        pid_t pid,
+        const audio_attributes_t* pAttributes)
     : mStatus(NO_INIT),
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
@@ -156,7 +163,7 @@
     mStatus = set(streamType, sampleRate, format, channelMask,
             0 /*frameCount*/, flags, cbf, user, notificationFrames,
             sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo,
-            uid, pid);
+            uid, pid, pAttributes);
 }
 
 AudioTrack::~AudioTrack()
@@ -199,7 +206,8 @@
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
         int uid,
-        pid_t pid)
+        pid_t pid,
+        const audio_attributes_t* pAttributes)
 {
     ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
           "flags #%x, notificationFrames %u, sessionId %d, transferType %d",
@@ -245,7 +253,7 @@
     ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(),
             sharedBuffer->size());
 
-    ALOGV("set() streamType %d frameCount %u flags %04x", streamType, frameCount, flags);
+    ALOGV("set() streamType %d frameCount %zu flags %04x", streamType, frameCount, flags);
 
     AutoMutex lock(mLock);
 
@@ -259,18 +267,33 @@
     if (streamType == AUDIO_STREAM_DEFAULT) {
         streamType = AUDIO_STREAM_MUSIC;
     }
-    if (uint32_t(streamType) >= AUDIO_STREAM_CNT) {
-        ALOGE("Invalid stream type %d", streamType);
-        return BAD_VALUE;
+
+    if (pAttributes == NULL) {
+        if (uint32_t(streamType) >= AUDIO_STREAM_CNT) {
+            ALOGE("Invalid stream type %d", streamType);
+            return BAD_VALUE;
+        }
+        setAttributesFromStreamType(streamType);
+        mStreamType = streamType;
+    } else {
+        if (!isValidAttributes(pAttributes)) {
+            ALOGE("Invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]",
+                pAttributes->usage, pAttributes->content_type, pAttributes->flags,
+                pAttributes->tags);
+        }
+        // stream type shouldn't be looked at, this track has audio attributes
+        memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
+        setStreamTypeFromAttributes(mAttributes);
+        ALOGV("Building AudioTrack with attributes: usage=%d content=%d flags=0x%x tags=[%s]",
+                mAttributes.usage, mAttributes.content_type, mAttributes.flags, mAttributes.tags);
     }
-    mStreamType = streamType;
 
     status_t status;
     if (sampleRate == 0) {
-        status = AudioSystem::getOutputSamplingRate(&sampleRate, streamType);
+        status = AudioSystem::getOutputSamplingRateForAttr(&sampleRate, &mAttributes);
         if (status != NO_ERROR) {
             ALOGE("Could not get output sample rate for stream type %d; status %d",
-                    streamType, status);
+                    mStreamType, status);
             return status;
         }
     }
@@ -314,7 +337,7 @@
                 ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);
     }
     // only allow deep buffering for music stream type
-    if (streamType != AUDIO_STREAM_MUSIC) {
+    if (mStreamType != AUDIO_STREAM_MUSIC) {
         flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
     }
 
@@ -615,12 +638,12 @@
 
 status_t AudioTrack::setSampleRate(uint32_t rate)
 {
-    if (mIsTimed || isOffloaded()) {
+    if (mIsTimed || isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
 
     uint32_t afSamplingRate;
-    if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) {
+    if (AudioSystem::getOutputSamplingRateForAttr(&afSamplingRate, &mAttributes) != NO_ERROR) {
         return NO_INIT;
     }
     // Resampler implementation limits input sampling rate to 2 x output sampling rate.
@@ -646,7 +669,7 @@
     // sample rate can be updated during playback by the offloaded decoder so we need to
     // query the HAL and update if needed.
 // FIXME use Proxy return channel to update the rate from server and avoid polling here
-    if (isOffloaded_l()) {
+    if (isOffloadedOrDirect_l()) {
         if (mOutput != AUDIO_IO_HANDLE_NONE) {
             uint32_t sampleRate = 0;
             status_t status = AudioSystem::getSamplingRate(mOutput, &sampleRate);
@@ -660,7 +683,7 @@
 
 status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
 {
-    if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
+    if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
 
@@ -694,7 +717,7 @@
 status_t AudioTrack::setMarkerPosition(uint32_t marker)
 {
     // The only purpose of setting marker position is to get a callback
-    if (mCbf == NULL || isOffloaded()) {
+    if (mCbf == NULL || isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
 
@@ -707,7 +730,7 @@
 
 status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
 {
-    if (isOffloaded()) {
+    if (isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
     if (marker == NULL) {
@@ -723,7 +746,7 @@
 status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
 {
     // The only purpose of setting position update period is to get a callback
-    if (mCbf == NULL || isOffloaded()) {
+    if (mCbf == NULL || isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
 
@@ -736,7 +759,7 @@
 
 status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
 {
-    if (isOffloaded()) {
+    if (isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
     if (updatePeriod == NULL) {
@@ -751,7 +774,7 @@
 
 status_t AudioTrack::setPosition(uint32_t position)
 {
-    if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
+    if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
     if (position > mFrameCount) {
@@ -784,10 +807,10 @@
     }
 
     AutoMutex lock(mLock);
-    if (isOffloaded_l()) {
+    if (isOffloadedOrDirect_l()) {
         uint32_t dspFrames = 0;
 
-        if ((mState == STATE_PAUSED) || (mState == STATE_PAUSED_STOPPING)) {
+        if (isOffloaded_l() && ((mState == STATE_PAUSED) || (mState == STATE_PAUSED_STOPPING))) {
             ALOGV("getPosition called in paused state, return cached position %u", mPausedPosition);
             *position = mPausedPosition;
             return NO_ERROR;
@@ -822,7 +845,7 @@
 
 status_t AudioTrack::reload()
 {
-    if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
+    if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
         return INVALID_OPERATION;
     }
 
@@ -867,12 +890,12 @@
         return NO_INIT;
     }
 
-    audio_io_handle_t output = AudioSystem::getOutput(mStreamType, mSampleRate, mFormat,
+    audio_io_handle_t output = AudioSystem::getOutputForAttr(&mAttributes, mSampleRate, mFormat,
             mChannelMask, mFlags, mOffloadInfo);
     if (output == AUDIO_IO_HANDLE_NONE) {
-        ALOGE("Could not get audio output for stream type %d, sample rate %u, format %#x, "
-              "channel mask %#x, flags %#x",
-              mStreamType, mSampleRate, mFormat, mChannelMask, mFlags);
+        ALOGE("Could not get audio output for stream type %d, usage %d, sample rate %u, format %#x,"
+              " channel mask %#x, flags %#x",
+              mStreamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags);
         return BAD_VALUE;
     }
     {
@@ -973,14 +996,14 @@
 
         // Ensure that buffer depth covers at least audio hardware latency
         uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
-        ALOGV("afFrameCount=%d, minBufCount=%d, afSampleRate=%u, afLatency=%d",
+        ALOGV("afFrameCount=%zu, minBufCount=%d, afSampleRate=%u, afLatency=%d",
                 afFrameCount, minBufCount, afSampleRate, afLatency);
         if (minBufCount <= nBuffering) {
             minBufCount = nBuffering;
         }
 
         size_t minFrameCount = (afFrameCount*mSampleRate*minBufCount)/afSampleRate;
-        ALOGV("minFrameCount: %u, afFrameCount=%d, minBufCount=%d, sampleRate=%u, afSampleRate=%u"
+        ALOGV("minFrameCount: %zu, afFrameCount=%zu, minBufCount=%d, sampleRate=%u, afSampleRate=%u"
                 ", afLatency=%d",
                 minFrameCount, afFrameCount, minBufCount, mSampleRate, afSampleRate, afLatency);
 
@@ -988,7 +1011,7 @@
             frameCount = minFrameCount;
         } else if (frameCount < minFrameCount) {
             // not ALOGW because it happens all the time when playing key clicks over A2DP
-            ALOGV("Minimum buffer size corrected from %d to %d",
+            ALOGV("Minimum buffer size corrected from %zu to %zu",
                      frameCount, minFrameCount);
             frameCount = minFrameCount;
         }
@@ -1018,6 +1041,10 @@
         trackFlags |= IAudioFlinger::TRACK_OFFLOAD;
     }
 
+    if (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
+        trackFlags |= IAudioFlinger::TRACK_DIRECT;
+    }
+
     size_t temp = frameCount;   // temp may be replaced by a revised value of frameCount,
                                 // but we will still need the original value also
     sp<IAudioTrack> track = audioFlinger->createTrack(mStreamType,
@@ -1071,14 +1098,14 @@
         // In current design, AudioTrack client checks and ensures frame count validity before
         // passing it to AudioFlinger so AudioFlinger should not return a different value except
         // for fast track as it uses a special method of assigning frame count.
-        ALOGW("Requested frameCount %u but received frameCount %u", frameCount, temp);
+        ALOGW("Requested frameCount %zu but received frameCount %zu", frameCount, temp);
     }
     frameCount = temp;
 
     mAwaitBoost = false;
     if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
         if (trackFlags & IAudioFlinger::TRACK_FAST) {
-            ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount);
+            ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu", frameCount);
             mAwaitBoost = true;
             if (mSharedBuffer == 0) {
                 // Theoretically double-buffering is not required for fast tracks,
@@ -1089,7 +1116,7 @@
                 }
             }
         } else {
-            ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
+            ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu", frameCount);
             // once denied, do not request again if IAudioTrack is re-created
             mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
             if (mSharedBuffer == 0) {
@@ -1109,6 +1136,16 @@
             //return NO_INIT;
         }
     }
+    if (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
+        if (trackFlags & IAudioFlinger::TRACK_DIRECT) {
+            ALOGV("AUDIO_OUTPUT_FLAG_DIRECT successful");
+        } else {
+            ALOGW("AUDIO_OUTPUT_FLAG_DIRECT denied by server");
+            mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_DIRECT);
+            // FIXME This is a warning, not an error, so don't return error status
+            //return NO_INIT;
+        }
+    }
 
     // We retain a copy of the I/O handle, but don't own the reference
     mOutput = output;
@@ -1304,6 +1341,16 @@
         return INVALID_OPERATION;
     }
 
+    if (isDirect()) {
+        AutoMutex lock(mLock);
+        int32_t flags = android_atomic_and(
+                            ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END),
+                            &mCblk->mFlags);
+        if (flags & CBLK_INVALID) {
+            return DEAD_OBJECT;
+        }
+    }
+
     if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
         // Sanity-check: user is most-likely passing an error code, and it would
         // make the return value ambiguous (actualSize vs error).
@@ -1452,7 +1499,7 @@
         // for offloaded tracks restoreTrack_l() will just update the sequence and clear
         // AudioSystem cache. We should not exit here but after calling the callback so
         // that the upper layers can recreate the track
-        if (!isOffloaded_l() || (mSequence == mObservedSequence)) {
+        if (!isOffloadedOrDirect_l() || (mSequence == mObservedSequence)) {
             status_t status = restoreTrack_l("processAudioBuffer");
             mLock.unlock();
             // Run again immediately, but with a new IAudioTrack
@@ -1578,7 +1625,7 @@
         mObservedSequence = sequence;
         mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL);
         // for offloaded tracks, just wait for the upper layers to recreate the track
-        if (isOffloaded()) {
+        if (isOffloadedOrDirect()) {
             return NS_INACTIVE;
         }
     }
@@ -1636,10 +1683,10 @@
         size_t nonContig;
         status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
         LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0),
-                "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
+                "obtainBuffer() err=%d frameCount=%zu", err, audioBuffer.frameCount);
         requested = &ClientProxy::kNonBlocking;
         size_t avail = audioBuffer.frameCount + nonContig;
-        ALOGV("obtainBuffer(%u) returned %u = %u + %u err %d",
+        ALOGV("obtainBuffer(%u) returned %zu = %zu + %zu err %d",
                 mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err);
         if (err != NO_ERROR) {
             if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR ||
@@ -1674,8 +1721,8 @@
 
         // Sanity check on returned size
         if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) {
-            ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes",
-                    reqSize, (int) writtenSize);
+            ALOGE("EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes",
+                    reqSize, ssize_t(writtenSize));
             return NS_NEVER;
         }
 
@@ -1736,7 +1783,7 @@
 status_t AudioTrack::restoreTrack_l(const char *from)
 {
     ALOGW("dead IAudioTrack, %s, creating a new one from %s()",
-          isOffloaded_l() ? "Offloaded" : "PCM", from);
+          isOffloadedOrDirect_l() ? "Offloaded or Direct" : "PCM", from);
     ++mSequence;
     status_t result;
 
@@ -1744,7 +1791,7 @@
     // output parameters in createTrack_l()
     AudioSystem::clearAudioConfigCache();
 
-    if (isOffloaded_l()) {
+    if (isOffloadedOrDirect_l()) {
         // FIXME re-creation of offloaded tracks is not yet implemented
         return DEAD_OBJECT;
     }
@@ -1830,6 +1877,19 @@
     return isOffloaded_l();
 }
 
+bool AudioTrack::isDirect() const
+{
+    AutoMutex lock(mLock);
+    return isDirect_l();
+}
+
+bool AudioTrack::isOffloadedOrDirect() const
+{
+    AutoMutex lock(mLock);
+    return isOffloadedOrDirect_l();
+}
+
+
 status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const
 {
 
@@ -1858,6 +1918,136 @@
     return mProxy->getUnderrunFrames();
 }
 
+void AudioTrack::setAttributesFromStreamType(audio_stream_type_t streamType) {
+    mAttributes.flags = 0x0;
+
+    switch(streamType) {
+    case AUDIO_STREAM_DEFAULT:
+    case AUDIO_STREAM_MUSIC:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+        mAttributes.usage = AUDIO_USAGE_MEDIA;
+        break;
+    case AUDIO_STREAM_VOICE_CALL:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+        mAttributes.usage = AUDIO_USAGE_VOICE_COMMUNICATION;
+        break;
+    case AUDIO_STREAM_ENFORCED_AUDIBLE:
+        mAttributes.flags  |= AUDIO_FLAG_AUDIBILITY_ENFORCED;
+        // intended fall through, attributes in common with STREAM_SYSTEM
+    case AUDIO_STREAM_SYSTEM:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+        mAttributes.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+        break;
+    case AUDIO_STREAM_RING:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+        mAttributes.usage = AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+        break;
+    case AUDIO_STREAM_ALARM:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+        mAttributes.usage = AUDIO_USAGE_ALARM;
+        break;
+    case AUDIO_STREAM_NOTIFICATION:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+        mAttributes.usage = AUDIO_USAGE_NOTIFICATION;
+        break;
+    case AUDIO_STREAM_BLUETOOTH_SCO:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+        mAttributes.usage = AUDIO_USAGE_VOICE_COMMUNICATION;
+        mAttributes.flags |= AUDIO_FLAG_SCO;
+        break;
+    case AUDIO_STREAM_DTMF:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+        mAttributes.usage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
+        break;
+    case AUDIO_STREAM_TTS:
+        mAttributes.content_type = AUDIO_CONTENT_TYPE_SPEECH;
+        mAttributes.usage = AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
+        break;
+    default:
+        ALOGE("invalid stream type %d when converting to attributes", streamType);
+    }
+}
+
+void AudioTrack::setStreamTypeFromAttributes(audio_attributes_t& aa) {
+    // flags to stream type mapping
+    if ((aa.flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
+        mStreamType = AUDIO_STREAM_ENFORCED_AUDIBLE;
+        return;
+    }
+    if ((aa.flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) {
+        mStreamType = AUDIO_STREAM_BLUETOOTH_SCO;
+        return;
+    }
+
+    // usage to stream type mapping
+    switch (aa.usage) {
+    case AUDIO_USAGE_MEDIA:
+    case AUDIO_USAGE_GAME:
+    case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
+    case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+        mStreamType = AUDIO_STREAM_MUSIC;
+        return;
+    case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
+        mStreamType = AUDIO_STREAM_SYSTEM;
+        return;
+    case AUDIO_USAGE_VOICE_COMMUNICATION:
+        mStreamType = AUDIO_STREAM_VOICE_CALL;
+        return;
+
+    case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+        mStreamType = AUDIO_STREAM_DTMF;
+        return;
+
+    case AUDIO_USAGE_ALARM:
+        mStreamType = AUDIO_STREAM_ALARM;
+        return;
+    case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+        mStreamType = AUDIO_STREAM_RING;
+        return;
+
+    case AUDIO_USAGE_NOTIFICATION:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+    case AUDIO_USAGE_NOTIFICATION_EVENT:
+        mStreamType = AUDIO_STREAM_NOTIFICATION;
+        return;
+
+    case AUDIO_USAGE_UNKNOWN:
+    default:
+        mStreamType = AUDIO_STREAM_MUSIC;
+    }
+}
+
+bool AudioTrack::isValidAttributes(const audio_attributes_t *paa) {
+    // has flags that map to a strategy?
+    if ((paa->flags & (AUDIO_FLAG_AUDIBILITY_ENFORCED | AUDIO_FLAG_SCO)) != 0) {
+        return true;
+    }
+
+    // has known usage?
+    switch (paa->usage) {
+    case AUDIO_USAGE_UNKNOWN:
+    case AUDIO_USAGE_MEDIA:
+    case AUDIO_USAGE_VOICE_COMMUNICATION:
+    case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+    case AUDIO_USAGE_ALARM:
+    case AUDIO_USAGE_NOTIFICATION:
+    case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+    case AUDIO_USAGE_NOTIFICATION_EVENT:
+    case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
+    case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+    case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
+    case AUDIO_USAGE_GAME:
+        break;
+    default:
+        return false;
+    }
+    return true;
+}
 // =========================================================================
 
 void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
@@ -1918,7 +2108,7 @@
         ns = 1000000000LL;
         // fall through
     default:
-        LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
+        LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns);
         pauseInternal(ns);
         return true;
     }
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 0dbfa62..eec025e 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -135,7 +135,7 @@
         // pipe should not be overfull
         if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
             if (mIsOut) {
-                ALOGE("Shared memory control block is corrupt (filled=%d, mFrameCount=%u); "
+                ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); "
                         "shutting down", filled, mFrameCount);
                 mIsShutdown = true;
                 status = NO_INIT;
@@ -338,7 +338,7 @@
     ssize_t filled = rear - front;
     // pipe should not be overfull
     if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
-        ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+        ALOGE("Shared memory control block is corrupt (filled=%zd); shutting down", filled);
         return 0;
     }
     return (size_t)filled;
@@ -555,7 +555,7 @@
     ssize_t filled = rear - front;
     // pipe should not already be overfull
     if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
-        ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+        ALOGE("Shared memory control block is corrupt (filled=%zd); shutting down", filled);
         mIsShutdown = true;
     }
     if (mIsShutdown) {
@@ -642,7 +642,7 @@
     }
     // FIXME AudioRecord wakeup needs to be optimized; it currently wakes up client every time
     if (!mIsOut || (mAvailToClient + stepCount >= minimum)) {
-        ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum);
+        ALOGV("mAvailToClient=%zu stepCount=%zu minimum=%zu", mAvailToClient, stepCount, minimum);
         int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
         if (!(old & CBLK_FUTEX_WAKE)) {
             (void) syscall(__NR_futex, &cblk->mFutex,
@@ -675,7 +675,7 @@
     ssize_t filled = rear - cblk->u.mStreaming.mFront;
     // pipe should not already be overfull
     if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
-        ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+        ALOGE("Shared memory control block is corrupt (filled=%zd); shutting down", filled);
         mIsShutdown = true;
         return 0;
     }
@@ -834,7 +834,7 @@
     size_t newPosition = position + stepCount;
     int32_t setFlags = 0;
     if (!(position <= newPosition && newPosition <= mFrameCount)) {
-        ALOGW("%s newPosition %u outside [%u, %u]", __func__, newPosition, position, mFrameCount);
+        ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position, mFrameCount);
         newPosition = mFrameCount;
     } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) {
         if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) {
diff --git a/media/libmedia/CharacterEncodingDetector.cpp b/media/libmedia/CharacterEncodingDetector.cpp
index 4992798..7d1ddfd 100644
--- a/media/libmedia/CharacterEncodingDetector.cpp
+++ b/media/libmedia/CharacterEncodingDetector.cpp
@@ -112,7 +112,7 @@
         if (allprintable) {
             // since 'buf' is empty, ICU would return a UTF-8 matcher with low confidence, so
             // no need to even call it
-            ALOGV("all tags are printable, assuming ascii (%d)", strlen(buf));
+            ALOGV("all tags are printable, assuming ascii (%zu)", strlen(buf));
         } else {
             ucsdet_setText(csd, buf, strlen(buf), &status);
             int32_t matches;
@@ -267,11 +267,11 @@
     Vector<const UCharsetMatch*> matches;
     UErrorCode status = U_ZERO_ERROR;
 
-    ALOGV("%d matches", nummatches);
+    ALOGV("%zu matches", nummatches);
     for (size_t i = 0; i < nummatches; i++) {
         const char *encname = ucsdet_getName(ucma[i], &status);
         int confidence = ucsdet_getConfidence(ucma[i], &status);
-        ALOGV("%d: %s %d", i, encname, confidence);
+        ALOGV("%zu: %s %d", i, encname, confidence);
         matches.push_back(ucma[i]);
     }
 
@@ -287,7 +287,7 @@
         return matches[0];
     }
 
-    ALOGV("considering %d matches", num);
+    ALOGV("considering %zu matches", num);
 
     // keep track of how many "special" characters result when converting the input using each
     // encoding
@@ -315,7 +315,7 @@
             freqcoverage = frequent_ja_coverage;
         }
 
-        ALOGV("%d: %s %d", i, encname, confidence);
+        ALOGV("%zu: %s %d", i, encname, confidence);
         UConverter *conv = ucnv_open(encname, &status);
         const char *source = input;
         const char *sourceLimit = input + len;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 687fa76..5cf42f7 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -175,6 +175,7 @@
                                 track_flags_t *flags,
                                 pid_t tid,
                                 int *sessionId,
+                                size_t *notificationFrames,
                                 sp<IMemory>& cblk,
                                 sp<IMemory>& buffers,
                                 status_t *status)
@@ -214,6 +215,10 @@
             if (sessionId != NULL) {
                 *sessionId = lSessionId;
             }
+            size_t lNotificationFrames = (size_t) reply.readInt64();
+            if (notificationFrames != NULL) {
+                *notificationFrames = lNotificationFrames;
+            }
             lStatus = reply.readInt32();
             record = interface_cast<IAudioRecord>(reply.readStrongBinder());
             cblk = interface_cast<IMemory>(reply.readStrongBinder());
@@ -959,16 +964,19 @@
             track_flags_t flags = (track_flags_t) data.readInt32();
             pid_t tid = (pid_t) data.readInt32();
             int sessionId = data.readInt32();
+            size_t notificationFrames = 0;
             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);
             LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
             reply->writeInt64(frameCount);
             reply->writeInt32(flags);
             reply->writeInt32(sessionId);
+            reply->writeInt64(notificationFrames);
             reply->writeInt32(status);
             reply->writeStrongBinder(record->asBinder());
             reply->writeStrongBinder(cblk->asBinder());
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 77d131b..41a9065 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -64,7 +64,8 @@
     RELEASE_AUDIO_PATCH,
     LIST_AUDIO_PATCHES,
     SET_AUDIO_PORT_CONFIG,
-    REGISTER_CLIENT
+    REGISTER_CLIENT,
+    GET_OUTPUT_FOR_ATTR
 };
 
 class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -155,6 +156,36 @@
         return static_cast <audio_io_handle_t> (reply.readInt32());
     }
 
+    virtual audio_io_handle_t getOutputForAttr(
+                                            const audio_attributes_t *attr,
+                                            uint32_t samplingRate,
+                                            audio_format_t format,
+                                            audio_channel_mask_t channelMask,
+                                            audio_output_flags_t flags,
+                                            const audio_offload_info_t *offloadInfo)
+        {
+            Parcel data, reply;
+            data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+            if (attr == NULL) {
+                ALOGE("Writing NULL audio attributes - shouldn't happen");
+                return (audio_io_handle_t) 0;
+            }
+            data.write(attr, sizeof(audio_attributes_t));
+            data.writeInt32(samplingRate);
+            data.writeInt32(static_cast <uint32_t>(format));
+            data.writeInt32(channelMask);
+            data.writeInt32(static_cast <uint32_t>(flags));
+            // hasOffloadInfo
+            if (offloadInfo == NULL) {
+                data.writeInt32(0);
+            } else {
+                data.writeInt32(1);
+                data.write(offloadInfo, sizeof(audio_offload_info_t));
+            }
+            remote()->transact(GET_OUTPUT_FOR_ATTR, data, &reply);
+            return static_cast <audio_io_handle_t> (reply.readInt32());
+        }
+
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
                                  int session)
@@ -614,6 +645,30 @@
             return NO_ERROR;
         } break;
 
+        case GET_OUTPUT_FOR_ATTR: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            audio_attributes_t *attr = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+            data.read(attr, sizeof(audio_attributes_t));
+            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());
+            bool hasOffloadInfo = data.readInt32() != 0;
+            audio_offload_info_t offloadInfo;
+            if (hasOffloadInfo) {
+                data.read(&offloadInfo, sizeof(audio_offload_info_t));
+            }
+            audio_io_handle_t output = getOutputForAttr(attr,
+                    samplingRate,
+                    format,
+                    channelMask,
+                    flags,
+                    hasOffloadInfo ? &offloadInfo : NULL);
+            reply->writeInt32(static_cast <int>(output));
+            return NO_ERROR;
+        } break;
+
         case START_OUTPUT: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
             audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 432d890..38f717c 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -15,8 +15,10 @@
 ** limitations under the License.
 */
 
+#include <inttypes.h>
 #include <stdint.h>
 #include <sys/types.h>
+
 #include <binder/Parcel.h>
 #include <media/IMediaHTTPService.h>
 #include <media/IMediaMetadataRetriever.h>
@@ -125,7 +127,7 @@
 
     sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
     {
-        ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
+        ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
         Parcel data, reply;
         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
         data.writeInt64(timeUs);
@@ -237,7 +239,7 @@
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
             int64_t timeUs = data.readInt64();
             int option = data.readInt32();
-            ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
+            ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
             setSchedPolicy(data);
 #endif
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 8e58162..95af006 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -17,6 +17,10 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "IMediaRecorder"
+
+#include <inttypes.h>
+#include <unistd.h>
+
 #include <utils/Log.h>
 #include <binder/Parcel.h>
 #include <camera/ICamera.h>
@@ -24,8 +28,6 @@
 #include <media/IMediaRecorder.h>
 #include <gui/Surface.h>
 #include <gui/IGraphicBufferProducer.h>
-#include <unistd.h>
-
 
 namespace android {
 
@@ -167,7 +169,7 @@
     }
 
     status_t setOutputFile(int fd, int64_t offset, int64_t length) {
-        ALOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length);
+        ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
         Parcel data, reply;
         data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
         data.writeFileDescriptor(fd);
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 28238c4..e9e453b 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -475,7 +475,7 @@
 }
 
 void MediaProfiles::initRequiredProfileRefs(const Vector<int>& cameraIds) {
-    ALOGV("Number of camera ids: %d", cameraIds.size());
+    ALOGV("Number of camera ids: %zu", cameraIds.size());
     CHECK(cameraIds.size() > 0);
     mRequiredProfileRefs = new RequiredProfiles[cameraIds.size()];
     for (size_t i = 0, n = cameraIds.size(); i < n; ++i) {
@@ -602,14 +602,14 @@
 
                 int index = getCamcorderProfileIndex(cameraId, profile->mQuality);
                 if (index != -1) {
-                    ALOGV("Profile quality %d for camera %d already exists",
+                    ALOGV("Profile quality %d for camera %zu already exists",
                         profile->mQuality, cameraId);
                     CHECK(index == refIndex);
                     continue;
                 }
 
                 // Insert the new profile
-                ALOGV("Add a profile: quality %d=>%d for camera %d",
+                ALOGV("Add a profile: quality %d=>%d for camera %zu",
                         mCamcorderProfiles[info->mRefProfileIndex]->mQuality,
                         profile->mQuality, cameraId);
 
diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp
index a55e09c..2aa0592 100644
--- a/media/libmedia/SoundPool.cpp
+++ b/media/libmedia/SoundPool.cpp
@@ -16,6 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "SoundPool"
+
+#include <inttypes.h>
+
 #include <utils/Log.h>
 
 #define USE_SHARED_MEM_BUFFER
@@ -212,7 +215,7 @@
 
 int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
 {
-    ALOGV("load: fd=%d, offset=%lld, length=%lld, priority=%d",
+    ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
             fd, offset, length, priority);
     Mutex::Autolock lock(&mLock);
     sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length);
@@ -462,7 +465,8 @@
     mFd = dup(fd);
     mOffset = offset;
     mLength = length;
-    ALOGV("create sampleID=%d, fd=%d, offset=%lld, length=%lld", mSampleID, mFd, mLength, mOffset);
+    ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64,
+        mSampleID, mFd, mLength, mOffset);
 }
 
 void Sample::init()
@@ -516,7 +520,7 @@
         ALOGE("Unable to load sample: %s", mUrl);
         goto error;
     }
-    ALOGV("pointer = %p, size = %u, sampleRate = %u, numChannels = %d",
+    ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d",
           mHeap->getBase(), mSize, sampleRate, numChannels);
 
     if (sampleRate > kMaxSampleRate) {
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 1d6bb6f..39a239d 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -18,6 +18,8 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaMetadataRetriever"
 
+#include <inttypes.h>
+
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
 #include <media/mediametadataretriever.h>
@@ -114,7 +116,7 @@
 
 status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
 {
-    ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
     Mutex::Autolock _l(mLock);
     if (mRetriever == 0) {
         ALOGE("retriever is not initialized");
@@ -129,7 +131,7 @@
 
 sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option)
 {
-    ALOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
+    ALOGV("getFrameAtTime: time(%" PRId64 " us) option(%d)", timeUs, option);
     Mutex::Autolock _l(mLock);
     if (mRetriever == 0) {
         ALOGE("retriever is not initialized");
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 0be01a9..889bd7f 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -17,12 +17,14 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaPlayer"
-#include <utils/Log.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
 #include <fcntl.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utils/Log.h>
 
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
@@ -157,7 +159,7 @@
 
 status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
 {
-    ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
     status_t err = UNKNOWN_ERROR;
     const sp<IMediaPlayerService>& service(getMediaPlayerService());
     if (service != 0) {
@@ -194,7 +196,7 @@
             (mCurrentState != MEDIA_PLAYER_STATE_ERROR) &&
             ((mCurrentState & MEDIA_PLAYER_IDLE) != MEDIA_PLAYER_IDLE);
     if ((mPlayer != NULL) && hasBeenInitialized) {
-        ALOGV("invoke %d", request.dataSize());
+        ALOGV("invoke %zu", request.dataSize());
         return  mPlayer->invoke(request, reply);
     }
     ALOGE("invoke failed: wrong state %X", mCurrentState);
@@ -622,10 +624,32 @@
     return mPlayer->attachAuxEffect(effectId);
 }
 
+// always call with lock held
+status_t MediaPlayer::checkStateForKeySet_l(int key)
+{
+    switch(key) {
+    case KEY_PARAMETER_AUDIO_ATTRIBUTES:
+        if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |
+                MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) {
+            // Can't change the audio attributes after prepare
+            ALOGE("trying to set audio attributes called in state %d", mCurrentState);
+            return INVALID_OPERATION;
+        }
+        break;
+    default:
+        // parameter doesn't require player state check
+        break;
+    }
+    return OK;
+}
+
 status_t MediaPlayer::setParameter(int key, const Parcel& request)
 {
     ALOGV("MediaPlayer::setParameter(%d)", key);
     Mutex::Autolock _l(mLock);
+    if (checkStateForKeySet_l(key) != OK) {
+        return INVALID_OPERATION;
+    }
     if (mPlayer != NULL) {
         return  mPlayer->setParameter(key, request);
     }
@@ -818,7 +842,7 @@
                                         audio_format_t* pFormat,
                                         const sp<IMemoryHeap>& heap, size_t *pSize)
 {
-    ALOGV("decode(%d, %lld, %lld)", fd, offset, length);
+    ALOGV("decode(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
     status_t status;
     const sp<IMediaPlayerService>& service = getMediaPlayerService();
     if (service != 0) {
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 3710e46..c8192e9 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -17,6 +17,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaRecorder"
+
+#include <inttypes.h>
+
 #include <utils/Log.h>
 #include <media/mediarecorder.h>
 #include <binder/IServiceManager.h>
@@ -286,7 +289,7 @@
 
 status_t MediaRecorder::setOutputFile(int fd, int64_t offset, int64_t length)
 {
-    ALOGV("setOutputFile(%d, %lld, %lld)", fd, offset, length);
+    ALOGV("setOutputFile(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
     if (mMediaRecorder == NULL) {
         ALOGE("media recorder is not initialized yet");
         return INVALID_OPERATION;
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 9b239b1..e9c5e8e 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -176,11 +176,11 @@
 class StagefrightPlayerFactory :
     public MediaPlayerFactory::IFactory {
   public:
-    virtual float scoreFactory(const sp<IMediaPlayer>& client,
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                                int fd,
                                int64_t offset,
-                               int64_t length,
-                               float curScore) {
+                               int64_t /*length*/,
+                               float /*curScore*/) {
         char buf[20];
         lseek(fd, offset, SEEK_SET);
         read(fd, buf, sizeof(buf));
@@ -203,7 +203,7 @@
 
 class NuPlayerFactory : public MediaPlayerFactory::IFactory {
   public:
-    virtual float scoreFactory(const sp<IMediaPlayer>& client,
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                                const char* url,
                                float curScore) {
         static const float kOurScore = 0.8;
@@ -235,9 +235,9 @@
         return 0.0;
     }
 
-    virtual float scoreFactory(const sp<IMediaPlayer>& client,
-                               const sp<IStreamSource> &source,
-                               float curScore) {
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+                               const sp<IStreamSource>& /*source*/,
+                               float /*curScore*/) {
         return 1.0;
     }
 
@@ -249,7 +249,7 @@
 
 class SonivoxPlayerFactory : public MediaPlayerFactory::IFactory {
   public:
-    virtual float scoreFactory(const sp<IMediaPlayer>& client,
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                                const char* url,
                                float curScore) {
         static const float kOurScore = 0.4;
@@ -280,7 +280,7 @@
         return 0.0;
     }
 
-    virtual float scoreFactory(const sp<IMediaPlayer>& client,
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                                int fd,
                                int64_t offset,
                                int64_t length,
@@ -318,9 +318,9 @@
 
 class TestPlayerFactory : public MediaPlayerFactory::IFactory {
   public:
-    virtual float scoreFactory(const sp<IMediaPlayer>& client,
+    virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                                const char* url,
-                               float curScore) {
+                               float /*curScore*/) {
         if (TestPlayerStub::canBeUsed(url)) {
             return 1.0;
         }
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.h b/media/libmediaplayerservice/MediaPlayerFactory.h
index fe8972b..5ddde19 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.h
+++ b/media/libmediaplayerservice/MediaPlayerFactory.h
@@ -29,19 +29,19 @@
       public:
         virtual ~IFactory() { }
 
-        virtual float scoreFactory(const sp<IMediaPlayer>& client,
-                                   const char* url,
-                                   float curScore) { return 0.0; }
+        virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+                                   const char* /*url*/,
+                                   float /*curScore*/) { return 0.0; }
 
-        virtual float scoreFactory(const sp<IMediaPlayer>& client,
-                                   int fd,
-                                   int64_t offset,
-                                   int64_t length,
-                                   float curScore) { return 0.0; }
+        virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+                                   int /*fd*/,
+                                   int64_t /*offset*/,
+                                   int64_t /*length*/,
+                                   float /*curScore*/) { return 0.0; }
 
-        virtual float scoreFactory(const sp<IMediaPlayer>& client,
-                                   const sp<IStreamSource> &source,
-                                   float curScore) { return 0.0; }
+        virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
+                                   const sp<IStreamSource> &/*source*/,
+                                   float /*curScore*/) { return 0.0; }
 
         virtual sp<MediaPlayerBase> createPlayer() = 0;
     };
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 778eb9a..7218467 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -186,6 +186,60 @@
 }  // anonymous namespace
 
 
+namespace {
+using android::Parcel;
+using android::String16;
+
+// marshalling tag indicating flattened utf16 tags
+// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
+const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
+
+// Audio attributes format in a parcel:
+//
+//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       usage                                   |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       content_type                            |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       flags                                   |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       kAudioAttributesMarshallTagFlattenTags  | // ignore tags if not found
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       flattened tags in UTF16                 |
+// |                         ...                                   |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that contains audio attributes.
+// @param[out] attributes On exit points to an initialized audio_attributes_t structure
+// @param[out] status On exit contains the status code to be returned.
+void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes)
+{
+    attributes->usage = (audio_usage_t) parcel.readInt32();
+    attributes->content_type = (audio_content_type_t) parcel.readInt32();
+    attributes->flags = (audio_flags_mask_t) parcel.readInt32();
+    const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
+    if (hasFlattenedTag) {
+        // the tags are UTF16, convert to UTF8
+        String16 tags = parcel.readString16();
+        ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
+        if (realTagSize <= 0) {
+            strcpy(attributes->tags, "");
+        } else {
+            // copy the flattened string into the attributes as the destination for the conversion:
+            // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
+            size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
+                    AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
+            utf16_to_utf8(tags.string(), tagSize, attributes->tags);
+        }
+    } else {
+        ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
+        strcpy(attributes->tags, "");
+    }
+}
+} // anonymous namespace
+
+
 namespace android {
 
 static bool checkPermission(const char* permissionString) {
@@ -307,7 +361,7 @@
     return new RemoteDisplay(client, iface.string());
 }
 
-status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
+status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& /*args*/) const
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
@@ -508,6 +562,7 @@
     mAudioSessionId = audioSessionId;
     mUID = uid;
     mRetransmitEndpointValid = false;
+    mAudioAttributes = NULL;
 
 #if CALLBACK_ANTAGONIZER
     ALOGD("create Antagonizer");
@@ -522,6 +577,9 @@
     wp<Client> client(this);
     disconnect();
     mService->removeClient(client);
+    if (mAudioAttributes != NULL) {
+        free(mAudioAttributes);
+    }
 }
 
 void MediaPlayerService::Client::disconnect()
@@ -587,7 +645,7 @@
 
     if (!p->hardwareOutput()) {
         mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
-                mPid);
+                mPid, mAudioAttributes);
         static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
     }
 
@@ -673,8 +731,8 @@
 
     ALOGV("st_dev  = %llu", sb.st_dev);
     ALOGV("st_mode = %u", sb.st_mode);
-    ALOGV("st_uid  = %lu", sb.st_uid);
-    ALOGV("st_gid  = %lu", sb.st_gid);
+    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
+    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
     ALOGV("st_size = %llu", sb.st_size);
 
     if (offset >= sb.st_size) {
@@ -803,7 +861,7 @@
 }
 
 status_t MediaPlayerService::Client::getMetadata(
-        bool update_only, bool apply_filter, Parcel *reply)
+        bool update_only, bool /*apply_filter*/, Parcel *reply)
 {
     sp<MediaPlayerBase> player = getPlayer();
     if (player == 0) return UNKNOWN_ERROR;
@@ -968,6 +1026,22 @@
     return NO_ERROR;
 }
 
+status_t MediaPlayerService::Client::setAudioAttributes_l(const Parcel &parcel)
+{
+    if (mAudioAttributes != NULL) { free(mAudioAttributes); }
+    mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+    unmarshallAudioAttributes(parcel, mAudioAttributes);
+
+    ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
+            mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
+            mAudioAttributes->tags);
+
+    if (mAudioOutput != 0) {
+        mAudioOutput->setAudioAttributes(mAudioAttributes);
+    }
+    return NO_ERROR;
+}
+
 status_t MediaPlayerService::Client::setLooping(int loop)
 {
     ALOGV("[%d] setLooping(%d)", mConnId, loop);
@@ -1016,9 +1090,17 @@
 
 status_t MediaPlayerService::Client::setParameter(int key, const Parcel &request) {
     ALOGV("[%d] setParameter(%d)", mConnId, key);
-    sp<MediaPlayerBase> p = getPlayer();
-    if (p == 0) return UNKNOWN_ERROR;
-    return p->setParameter(key, request);
+    switch (key) {
+    case KEY_PARAMETER_AUDIO_ATTRIBUTES:
+    {
+        Mutex::Autolock l(mLock);
+        return setAudioAttributes_l(request);
+    }
+    default:
+        sp<MediaPlayerBase> p = getPlayer();
+        if (p == 0) { return UNKNOWN_ERROR; }
+        return p->setParameter(key, request);
+    }
 }
 
 status_t MediaPlayerService::Client::getParameter(int key, Parcel *reply) {
@@ -1300,7 +1382,8 @@
 
 #undef LOG_TAG
 #define LOG_TAG "AudioSink"
-MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid)
+MediaPlayerService::AudioOutput::AudioOutput(int sessionId, int uid, int pid,
+        const audio_attributes_t* attr)
     : mCallback(NULL),
       mCallbackCookie(NULL),
       mCallbackData(NULL),
@@ -1319,6 +1402,7 @@
     mAuxEffectId = 0;
     mSendLevel = 0.0;
     setMinBufferCount();
+    mAttributes = attr;
 }
 
 MediaPlayerService::AudioOutput::~AudioOutput()
@@ -1408,6 +1492,10 @@
     return mTrack->getParameters(keys);
 }
 
+void MediaPlayerService::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
+    mAttributes = attributes;
+}
+
 void MediaPlayerService::AudioOutput::deleteRecycledTrack()
 {
     ALOGV("deleteRecycledTrack");
@@ -1557,7 +1645,8 @@
                     AudioTrack::TRANSFER_CALLBACK,
                     offloadInfo,
                     mUid,
-                    mPid);
+                    mPid,
+                    mAttributes);
         } else {
             t = new AudioTrack(
                     mStreamType,
@@ -1573,13 +1662,18 @@
                     AudioTrack::TRANSFER_DEFAULT,
                     NULL, // offload info
                     mUid,
-                    mPid);
+                    mPid,
+                    mAttributes);
         }
 
         if ((t == 0) || (t->initCheck() != NO_ERROR)) {
             ALOGE("Unable to create audio track");
             delete newcbd;
             return NO_INIT;
+        } else {
+            // successful AudioTrack initialization implies a legacy stream type was generated
+            // from the audio attributes
+            mStreamType = t->streamType();
         }
     }
 
@@ -1926,8 +2020,8 @@
 status_t MediaPlayerService::AudioCache::open(
         uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
         audio_format_t format, int bufferCount,
-        AudioCallback cb, void *cookie, audio_output_flags_t flags,
-        const audio_offload_info_t *offloadInfo)
+        AudioCallback cb, void *cookie, audio_output_flags_t /*flags*/,
+        const audio_offload_info_t* /*offloadInfo*/)
 {
     ALOGV("open(%u, %d, 0x%x, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount);
     if (mHeap->getHeapID() < 0) {
@@ -1994,7 +2088,7 @@
 }
 
 void MediaPlayerService::AudioCache::notify(
-        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
+        void* cookie, int msg, int ext1, int ext2, const Parcel* /*obj*/)
 {
     ALOGV("notify(%p, %d, %d, %d)", cookie, msg, ext1, ext2);
     AudioCache* p = static_cast<AudioCache*>(cookie);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 448f27a..2eca6a0 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -72,7 +72,8 @@
         class CallbackData;
 
      public:
-                                AudioOutput(int sessionId, int uid, int pid);
+                                AudioOutput(int sessionId, int uid, int pid,
+                                        const audio_attributes_t * attr);
         virtual                 ~AudioOutput();
 
         virtual bool            ready() const { return mTrack != 0; }
@@ -104,6 +105,7 @@
                 void            setAudioStreamType(audio_stream_type_t streamType) {
                                                                         mStreamType = streamType; }
         virtual audio_stream_type_t getAudioStreamType() const { return mStreamType; }
+                void            setAudioAttributes(const audio_attributes_t * attributes);
 
                 void            setVolume(float left, float right);
         virtual status_t        setPlaybackRatePermille(int32_t ratePermille);
@@ -133,6 +135,7 @@
         CallbackData *          mCallbackData;
         uint64_t                mBytesWritten;
         audio_stream_type_t     mStreamType;
+        const audio_attributes_t *mAttributes;
         float                   mLeftVolume;
         float                   mRightVolume;
         int32_t                 mPlaybackRatePermille;
@@ -410,6 +413,8 @@
         // Disconnect from the currently connected ANativeWindow.
         void disconnectNativeWindow();
 
+        status_t setAudioAttributes_l(const Parcel &request);
+
         mutable     Mutex                       mLock;
                     sp<MediaPlayerBase>         mPlayer;
                     sp<MediaPlayerService>      mService;
@@ -420,6 +425,7 @@
                     bool                        mLoop;
                     int32_t                     mConnId;
                     int                         mAudioSessionId;
+                    audio_attributes_t *        mAudioAttributes;
                     uid_t                       mUID;
                     sp<ANativeWindow>           mConnectedWindow;
                     sp<IBinder>                 mConnectedWindowBinder;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 80c7e0a..fa28451 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -57,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
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
@@ -147,8 +147,8 @@
     }
     ALOGV("st_dev  = %llu", sb.st_dev);
     ALOGV("st_mode = %u", sb.st_mode);
-    ALOGV("st_uid  = %lu", sb.st_uid);
-    ALOGV("st_gid  = %lu", sb.st_gid);
+    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));
+    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));
     ALOGV("st_size = %llu", sb.st_size);
 
     if (offset >= sb.st_size) {
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index deeddd1..749ef96 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -114,7 +114,7 @@
 }
 
 status_t MidiFile::setDataSource(
-        const sp<IMediaHTTPService> &httpService,
+        const sp<IMediaHTTPService> & /*httpService*/,
         const char* path,
         const KeyedVector<String8, String8> *) {
     ALOGV("MidiFile::setDataSource url=%s", path);
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 12802ba..82e4e88 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -38,7 +38,7 @@
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
     virtual status_t    setVideoSurfaceTexture(
-                                const sp<IGraphicBufferProducer>& bufferProducer)
+                                const sp<IGraphicBufferProducer>& /*bufferProducer*/)
                             { return UNKNOWN_ERROR; }
     virtual status_t    prepare();
     virtual status_t    prepareAsync();
@@ -53,13 +53,13 @@
     virtual status_t    reset();
     virtual status_t    setLooping(int loop);
     virtual player_type playerType() { return SONIVOX_PLAYER; }
-    virtual status_t    invoke(const Parcel& request, Parcel *reply) {
+    virtual status_t    invoke(const Parcel& /*request*/, Parcel* /*reply*/) {
         return INVALID_OPERATION;
     }
-    virtual status_t    setParameter(int key, const Parcel &request) {
+    virtual status_t    setParameter(int /*key*/, const Parcel &/*request*/) {
         return INVALID_OPERATION;
     }
-    virtual status_t    getParameter(int key, Parcel *reply) {
+    virtual status_t    getParameter(int /*key*/, Parcel* /*reply*/) {
         return INVALID_OPERATION;
     }
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 06aac33..388f77a 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -67,6 +67,14 @@
 
     CHECK(extractor != NULL);
 
+    sp<MetaData> fileMeta = extractor->getMetaData();
+    if (fileMeta != NULL) {
+        int64_t duration;
+        if (fileMeta->findInt64(kKeyDuration, &duration)) {
+            mDurationUs = duration;
+        }
+    }
+
     for (size_t i = 0; i < extractor->countTracks(); ++i) {
         sp<MetaData> meta = extractor->getTrackMetaData(i);
 
@@ -264,7 +272,9 @@
 
             if (seeking) {
                 track->mPackets->queueDiscontinuity(
-                        ATSParser::DISCONTINUITY_SEEK, NULL);
+                        ATSParser::DISCONTINUITY_SEEK,
+                        NULL,
+                        true /* discard */);
             }
 
             track->mPackets->queueAccessUnit(buffer);
@@ -272,7 +282,9 @@
         } else if (err == INFO_FORMAT_CHANGED) {
 #if 0
             track->mPackets->queueDiscontinuity(
-                    ATSParser::DISCONTINUITY_FORMATCHANGE, NULL);
+                    ATSParser::DISCONTINUITY_FORMATCHANGE,
+                    NULL,
+                    false /* discard */);
 #endif
         } else {
             track->mPackets->signalEOS(err);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 5abfb71..dd73cc4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -674,9 +674,9 @@
 
     bool hasCC = false;
 
-    ABitReader br(sei->data() + 1, sei->size() - 1);
+    NALBitReader br(sei->data() + 1, sei->size() - 1);
     // sei_message()
-    while (br.numBitsLeft() >= 16) { // at least 16-bit for sei_message()
+    while (br.atLeastNumBitsLeft(16)) { // at least 16-bit for sei_message()
         uint32_t payload_type = 0;
         size_t payload_size = 0;
         uint8_t last_byte;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 94800ba..2338b23 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -505,7 +505,10 @@
             TrackInfo *info = &mTracks.editItemAt(trackIndex);
             sp<AnotherPacketSource> source = info->mSource;
             if (source != NULL) {
-                source->queueDiscontinuity(ATSParser::DISCONTINUITY_SEEK, NULL);
+                source->queueDiscontinuity(
+                        ATSParser::DISCONTINUITY_SEEK,
+                        NULL,
+                        true /* discard */);
             }
 
             break;
diff --git a/media/libnbaio/MonoPipe.cpp b/media/libnbaio/MonoPipe.cpp
index 4adf018..0b65861 100644
--- a/media/libnbaio/MonoPipe.cpp
+++ b/media/libnbaio/MonoPipe.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 #define LOG_TAG "MonoPipe"
 //#define LOG_NDEBUG 0
 
@@ -87,7 +89,7 @@
     static const uint64_t kUnsignedHiBitsMask = ~(0xFFFFFFFFull);
     if ((N & kSignedHiBitsMask) || (D & kUnsignedHiBitsMask)) {
         ALOGE("Cannot reduce sample rate to local clock frequency ratio to fit"
-              " in a 32/32 bit rational.  (max reduction is 0x%016llx/0x%016llx"
+              " in a 32/32 bit rational.  (max reduction is 0x%016" PRIx64 "/0x%016" PRIx64
               ").  getNextWriteTimestamp calls will be non-functional", N, D);
         return;
     }
@@ -308,7 +310,7 @@
         // error, but then zero out the ratio in the linear transform so
         // that we don't try to do any conversions from now on.  This
         // MonoPipe's getNextWriteTimestamp is now broken for good.
-        ALOGE("Overflow when attempting to convert %d audio frames to"
+        ALOGE("Overflow when attempting to convert %zu audio frames to"
               " duration in local time.  getNextWriteTimestamp will fail from"
               " now on.", audFrames);
         mSamplesToLocalTime.a_to_b_numer = 0;
diff --git a/media/libnbaio/NBAIO.cpp b/media/libnbaio/NBAIO.cpp
index ff3284c..d641e74 100644
--- a/media/libnbaio/NBAIO.cpp
+++ b/media/libnbaio/NBAIO.cpp
@@ -137,7 +137,7 @@
 ssize_t NBAIO_Port::negotiate(const NBAIO_Format offers[], size_t numOffers,
                                   NBAIO_Format counterOffers[], size_t& numCounterOffers)
 {
-    ALOGV("negotiate offers=%p numOffers=%u countersOffers=%p numCounterOffers=%u",
+    ALOGV("negotiate offers=%p numOffers=%zu countersOffers=%p numCounterOffers=%zu",
             offers, numOffers, counterOffers, numCounterOffers);
     if (Format_isValid(mFormat)) {
         for (size_t i = 0; i < numOffers; ++i) {
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index deee8e7..353920e 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AACWriter"
 #include <utils/Log.h>
@@ -27,10 +33,6 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/mediarecorder.h>
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
 
 namespace android {
 
@@ -348,7 +350,7 @@
             mResumed = false;
         }
         timestampUs -= previousPausedDurationUs;
-        ALOGV("time stamp: %lld, previous paused duration: %lld",
+        ALOGV("time stamp: %" PRId64 ", previous paused duration: %" PRId64,
             timestampUs, previousPausedDurationUs);
         if (timestampUs > maxTimestampUs) {
             maxTimestampUs = timestampUs;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9ab1417..9c64d72 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -547,7 +547,7 @@
     }
 
     sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", ACodec::kWhatBuffersAllocated);
+    notify->setInt32("what", CodecBase::kWhatBuffersAllocated);
 
     notify->setInt32("portIndex", portIndex);
 
@@ -2035,6 +2035,10 @@
             err = setupAVCEncoderParameters(msg);
             break;
 
+        case OMX_VIDEO_CodingHEVC:
+            err = setupHEVCEncoderParameters(msg);
+            break;
+
         case OMX_VIDEO_CodingVP8:
         case OMX_VIDEO_CodingVP9:
             err = setupVPXEncoderParameters(msg);
@@ -2371,6 +2375,62 @@
     return configureBitrate(bitrate, bitrateMode);
 }
 
+status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
+    int32_t bitrate, iFrameInterval;
+    if (!msg->findInt32("bitrate", &bitrate)
+            || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+        return INVALID_OPERATION;
+    }
+
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+
+    float frameRate;
+    if (!msg->findFloat("frame-rate", &frameRate)) {
+        int32_t tmp;
+        if (!msg->findInt32("frame-rate", &tmp)) {
+            return INVALID_OPERATION;
+        }
+        frameRate = (float)tmp;
+    }
+
+    OMX_VIDEO_PARAM_HEVCTYPE hevcType;
+    InitOMXParams(&hevcType);
+    hevcType.nPortIndex = kPortIndexOutput;
+
+    status_t err = OK;
+    err = mOMX->getParameter(
+            mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
+    if (err != OK) {
+        return err;
+    }
+
+    int32_t profile;
+    if (msg->findInt32("profile", &profile)) {
+        int32_t level;
+        if (!msg->findInt32("level", &level)) {
+            return INVALID_OPERATION;
+        }
+
+        err = verifySupportForProfileAndLevel(profile, level);
+        if (err != OK) {
+            return err;
+        }
+
+        hevcType.eProfile = static_cast<OMX_VIDEO_HEVCPROFILETYPE>(profile);
+        hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level);
+    }
+
+    // TODO: Need OMX structure definition for setting iFrameInterval
+
+    err = mOMX->setParameter(
+            mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
+    if (err != OK) {
+        return err;
+    }
+
+    return configureBitrate(bitrate, bitrateMode);
+}
+
 status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) {
     int32_t bitrate;
     int32_t iFrameInterval = 0;
@@ -2945,7 +3005,7 @@
 
 void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) {
     sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", ACodec::kWhatError);
+    notify->setInt32("what", CodecBase::kWhatError);
     notify->setInt32("omx-error", error);
     notify->setInt32("err", internalError);
     notify->post();
@@ -3338,7 +3398,7 @@
     CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
 
     sp<AMessage> notify = mCodec->mNotify->dup();
-    notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+    notify->setInt32("what", CodecBase::kWhatFillThisBuffer);
     notify->setInt32("buffer-id", info->mBufferID);
 
     info->mData->meta()->clear();
@@ -3633,7 +3693,7 @@
             info->mData->meta()->setInt64("timeUs", timeUs);
 
             sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+            notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
             notify->setInt32("buffer-id", info->mBufferID);
             notify->setBuffer("buffer", info->mData);
             notify->setInt32("flags", flags);
@@ -3650,7 +3710,7 @@
                 ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str());
 
                 sp<AMessage> notify = mCodec->mNotify->dup();
-                notify->setInt32("what", ACodec::kWhatEOS);
+                notify->setInt32("what", CodecBase::kWhatEOS);
                 notify->setInt32("err", mCodec->mInputEOSResult);
                 notify->post();
 
@@ -3831,7 +3891,7 @@
                      "cannot keep component allocated on shutdown in Uninitialized state");
 
             sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+            notify->setInt32("what", CodecBase::kWhatShutdownCompleted);
             notify->post();
 
             handled = true;
@@ -3841,7 +3901,7 @@
         case ACodec::kWhatFlush:
         {
             sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->setInt32("what", CodecBase::kWhatFlushCompleted);
             notify->post();
 
             handled = true;
@@ -3963,7 +4023,7 @@
 
     {
         sp<AMessage> notify = mCodec->mNotify->dup();
-        notify->setInt32("what", ACodec::kWhatComponentAllocated);
+        notify->setInt32("what", CodecBase::kWhatComponentAllocated);
         notify->setString("componentName", mCodec->mComponentName.c_str());
         notify->post();
     }
@@ -4013,7 +4073,7 @@
 
     if (mCodec->mExplicitShutdown) {
         sp<AMessage> notify = mCodec->mNotify->dup();
-        notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+        notify->setInt32("what", CodecBase::kWhatShutdownCompleted);
         notify->post();
         mCodec->mExplicitShutdown = false;
     }
@@ -4060,7 +4120,7 @@
         case ACodec::kWhatFlush:
         {
             sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->setInt32("what", CodecBase::kWhatFlushCompleted);
             notify->post();
 
             handled = true;
@@ -4109,7 +4169,7 @@
 
     {
         sp<AMessage> notify = mCodec->mNotify->dup();
-        notify->setInt32("what", ACodec::kWhatComponentConfigured);
+        notify->setInt32("what", CodecBase::kWhatComponentConfigured);
         notify->setMessage("input-format", mCodec->mInputFormat);
         notify->setMessage("output-format", mCodec->mOutputFormat);
         notify->post();
@@ -4123,7 +4183,7 @@
     ALOGV("onCreateInputSurface");
 
     sp<AMessage> notify = mCodec->mNotify->dup();
-    notify->setInt32("what", ACodec::kWhatInputSurfaceCreated);
+    notify->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
 
     sp<IGraphicBufferProducer> bufferProducer;
     status_t err;
@@ -4277,7 +4337,7 @@
         {
             // We haven't even started yet, so we're flushed alright...
             sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->setInt32("what", CodecBase::kWhatFlushCompleted);
             notify->post();
             return true;
         }
@@ -4338,7 +4398,7 @@
         {
             // We haven't even started yet, so we're flushed alright...
             sp<AMessage> notify = mCodec->mNotify->dup();
-            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->setInt32("what", CodecBase::kWhatFlushCompleted);
             notify->post();
 
             return true;
@@ -4641,7 +4701,7 @@
 
 void ACodec::onSignalEndOfInputStream() {
     sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", ACodec::kWhatSignaledInputEOS);
+    notify->setInt32("what", CodecBase::kWhatSignaledInputEOS);
 
     status_t err = mOMX->signalEndOfInputStream(mNode);
     if (err != OK) {
@@ -5071,7 +5131,7 @@
         mCodec->waitUntilAllPossibleNativeWindowBuffersAreReturnedToUs();
 
         sp<AMessage> notify = mCodec->mNotify->dup();
-        notify->setInt32("what", ACodec::kWhatFlushCompleted);
+        notify->setInt32("what", CodecBase::kWhatFlushCompleted);
         notify->post();
 
         mCodec->mPortEOS[kPortIndexInput] =
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 653ca36..9aa7d95 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/MediaBuffer.h>
@@ -22,10 +28,6 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/mediarecorder.h>
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
 
 namespace android {
 
@@ -235,7 +237,7 @@
             mResumed = false;
         }
         timestampUs -= previousPausedDurationUs;
-        ALOGV("time stamp: %lld, previous paused duration: %lld",
+        ALOGV("time stamp: %" PRId64 ", previous paused duration: %" PRId64,
                 timestampUs, previousPausedDurationUs);
         if (timestampUs > maxTimestampUs) {
             maxTimestampUs = timestampUs;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index d9e39ff..99c8e9f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -15,6 +15,7 @@
         CameraSource.cpp                  \
         CameraSourceTimeLapse.cpp         \
         ClockEstimator.cpp                \
+        CodecBase.cpp                     \
         DataSource.cpp                    \
         DataURISource.cpp                 \
         DRMExtractor.cpp                  \
@@ -68,6 +69,7 @@
         $(TOP)/external/flac/include \
         $(TOP)/external/tremolo \
         $(TOP)/external/openssl/include \
+        $(TOP)/external/libvpx/libwebm \
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 2669849..fdac8fc 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioPlayer"
 #include <utils/Log.h>
@@ -566,12 +568,12 @@
                             int64_t timeToCompletionUs =
                                 (1000000ll * numFramesPendingPlayout) / mSampleRate;
 
-                            ALOGV("total number of frames played: %lld (%lld us)",
+                            ALOGV("total number of frames played: %" PRId64 " (%lld us)",
                                     (mNumFramesPlayed + numAdditionalFrames),
                                     1000000ll * (mNumFramesPlayed + numAdditionalFrames)
                                         / mSampleRate);
 
-                            ALOGV("%d frames left to play, %lld us (%.2f secs)",
+                            ALOGV("%d frames left to play, %" PRId64 " us (%.2f secs)",
                                  numFramesPendingPlayout,
                                  timeToCompletionUs, timeToCompletionUs / 1E6);
 
@@ -628,7 +630,7 @@
                 mPositionTimeRealUs =
                     ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
                         / mSampleRate;
-                ALOGV("buffer->size() = %d, "
+                ALOGV("buffer->size() = %zu, "
                      "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f",
                      mInputBuffer->range_length(),
                      mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6);
@@ -746,7 +748,7 @@
 
     // HAL position is relative to the first buffer we sent at mStartPosUs
     const int64_t renderedDuration = mStartPosUs + playedUs;
-    ALOGV("getOutputPlayPositionUs_l %lld", renderedDuration);
+    ALOGV("getOutputPlayPositionUs_l %" PRId64, renderedDuration);
     return renderedDuration;
 }
 
@@ -758,7 +760,7 @@
             return mSeekTimeUs;
         }
         mPositionTimeRealUs = getOutputPlayPositionUs_l();
-        ALOGV("getMediaTimeUs getOutputPlayPositionUs_l() mPositionTimeRealUs %lld",
+        ALOGV("getMediaTimeUs getOutputPlayPositionUs_l() mPositionTimeRealUs %" PRId64,
               mPositionTimeRealUs);
         return mPositionTimeRealUs;
     }
@@ -796,7 +798,7 @@
 status_t AudioPlayer::seekTo(int64_t time_us) {
     Mutex::Autolock autoLock(mLock);
 
-    ALOGV("seekTo( %lld )", time_us);
+    ALOGV("seekTo( %" PRId64 " )", time_us);
 
     mSeeking = true;
     mPositionTimeRealUs = mPositionTimeMediaUs = -1;
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index d0e0e8e..d9aed01 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+#include <stdlib.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioSource"
 #include <utils/Log.h>
@@ -26,7 +29,6 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <cutils/properties.h>
-#include <stdlib.h>
 
 namespace android {
 
@@ -136,7 +138,7 @@
 }
 
 void AudioSource::waitOutstandingEncodingFrames_l() {
-    ALOGV("waitOutstandingEncodingFrames_l: %lld", mNumClientOwnedBuffers);
+    ALOGV("waitOutstandingEncodingFrames_l: %" PRId64, mNumClientOwnedBuffers);
     while (mNumClientOwnedBuffers > 0) {
         mFrameEncodingCompletionCondition.wait(mLock);
     }
@@ -269,7 +271,7 @@
 status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) {
     int64_t timeUs = systemTime() / 1000ll;
 
-    ALOGV("dataCallbackTimestamp: %lld us", timeUs);
+    ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs);
     Mutex::Autolock autoLock(mLock);
     if (!mStarted) {
         ALOGW("Spurious callback from AudioRecord. Drop the audio data.");
@@ -279,7 +281,7 @@
     // Drop retrieved and previously lost audio data.
     if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
         (void) mRecord->getInputFramesLost();
-        ALOGV("Drop audio data at %lld/%lld us", timeUs, mStartTimeUs);
+        ALOGV("Drop audio data at %" PRId64 "/%" PRId64 " us", timeUs, mStartTimeUs);
         return OK;
     }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d679be1..63799e1 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -19,7 +19,9 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AwesomePlayer"
 #define ATRACE_TAG ATRACE_TAG_VIDEO
+
 #include <inttypes.h>
+
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
@@ -408,6 +410,13 @@
 
         totalBitRate += bitrate;
     }
+    sp<MetaData> fileMeta = mExtractor->getMetaData();
+    if (fileMeta != NULL) {
+        int64_t duration;
+        if (fileMeta->findInt64(kKeyDuration, &duration)) {
+            mDurationUs = duration;
+        }
+    }
 
     mBitrate = totalBitRate;
 
@@ -1708,7 +1717,7 @@
     }
 
     if (mAudioPlayer != NULL) {
-        ALOGV("seeking audio to %lld us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
+        ALOGV("seeking audio to %" PRId64 " us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
 
         // If we don't have a video time, seek audio to the originally
         // requested seek time instead.
@@ -1772,7 +1781,7 @@
     if (!mVideoBuffer) {
         MediaSource::ReadOptions options;
         if (mSeeking != NO_SEEK) {
-            ALOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
+            ALOGV("seeking to %" PRId64 " us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
 
             options.setSeekTo(
                     mSeekTimeUs,
@@ -1842,7 +1851,7 @@
 
     if (mSeeking == SEEK_VIDEO_ONLY) {
         if (mSeekTimeUs > timeUs) {
-            ALOGI("XXX mSeekTimeUs = %lld us, timeUs = %lld us",
+            ALOGI("XXX mSeekTimeUs = %" PRId64 " us, timeUs = %" PRId64 " us",
                  mSeekTimeUs, timeUs);
         }
     }
@@ -1940,13 +1949,13 @@
 
         if (latenessUs > 40000) {
             // We're more than 40ms late.
-            ALOGV("we're late by %lld us (%.2f secs)",
+            ALOGV("we're late by %" PRId64 " us (%.2f secs)",
                  latenessUs, latenessUs / 1E6);
 
             if (!(mFlags & SLOW_DECODER_HACK)
                     || mSinceLastDropped > FRAME_DROP_FREQ)
             {
-                ALOGV("we're late by %lld us (%.2f secs) dropping "
+                ALOGV("we're late by %" PRId64 " us (%.2f secs) dropping "
                      "one after %d frames",
                      latenessUs, latenessUs / 1E6, mSinceLastDropped);
 
@@ -2308,12 +2317,12 @@
 
                     if (finalStatus != OK
                             || (metaDataSize >= 0
-                                && cachedDataRemaining >= metaDataSize)
+                                && (off64_t)cachedDataRemaining >= metaDataSize)
                             || (mFlags & PREPARE_CANCELLED)) {
                         break;
                     }
 
-                    ALOGV("now cached %d bytes of data", cachedDataRemaining);
+                    ALOGV("now cached %zu bytes of data", cachedDataRemaining);
 
                     if (metaDataSize < 0
                             && cachedDataRemaining >= kMinBytesForSniffing) {
@@ -2692,7 +2701,7 @@
 
 status_t AwesomePlayer::selectTrack(size_t trackIndex, bool select) {
     ATRACE_CALL();
-    ALOGV("selectTrack: trackIndex = %d and select=%d", trackIndex, select);
+    ALOGV("selectTrack: trackIndex = %zu and select=%d", trackIndex, select);
     Mutex::Autolock autoLock(mLock);
     size_t trackCount = mExtractor->countTracks();
     if (mTextDriver != NULL) {
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index b31e9e8..2b50763 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "CameraSource"
 #include <utils/Log.h>
@@ -77,7 +79,7 @@
 
 void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
                                     camera_frame_metadata_t * /* metadata */) {
-    ALOGV("postData(%d, ptr:%p, size:%d)",
+    ALOGV("postData(%d, ptr:%p, size:%zu)",
          msgType, dataPtr->pointer(), dataPtr->size());
 
     sp<CameraSource> source = mSource.promote();
@@ -711,7 +713,7 @@
         if (NO_ERROR !=
             mFrameCompleteCondition.waitRelative(mLock,
                     mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
-            ALOGW("Timed out waiting for outstanding frames being encoded: %d",
+            ALOGW("Timed out waiting for outstanding frames being encoded: %zu",
                 mFramesBeingEncoded.size());
         }
     }
@@ -722,7 +724,7 @@
     }
 
     if (mCollectStats) {
-        ALOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
+        ALOGI("Frames received/encoded/dropped: %d/%d/%d in %" PRId64 " us",
                 mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped,
                 mLastFrameTimestampUs - mFirstFrameTimeUs);
     }
@@ -809,7 +811,7 @@
                     ALOGW("camera recording proxy is gone");
                     return ERROR_END_OF_STREAM;
                 }
-                ALOGW("Timed out waiting for incoming camera video frames: %lld us",
+                ALOGW("Timed out waiting for incoming camera video frames: %" PRId64 " us",
                     mLastFrameTimestampUs);
             }
         }
@@ -832,10 +834,10 @@
 
 void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
         int32_t msgType, const sp<IMemory> &data) {
-    ALOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
+    ALOGV("dataCallbackTimestamp: timestamp %" PRId64 " us", timestampUs);
     Mutex::Autolock autoLock(mLock);
     if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
-        ALOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
+        ALOGV("Drop frame at %" PRId64 "/%" PRId64 " us", timestampUs, mStartTimeUs);
         releaseOneRecordingFrame(data);
         return;
     }
@@ -874,7 +876,7 @@
     mFramesReceived.push_back(data);
     int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
     mFrameTimes.push_back(timeUs);
-    ALOGV("initial delay: %lld, current time stamp: %lld",
+    ALOGV("initial delay: %" PRId64 ", current time stamp: %" PRId64,
         mStartTimeUs, timeUs);
     mFrameAvailableCondition.signal();
 }
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 15ba967..0acd9d0 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "CameraSourceTimeLapse"
 
@@ -79,7 +81,7 @@
       mSkipCurrentFrame(false) {
 
     mTimeBetweenFrameCaptureUs = timeBetweenFrameCaptureUs;
-    ALOGD("starting time lapse mode: %lld us",
+    ALOGD("starting time lapse mode: %" PRId64 " us",
         mTimeBetweenFrameCaptureUs);
 
     mVideoWidth = videoSize.width;
@@ -266,7 +268,7 @@
 
             // Really make sure that this video recording frame will not be dropped.
             if (*timestampUs < mStartTimeUs) {
-                ALOGI("set timestampUs to start time stamp %lld us", mStartTimeUs);
+                ALOGI("set timestampUs to start time stamp %" PRId64 " us", mStartTimeUs);
                 *timestampUs = mStartTimeUs;
             }
             return false;
diff --git a/media/libstagefright/CodecBase.cpp b/media/libstagefright/CodecBase.cpp
new file mode 100644
index 0000000..f729d4d
--- /dev/null
+++ b/media/libstagefright/CodecBase.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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 "CodecBase"
+
+#include <inttypes.h>
+
+#include <media/stagefright/CodecBase.h>
+
+namespace android {
+
+CodecBase::CodecBase() {
+}
+
+CodecBase::~CodecBase() {
+}
+
+CodecBase::PortDescription::PortDescription() {
+}
+
+CodecBase::PortDescription::~PortDescription() {
+}
+
+}  // namespace android
diff --git a/media/libstagefright/DataURISource.cpp b/media/libstagefright/DataURISource.cpp
index 377bc85..2c39314 100644
--- a/media/libstagefright/DataURISource.cpp
+++ b/media/libstagefright/DataURISource.cpp
@@ -85,7 +85,7 @@
 }
 
 ssize_t DataURISource::readAt(off64_t offset, void *data, size_t size) {
-    if (offset >= mBuffer->size()) {
+    if ((offset < 0) || (offset >= (off64_t)mBuffer->size())) {
         return 0;
     }
 
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
index 4a0c35c..427bf7b 100644
--- a/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/ESDS.cpp
@@ -91,7 +91,7 @@
     }
     while (more);
 
-    ALOGV("tag=0x%02x data_size=%d", *tag, *data_size);
+    ALOGV("tag=0x%02x data_size=%zu", *tag, *data_size);
 
     if (*data_size > size) {
         return ERROR_MALFORMED;
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 78c12e1..9856f92 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -682,7 +682,7 @@
                     break;
                 }
 
-                ALOGV("writing access unit at time %.2f secs (index %d)",
+                ALOGV("writing access unit at time %.2f secs (index %zu)",
                      minTimeUs / 1E6, minIndex);
 
                 source = mSources.editItemAt(minIndex);
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 297f4fc..207acc8 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -16,17 +16,19 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MPEG4Extractor"
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include <utils/Log.h>
 
 #include "include/MPEG4Extractor.h"
 #include "include/SampleTable.h"
 #include "include/ESDS.h"
 
-#include <ctype.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -51,6 +53,7 @@
                 int32_t timeScale,
                 const sp<SampleTable> &sampleTable,
                 Vector<SidxEntry> &sidx,
+                const Trex *trex,
                 off64_t firstMoofOffset);
 
     virtual status_t start(MetaData *params = NULL);
@@ -74,6 +77,7 @@
     uint32_t mCurrentSampleIndex;
     uint32_t mCurrentFragmentIndex;
     Vector<SidxEntry> &mSegments;
+    const Trex *mTrex;
     off64_t mFirstMoofOffset;
     off64_t mCurrentMoofOffset;
     off64_t mNextMoofOffset;
@@ -141,6 +145,7 @@
         off64_t offset;
         size_t size;
         uint32_t duration;
+        int32_t compositionOffset;
         uint8_t iv[16];
         Vector<size_t> clearsizes;
         Vector<size_t> encryptedsizes;
@@ -343,8 +348,7 @@
 }
 
 MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
-    : mSidxDuration(0),
-      mMoofOffset(0),
+    : mMoofOffset(0),
       mDataSource(source),
       mInitCheck(NO_INIT),
       mHasVideo(false),
@@ -369,7 +373,7 @@
     SINF *sinf = mFirstSINF;
     while (sinf) {
         SINF *next = sinf->next;
-        delete sinf->IPMPData;
+        delete[] sinf->IPMPData;
         delete sinf;
         sinf = next;
     }
@@ -409,7 +413,7 @@
         track = track->next;
     }
 
-    ALOGV("MPEG4Extractor::countTracks: %d tracks", n);
+    ALOGV("MPEG4Extractor::countTracks: %zu tracks", n);
     return n;
 }
 
@@ -694,7 +698,10 @@
                 return ERROR_MALFORMED;
             }
             sinf->len = dataLen - 3;
-            sinf->IPMPData = new char[sinf->len];
+            sinf->IPMPData = new (std::nothrow) char[sinf->len];
+            if (sinf->IPMPData == NULL) {
+                return ERROR_MALFORMED;
+            }
             data_offset += 2;
 
             if (mDataSource->readAt(data_offset, sinf->IPMPData, sinf->len) < sinf->len) {
@@ -787,7 +794,7 @@
         }
     } else if (chunk_size < 8) {
         // The smallest valid chunk is 8 bytes long.
-        ALOGE("invalid chunk size: %d", int(chunk_size));
+        ALOGE("invalid chunk size: %" PRIu64, chunk_size);
         return ERROR_MALFORMED;
     }
 
@@ -798,7 +805,7 @@
 #if 0
     static const char kWhitespace[] = "                                        ";
     const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
-    printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
+    printf("%sfound chunk '%s' of size %" PRIu64 "\n", indent, chunk, chunk_size);
 
     char buffer[256];
     size_t n = chunk_size;
@@ -854,7 +861,7 @@
         case FOURCC('e', 'd', 't', 's'):
         {
             if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
-                ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
+                ALOGV("sampleTable chunk is %" PRIu64 " bytes long.", chunk_size);
 
                 if (mDataSource->flags()
                         & (DataSource::kWantsPrefetching
@@ -1112,7 +1119,10 @@
                 return ERROR_MALFORMED;
             }
 
-            pssh.data = new uint8_t[pssh.datalen];
+            pssh.data = new (std::nothrow) uint8_t[pssh.datalen];
+            if (pssh.data == NULL) {
+                return ERROR_MALFORMED;
+            }
             ALOGV("allocated pssh @ %p", pssh.data);
             ssize_t requested = (ssize_t) pssh.datalen;
             if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
@@ -1157,6 +1167,8 @@
 
             mLastTrack->timescale = ntohl(timescale);
 
+            // 14496-12 says all ones means indeterminate, but some files seem to use
+            // 0 instead. We treat both the same.
             int64_t duration = 0;
             if (version == 1) {
                 if (mDataSource->readAt(
@@ -1164,7 +1176,9 @@
                         < (ssize_t)sizeof(duration)) {
                     return ERROR_IO;
                 }
-                duration = ntoh64(duration);
+                if (duration != -1) {
+                    duration = ntoh64(duration);
+                }
             } else {
                 uint32_t duration32;
                 if (mDataSource->readAt(
@@ -1172,13 +1186,14 @@
                         < (ssize_t)sizeof(duration32)) {
                     return ERROR_IO;
                 }
-                // ffmpeg sets duration to -1, which is incorrect.
                 if (duration32 != 0xffffffff) {
                     duration = ntohl(duration32);
                 }
             }
-            mLastTrack->meta->setInt64(
-                    kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
+            if (duration != 0) {
+                mLastTrack->meta->setInt64(
+                        kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
+            }
 
             uint8_t lang[2];
             off64_t lang_offset;
@@ -1718,11 +1733,11 @@
         {
             *offset += chunk_size;
 
-            if (chunk_data_size < 24) {
+            if (chunk_data_size < 32) {
                 return ERROR_MALFORMED;
             }
 
-            uint8_t header[24];
+            uint8_t header[32];
             if (mDataSource->readAt(
                         data_offset, header, sizeof(header))
                     < (ssize_t)sizeof(header)) {
@@ -1730,14 +1745,27 @@
             }
 
             uint64_t creationTime;
+            uint64_t duration = 0;
             if (header[0] == 1) {
                 creationTime = U64_AT(&header[4]);
                 mHeaderTimescale = U32_AT(&header[20]);
+                duration = U64_AT(&header[24]);
+                if (duration == 0xffffffffffffffff) {
+                    duration = 0;
+                }
             } else if (header[0] != 0) {
                 return ERROR_MALFORMED;
             } else {
                 creationTime = U32_AT(&header[4]);
                 mHeaderTimescale = U32_AT(&header[12]);
+                uint32_t d32 = U32_AT(&header[16]);
+                if (d32 == 0xffffffff) {
+                    d32 = 0;
+                }
+                duration = d32;
+            }
+            if (duration != 0) {
+                mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
             }
 
             String8 s;
@@ -1748,6 +1776,50 @@
             break;
         }
 
+        case FOURCC('m', 'e', 'h', 'd'):
+        {
+            *offset += chunk_size;
+
+            if (chunk_data_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t flags[4];
+            if (mDataSource->readAt(
+                        data_offset, flags, sizeof(flags))
+                    < (ssize_t)sizeof(flags)) {
+                return ERROR_IO;
+            }
+
+            uint64_t duration = 0;
+            if (flags[0] == 1) {
+                // 64 bit
+                if (chunk_data_size < 12) {
+                    return ERROR_MALFORMED;
+                }
+                mDataSource->getUInt64(data_offset + 4, &duration);
+                if (duration == 0xffffffffffffffff) {
+                    duration = 0;
+                }
+            } else if (flags[0] == 0) {
+                // 32 bit
+                uint32_t d32;
+                mDataSource->getUInt32(data_offset + 4, &d32);
+                if (d32 == 0xffffffff) {
+                    d32 = 0;
+                }
+                duration = d32;
+            } else {
+                return ERROR_MALFORMED;
+            }
+
+            if (duration != 0) {
+                mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+            }
+
+            break;
+        }
+
         case FOURCC('m', 'd', 'a', 't'):
         {
             ALOGV("mdat chunk, drm: %d", mIsDrm);
@@ -1784,6 +1856,26 @@
             break;
         }
 
+        case FOURCC('t', 'r', 'e', 'x'):
+        {
+            *offset += chunk_size;
+
+            if (chunk_data_size < 24) {
+                return ERROR_IO;
+            }
+            uint32_t duration;
+            Trex trex;
+            if (!mDataSource->getUInt32(data_offset + 4, &trex.track_ID) ||
+                !mDataSource->getUInt32(data_offset + 8, &trex.default_sample_description_index) ||
+                !mDataSource->getUInt32(data_offset + 12, &trex.default_sample_duration) ||
+                !mDataSource->getUInt32(data_offset + 16, &trex.default_sample_size) ||
+                !mDataSource->getUInt32(data_offset + 20, &trex.default_sample_flags)) {
+                return ERROR_IO;
+            }
+            mTrex.add(trex);
+            break;
+        }
+
         case FOURCC('t', 'x', '3', 'g'):
         {
             uint32_t type;
@@ -1794,7 +1886,10 @@
                 size = 0;
             }
 
-            uint8_t *buffer = new uint8_t[size + chunk_size];
+            uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
+            if (buffer == NULL) {
+                return ERROR_MALFORMED;
+            }
 
             if (size > 0) {
                 memcpy(buffer, data, size);
@@ -1959,7 +2054,7 @@
         offset += 16;
         size -= 16;
     }
-    ALOGV("sidx pres/off: %Ld/%Ld", earliestPresentationTime, firstOffset);
+    ALOGV("sidx pres/off: %" PRIu64 "/%" PRIu64, earliestPresentationTime, firstOffset);
 
     if (size < 4) {
         return -EINVAL;
@@ -2005,12 +2100,11 @@
         mSidxEntries.add(se);
     }
 
-    mSidxDuration = total_duration * 1000000 / timeScale;
-    ALOGV("duration: %lld", mSidxDuration);
+    uint64_t sidxDuration = total_duration * 1000000 / timeScale;
 
     int64_t metaDuration;
     if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
-        mLastTrack->meta->setInt64(kKeyDuration, mSidxDuration);
+        mLastTrack->meta->setInt64(kKeyDuration, sidxDuration);
     }
     return OK;
 }
@@ -2111,7 +2205,10 @@
         return ERROR_MALFORMED;
     }
 
-    uint8_t *buffer = new uint8_t[size + 1];
+    uint8_t *buffer = new (std::nothrow) uint8_t[size + 1];
+    if (buffer == NULL) {
+        return ERROR_MALFORMED;
+    }
     if (mDataSource->readAt(
                 offset, buffer, size) != (ssize_t)size) {
         delete[] buffer;
@@ -2298,7 +2395,10 @@
         return ERROR_MALFORMED;
     }
 
-    uint8_t *buffer = new uint8_t[size];
+    uint8_t *buffer = new (std::nothrow) uint8_t[size];
+    if (buffer == NULL) {
+        return ERROR_MALFORMED;
+    }
     if (mDataSource->readAt(
                 offset, buffer, size) != (ssize_t)size) {
         delete[] buffer;
@@ -2477,11 +2577,24 @@
         return NULL;
     }
 
-    ALOGV("getTrack called, pssh: %d", mPssh.size());
+
+    Trex *trex = NULL;
+    int32_t trackId;
+    if (track->meta->findInt32(kKeyTrackID, &trackId)) {
+        for (size_t i = 0; i < mTrex.size(); i++) {
+            Trex *t = &mTrex.editItemAt(index);
+            if (t->track_ID == (uint32_t) trackId) {
+                trex = t;
+                break;
+            }
+        }
+    }
+
+    ALOGV("getTrack called, pssh: %zu", mPssh.size());
 
     return new MPEG4Source(
             track->meta, mDataSource, track->timescale, track->sampleTable,
-            mSidxEntries, mMoofOffset);
+            mSidxEntries, trex, mMoofOffset);
 }
 
 // static
@@ -2811,6 +2924,7 @@
         int32_t timeScale,
         const sp<SampleTable> &sampleTable,
         Vector<SidxEntry> &sidx,
+        const Trex *trex,
         off64_t firstMoofOffset)
     : mFormat(format),
       mDataSource(dataSource),
@@ -2819,6 +2933,7 @@
       mCurrentSampleIndex(0),
       mCurrentFragmentIndex(0),
       mSegments(sidx),
+      mTrex(trex),
       mFirstMoofOffset(firstMoofOffset),
       mCurrentMoofOffset(firstMoofOffset),
       mCurrentTime(0),
@@ -2835,6 +2950,8 @@
       mWantsNALFragments(false),
       mSrcBuffer(NULL) {
 
+    memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
+
     mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
     mDefaultIVSize = 0;
     mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
@@ -2917,7 +3034,11 @@
 
     mGroup->add_buffer(new MediaBuffer(max_size));
 
-    mSrcBuffer = new uint8_t[max_size];
+    mSrcBuffer = new (std::nothrow) uint8_t[max_size];
+    if (mSrcBuffer == NULL) {
+        // file probably specified a bad max size
+        return ERROR_MALFORMED;
+    }
 
     mStarted = true;
 
@@ -3402,8 +3523,8 @@
     } else if (mTrackFragmentHeaderInfo.mFlags
             & TrackFragmentHeaderInfo::kDefaultSampleDurationPresent) {
         sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
-    } else {
-        sampleDuration = mTrackFragmentHeaderInfo.mDefaultSampleDuration;
+    } else if (mTrex) {
+        sampleDuration = mTrex->default_sample_duration;
     }
 
     if (flags & kSampleSizePresent) {
@@ -3430,7 +3551,7 @@
         sampleCtsOffset = 0;
     }
 
-    if (size < sampleCount * bytesPerSample) {
+    if (size < (off64_t)sampleCount * bytesPerSample) {
         return -EINVAL;
     }
 
@@ -3464,7 +3585,7 @@
             offset += 4;
         }
 
-        ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, "
+        ALOGV("adding sample %d at offset 0x%08" PRIx64 ", size %u, duration %u, "
               " flags 0x%08x", i + 1,
                 dataOffset, sampleSize, sampleDuration,
                 (flags & kFirstSampleFlagsPresent) && i == 0
@@ -3472,6 +3593,7 @@
         tmp.offset = dataOffset;
         tmp.size = sampleSize;
         tmp.duration = sampleDuration;
+        tmp.compositionOffset = sampleCtsOffset;
         mCurrentSamples.add(tmp);
 
         dataOffset += sampleSize;
@@ -3874,7 +3996,7 @@
         const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
         offset = smpl->offset;
         size = smpl->size;
-        cts = mCurrentTime;
+        cts = mCurrentTime + smpl->compositionOffset;
         mCurrentTime += smpl->duration;
         isSyncSample = (mCurrentSampleIndex == 0); // XXX
 
@@ -4181,7 +4303,7 @@
 
         char chunkstring[5];
         MakeFourCCString(chunkType, chunkstring);
-        ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset);
+        ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, offset);
         switch (chunkType) {
             case FOURCC('f', 't', 'y', 'p'):
             {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 24e53b3..4b8440b 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -16,13 +16,17 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MPEG4Writer"
-#include <inttypes.h>
-#include <utils/Log.h>
 
 #include <arpa/inet.h>
-
+#include <fcntl.h>
+#include <inttypes.h>
 #include <pthread.h>
 #include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utils/Log.h>
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MPEG4Writer.h>
@@ -34,10 +38,6 @@
 #include <media/stagefright/Utils.h>
 #include <media/mediarecorder.h>
 #include <cutils/properties.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
 
 #include "include/ESDS.h"
 
@@ -441,7 +441,7 @@
 
     // At most 2 tracks can be supported.
     if (mTracks.size() >= 2) {
-        ALOGE("Too many tracks (%d) to add", mTracks.size());
+        ALOGE("Too many tracks (%zu) to add", mTracks.size());
         return ERROR_UNSUPPORTED;
     }
 
@@ -555,8 +555,8 @@
         size = MAX_MOOV_BOX_SIZE;
     }
 
-    ALOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
-         " moov size %lld bytes",
+    ALOGI("limits: %" PRId64 "/%" PRId64 " bytes/us, bit rate: %d bps and the"
+         " estimated moov size %" PRId64 " bytes",
          mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
     return factor * size;
 }
@@ -592,8 +592,8 @@
         // If file size is set to be larger than the 32 bit file
         // size limit, treat it as an error.
         if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
-            ALOGW("32-bit file size limit (%lld bytes) too big. "
-                 "It is changed to %lld bytes",
+            ALOGW("32-bit file size limit (%" PRId64 " bytes) too big. "
+                 "It is changed to %" PRId64 " bytes",
                 mMaxFileSizeLimitBytes, kMax32BitFileSize);
             mMaxFileSizeLimitBytes = kMax32BitFileSize;
         }
@@ -854,7 +854,7 @@
     }
 
     if (mTracks.size() > 1) {
-        ALOGD("Duration from tracks range is [%lld, %lld] us",
+        ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us",
             minDurationUs, maxDurationUs);
     }
 
@@ -1321,12 +1321,12 @@
 }
 
 void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
-    ALOGI("setStartTimestampUs: %lld", timeUs);
+    ALOGI("setStartTimestampUs: %" PRId64, timeUs);
     CHECK_GE(timeUs, 0ll);
     Mutex::Autolock autoLock(mLock);
     if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
         mStartTimestampUs = timeUs;
-        ALOGI("Earliest track starting time: %lld", mStartTimestampUs);
+        ALOGI("Earliest track starting time: %" PRId64, mStartTimestampUs);
     }
 }
 
@@ -1527,7 +1527,7 @@
     {
         int64_t timeUs;
         if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
-            ALOGV("Receive request to track progress status for every %lld us", timeUs);
+            ALOGV("Receive request to track progress status for every %" PRId64 " us", timeUs);
             mTrackEveryTimeDurationUs = timeUs;
             mTrackingProgressStatus = true;
         }
@@ -1561,7 +1561,7 @@
 }
 
 void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
-    ALOGV("writeChunkToFile: %lld from %s track",
+    ALOGV("writeChunkToFile: %" PRId64 " from %s track",
         chunk->mTimeStampUs, chunk->mTrack->isAudio()? "audio": "video");
 
     int32_t isFirstSample = true;
@@ -1737,7 +1737,7 @@
             startTimeOffsetUs = kInitialDelayTimeUs;
         }
         startTimeUs += startTimeOffsetUs;
-        ALOGI("Start time offset: %lld us", startTimeOffsetUs);
+        ALOGI("Start time offset: %" PRId64 " us", startTimeOffsetUs);
     }
 
     meta->setInt64(kKeyTime, startTimeUs);
@@ -1817,7 +1817,7 @@
 static const uint8_t *findNextStartCode(
         const uint8_t *data, size_t length) {
 
-    ALOGV("findNextStartCode: %p %d", data, length);
+    ALOGV("findNextStartCode: %p %zu", data, length);
 
     size_t bytesLeft = length;
     while (bytesLeft > 4  &&
@@ -2238,7 +2238,7 @@
             }
 
             timestampUs = decodingTimeUs;
-            ALOGV("decoding time: %lld and ctts offset time: %lld",
+            ALOGV("decoding time: %" PRId64 " and ctts offset time: %" PRId64,
                 timestampUs, cttsOffsetTimeUs);
 
             // Update ctts box table if necessary
@@ -2291,7 +2291,7 @@
             return ERROR_MALFORMED;
         }
 
-        ALOGV("%s media time stamp: %lld and previous paused duration %lld",
+        ALOGV("%s media time stamp: %" PRId64 " and previous paused duration %" PRId64,
                 trackName, timestampUs, previousPausedDurationUs);
         if (timestampUs > mTrackDurationUs) {
             mTrackDurationUs = timestampUs;
@@ -2306,7 +2306,7 @@
             ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
                 (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
         if (currDurationTicks < 0ll) {
-            ALOGE("timestampUs %lld < lastTimestampUs %lld for %s track",
+            ALOGE("timestampUs %" PRId64 " < lastTimestampUs %" PRId64 " for %s track",
                 timestampUs, lastTimestampUs, trackName);
             copy->release();
             return UNKNOWN_ERROR;
@@ -2347,7 +2347,7 @@
             }
             previousSampleSize = sampleSize;
         }
-        ALOGV("%s timestampUs/lastTimestampUs: %lld/%lld",
+        ALOGV("%s timestampUs/lastTimestampUs: %" PRId64 "/%" PRId64,
                 trackName, timestampUs, lastTimestampUs);
         lastDurationUs = timestampUs - lastTimestampUs;
         lastDurationTicks = currDurationTicks;
@@ -2455,7 +2455,7 @@
     ALOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
             count, nZeroLengthFrames, mStszTableEntries->count(), trackName);
     if (mIsAudio) {
-        ALOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs());
+        ALOGI("Audio track drift time: %" PRId64 " us", mOwner->getDriftTimeUs());
     }
 
     if (err == ERROR_END_OF_STREAM) {
@@ -2538,11 +2538,11 @@
 }
 
 void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
-    ALOGV("trackProgressStatus: %lld us", timeUs);
+    ALOGV("trackProgressStatus: %" PRId64 " us", timeUs);
 
     if (mTrackEveryTimeDurationUs > 0 &&
         timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
-        ALOGV("Fire time tracking progress status at %lld us", timeUs);
+        ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs);
         mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
         mPreviousTrackTimeUs = timeUs;
     }
@@ -2576,13 +2576,13 @@
 }
 
 void MPEG4Writer::setDriftTimeUs(int64_t driftTimeUs) {
-    ALOGV("setDriftTimeUs: %lld us", driftTimeUs);
+    ALOGV("setDriftTimeUs: %" PRId64 " us", driftTimeUs);
     Mutex::Autolock autolock(mLock);
     mDriftTimeUs = driftTimeUs;
 }
 
 int64_t MPEG4Writer::getDriftTimeUs() {
-    ALOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
+    ALOGV("getDriftTimeUs: %" PRId64 " us", mDriftTimeUs);
     Mutex::Autolock autolock(mLock);
     return mDriftTimeUs;
 }
@@ -3038,7 +3038,7 @@
         return;
     }
 
-    ALOGV("ctts box has %d entries with range [%lld, %lld]",
+    ALOGV("ctts box has %d entries with range [%" PRId64 ", %" PRId64 "]",
             mCttsTableEntries->count(), mMinCttsOffsetTimeUs, mMaxCttsOffsetTimeUs);
 
     mOwner->beginBox("ctts");
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index 8af0880..1f80a47 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -134,7 +134,7 @@
 
 void MediaBuffer::set_range(size_t offset, size_t length) {
     if ((mGraphicBuffer == NULL) && (offset + length > mSize)) {
-        ALOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
+        ALOGE("offset = %zu, length = %zu, mSize = %zu", offset, length, mSize);
     }
     CHECK((mGraphicBuffer != NULL) || (offset + length <= mSize));
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b9c5904..6e3d6f8 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -67,7 +67,7 @@
 MediaCodec::MediaCodec(const sp<ALooper> &looper)
     : mState(UNINITIALIZED),
       mLooper(looper),
-      mCodec(new ACodec),
+      mCodec(NULL),
       mReplyID(0),
       mFlags(0),
       mSoftRenderer(NULL),
@@ -103,6 +103,7 @@
     // quickly, violating the OpenMAX specs, until that is remedied
     // we need to invest in an extra looper to free the main event
     // queue.
+    mCodec = new ACodec;
     bool needDedicatedLooper = false;
     if (nameIsType && !strncasecmp(name, "video/", 6)) {
         needDedicatedLooper = true;
@@ -541,7 +542,7 @@
             CHECK(msg->findInt32("what", &what));
 
             switch (what) {
-                case ACodec::kWhatError:
+                case CodecBase::kWhatError:
                 {
                     int32_t omxError, internalError;
                     CHECK(msg->findInt32("omx-error", &omxError));
@@ -638,7 +639,7 @@
                     break;
                 }
 
-                case ACodec::kWhatComponentAllocated:
+                case CodecBase::kWhatComponentAllocated:
                 {
                     CHECK_EQ(mState, INITIALIZING);
                     setState(INITIALIZED);
@@ -661,7 +662,7 @@
                     break;
                 }
 
-                case ACodec::kWhatComponentConfigured:
+                case CodecBase::kWhatComponentConfigured:
                 {
                     CHECK_EQ(mState, CONFIGURING);
                     setState(CONFIGURED);
@@ -676,9 +677,9 @@
                     break;
                 }
 
-                case ACodec::kWhatInputSurfaceCreated:
+                case CodecBase::kWhatInputSurfaceCreated:
                 {
-                    // response to ACodec::kWhatCreateInputSurface
+                    // response to initiateCreateInputSurface()
                     status_t err = NO_ERROR;
                     sp<AMessage> response = new AMessage();
                     if (!msg->findInt32("err", &err)) {
@@ -694,9 +695,9 @@
                     break;
                 }
 
-                case ACodec::kWhatSignaledInputEOS:
+                case CodecBase::kWhatSignaledInputEOS:
                 {
-                    // response to ACodec::kWhatSignalEndOfInputStream
+                    // response to signalEndOfInputStream()
                     sp<AMessage> response = new AMessage();
                     status_t err;
                     if (msg->findInt32("err", &err)) {
@@ -707,7 +708,7 @@
                 }
 
 
-                case ACodec::kWhatBuffersAllocated:
+                case CodecBase::kWhatBuffersAllocated:
                 {
                     int32_t portIndex;
                     CHECK(msg->findInt32("portIndex", &portIndex));
@@ -725,8 +726,8 @@
                     sp<RefBase> obj;
                     CHECK(msg->findObject("portDesc", &obj));
 
-                    sp<ACodec::PortDescription> portDesc =
-                        static_cast<ACodec::PortDescription *>(obj.get());
+                    sp<CodecBase::PortDescription> portDesc =
+                        static_cast<CodecBase::PortDescription *>(obj.get());
 
                     size_t numBuffers = portDesc->countBuffers();
 
@@ -759,7 +760,7 @@
                     break;
                 }
 
-                case ACodec::kWhatOutputFormatChanged:
+                case CodecBase::kWhatOutputFormatChanged:
                 {
                     ALOGV("codec output format changed");
 
@@ -810,7 +811,7 @@
                     break;
                 }
 
-                case ACodec::kWhatFillThisBuffer:
+                case CodecBase::kWhatFillThisBuffer:
                 {
                     /* size_t index = */updateBuffers(kPortIndexInput, msg);
 
@@ -857,7 +858,7 @@
                     break;
                 }
 
-                case ACodec::kWhatDrainThisBuffer:
+                case CodecBase::kWhatDrainThisBuffer:
                 {
                     /* size_t index = */updateBuffers(kPortIndexOutput, msg);
 
@@ -908,14 +909,14 @@
                     break;
                 }
 
-                case ACodec::kWhatEOS:
+                case CodecBase::kWhatEOS:
                 {
                     // We already notify the client of this by using the
                     // corresponding flag in "onOutputBufferReady".
                     break;
                 }
 
-                case ACodec::kWhatShutdownCompleted:
+                case CodecBase::kWhatShutdownCompleted:
                 {
                     if (mState == STOPPING) {
                         setState(INITIALIZED);
@@ -928,7 +929,7 @@
                     break;
                 }
 
-                case ACodec::kWhatFlushCompleted:
+                case CodecBase::kWhatFlushCompleted:
                 {
                     CHECK_EQ(mState, FLUSHING);
                     setState(STARTED);
@@ -1456,7 +1457,7 @@
         ++i;
     }
 
-    ALOGV("Found %u pieces of codec specific data.", mCSD.size());
+    ALOGV("Found %zu pieces of codec specific data.", mCSD.size());
 }
 
 status_t MediaCodec::queueCSDInputBuffer(size_t bufferIndex) {
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 924173c..9868ecf 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -17,6 +17,9 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaCodecSource"
 #define DEBUG_DRIFT_TIME 0
+
+#include <inttypes.h>
+
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
@@ -677,7 +680,7 @@
                     }
                     mbuf->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
 
-                    ALOGV("[video] time %lld us (%.2f secs), dts/pts diff %lld",
+                    ALOGV("[video] time %" PRId64 " us (%.2f secs), dts/pts diff %" PRId64,
                             timeUs, timeUs / 1E6, decodingTimeUs - timeUs);
                 } else {
                     int64_t driftTimeUs = 0;
@@ -687,7 +690,7 @@
                     mDriftTimeQueue.erase(mDriftTimeQueue.begin());
                     mbuf->meta_data()->setInt64(kKeyDriftTime, driftTimeUs);
 #endif // DEBUG_DRIFT_TIME
-                    ALOGV("[audio] time %lld us (%.2f secs), drift %lld",
+                    ALOGV("[audio] time %" PRId64 " us (%.2f secs), drift %" PRId64,
                             timeUs, timeUs / 1E6, driftTimeUs);
                 }
                 mbuf->meta_data()->setInt64(kKeyTime, timeUs);
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 90335ee..c7c6f34 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -176,7 +176,7 @@
     }
 
     if (trackIndex >= mTrackList.size()) {
-        ALOGE("WriteSampleData() get an invalid index %d", trackIndex);
+        ALOGE("WriteSampleData() get an invalid index %zu", trackIndex);
         return -EINVAL;
     }
 
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 61cf0ad..c1feff8 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NuCachedSource2"
 #include <utils/Log.h>
@@ -135,7 +137,7 @@
 }
 
 void PageCache::copy(size_t from, void *data, size_t size) {
-    ALOGV("copy from %d size %d", from, size);
+    ALOGV("copy from %zu size %zu", from, size);
 
     if (size == 0) {
         return;
@@ -333,7 +335,7 @@
             mNumRetriesLeft = 0;
         }
 
-        ALOGE("source returned error %d, %d retries left", n, mNumRetriesLeft);
+        ALOGE("source returned error %zd, %d retries left", n, mNumRetriesLeft);
         mCache->releasePage(page);
     } else if (n == 0) {
         ALOGI("ERROR_END_OF_STREAM");
@@ -464,14 +466,14 @@
     size_t actualBytes = mCache->releaseFromStart(maxBytes);
     mCacheOffset += actualBytes;
 
-    ALOGI("restarting prefetcher, totalSize = %d", mCache->totalSize());
+    ALOGI("restarting prefetcher, totalSize = %zu", mCache->totalSize());
     mFetching = true;
 }
 
 ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
     Mutex::Autolock autoSerializer(mSerializer);
 
-    ALOGV("readAt offset %lld, size %d", offset, size);
+    ALOGV("readAt offset %lld, size %zu", offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -539,7 +541,7 @@
 ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
     CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
 
-    ALOGV("readInternal offset %lld size %d", offset, size);
+    ALOGV("readInternal offset %lld size %zu", offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -679,7 +681,7 @@
         mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
     }
 
-    ALOGV("lowwater = %d bytes, highwater = %d bytes, keepalive = %lld us",
+    ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %" PRId64 " us",
          mLowwaterThresholdBytes,
          mHighwaterThresholdBytes,
          mKeepAliveIntervalUs);
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 64f56e9..f24cf3a 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -389,7 +389,7 @@
                 info->mFinalResult = err;
 
                 if (info->mFinalResult != ERROR_END_OF_STREAM) {
-                    ALOGW("read on track %d failed with error %d",
+                    ALOGW("read on track %zu failed with error %d",
                           info->mTrackIndex, err);
                 }
 
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index f3eeb03..8c15929 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -338,7 +338,7 @@
 
     const TOCEntry &entry = mTableOfContents.itemAt(left);
 
-    ALOGV("seeking to entry %d / %d at offset %lld",
+    ALOGV("seeking to entry %zu / %zu at offset %lld",
          left, mTableOfContents.size(), entry.mPageOffset);
 
     return seekToOffset(entry.mPageOffset);
@@ -381,7 +381,7 @@
     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 %d bytes",
+        ALOGV("failed to read %zu bytes at offset 0x%016llx, got %zd bytes",
              sizeof(header), offset, n);
 
         if (n < 0) {
@@ -505,7 +505,7 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                ALOGV("failed to read %zu bytes at 0x%016llx, got %d bytes",
+                ALOGV("failed to read %zu bytes at 0x%016llx, got %zd bytes",
                      packetSize, dataOffset, n);
                 return ERROR_IO;
             }
@@ -546,7 +546,7 @@
                 buffer = NULL;
             }
 
-            ALOGV("readPage returned %d", n);
+            ALOGV("readPage returned %zd", n);
 
             return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
         }
@@ -590,7 +590,7 @@
     if ((err = readNextPacket(&packet)) != OK) {
         return err;
     }
-    ALOGV("read packet of size %d\n", packet->range_length());
+    ALOGV("read packet of size %zu\n", packet->range_length());
     err = verifyHeader(packet, 1);
     packet->release();
     packet = NULL;
@@ -601,7 +601,7 @@
     if ((err = readNextPacket(&packet)) != OK) {
         return err;
     }
-    ALOGV("read packet of size %d\n", packet->range_length());
+    ALOGV("read packet of size %zu\n", packet->range_length());
     err = verifyHeader(packet, 3);
     packet->release();
     packet = NULL;
@@ -612,7 +612,7 @@
     if ((err = readNextPacket(&packet)) != OK) {
         return err;
     }
-    ALOGV("read packet of size %d\n", packet->range_length());
+    ALOGV("read packet of size %zu\n", packet->range_length());
     err = verifyHeader(packet, 5);
     packet->release();
     packet = NULL;
@@ -903,7 +903,7 @@
         return;
     }
 
-    ALOGV("got flac of size %d", flacSize);
+    ALOGV("got flac of size %zu", flacSize);
 
     uint32_t picType;
     uint32_t typeLen;
@@ -953,7 +953,7 @@
         goto exit;
     }
 
-    ALOGV("got image data, %d trailing bytes",
+    ALOGV("got image data, %zu trailing bytes",
          flacSize - 32 - typeLen - descLen - dataLen);
 
     fileMeta->setData(
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index af96225..8cc41e7 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -16,7 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "StagefrightMetadataRetriever"
+
 #include <inttypes.h>
+
 #include <utils/Log.h>
 
 #include "include/StagefrightMetadataRetriever.h"
@@ -87,7 +89,7 @@
         int fd, int64_t offset, int64_t length) {
     fd = dup(fd);
 
-    ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
 
     mParsedMetaData = false;
     mMetaData.clear();
@@ -242,7 +244,7 @@
             const char *mime;
             CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
 
-            ALOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s",
+            ALOGV("thumbNailTime = %" PRId64 " us, timeUs = %" PRId64 " us, mime = %s",
                  thumbNailTime, timeUs, mime);
         }
     }
@@ -325,7 +327,7 @@
 VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
         int64_t timeUs, int option) {
 
-    ALOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
+    ALOGV("getFrameAtTime: %" PRId64 " us option: %d", timeUs, option);
 
     if (mExtractor.get() == NULL) {
         ALOGV("no extractor.");
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 62aea36..4e1c65c 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -16,6 +16,8 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "SurfaceMediaSource"
 
+#include <inttypes.h>
+
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/SurfaceMediaSource.h>
 #include <media/stagefright/MediaDefs.h>
@@ -179,7 +181,7 @@
 }
 
 status_t SurfaceMediaSource::setMaxAcquiredBufferCount(size_t count) {
-    ALOGV("setMaxAcquiredBufferCount(%d)", count);
+    ALOGV("setMaxAcquiredBufferCount(%zu)", count);
     Mutex::Autolock lock(mMutex);
 
     CHECK_GT(count, 1);
@@ -209,7 +211,7 @@
     mFrameAvailableCondition.signal();
 
     while (mNumPendingBuffers > 0) {
-        ALOGI("Still waiting for %d buffers to be returned.",
+        ALOGI("Still waiting for %zu buffers to be returned.",
                 mNumPendingBuffers);
 
 #if DEBUG_PENDING_BUFFERS
@@ -269,7 +271,7 @@
     memcpy(data, &type, 4);
     memcpy(data + 4, &bufferHandle, sizeof(buffer_handle_t));
 
-    ALOGV("handle = %p, , offset = %d, length = %d",
+    ALOGV("handle = %p, , offset = %zu, length = %zu",
             bufferHandle, (*buffer)->range_length(), (*buffer)->range_offset());
 }
 
@@ -363,7 +365,7 @@
     (*buffer)->setObserver(this);
     (*buffer)->add_ref();
     (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000);
-    ALOGV("Frames encoded = %d, timestamp = %lld, time diff = %lld",
+    ALOGV("Frames encoded = %d, timestamp = %" PRId64 ", time diff = %" PRId64,
             mNumFramesEncoded, mCurrentTimestamp / 1000,
             mCurrentTimestamp / 1000 - prevTimeStamp / 1000);
 
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 3d2eb1f..da50c56 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -17,7 +17,11 @@
 #undef __STRICT_ANSI__
 #define __STDINT_LIMITS
 #define __STDC_LIMIT_MACROS
+
+#include <inttypes.h>
 #include <stdint.h>
+#include <sys/prctl.h>
+#include <sys/time.h>
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "TimedEventQueue"
@@ -26,9 +30,6 @@
 
 #include "include/TimedEventQueue.h"
 
-#include <sys/prctl.h>
-#include <sys/time.h>
-
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <binder/IServiceManager.h>
@@ -258,7 +259,7 @@
                 static int64_t kMaxTimeoutUs = 10000000ll;  // 10 secs
                 bool timeoutCapped = false;
                 if (delay_us > kMaxTimeoutUs) {
-                    ALOGW("delay_us exceeds max timeout: %lld us", delay_us);
+                    ALOGW("delay_us exceeds max timeout: %" PRId64 " us", delay_us);
 
                     // We'll never block for more than 10 secs, instead
                     // we will split up the full timeout into chunks of
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 7ff31a1..d53051e 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -335,7 +335,7 @@
                 // there can't be another param here, so use all the rest
                 i = csd0->size();
             }
-            ALOGV("block at %d, last was %d", i, lastparamoffset);
+            ALOGV("block at %zu, last was %d", i, lastparamoffset);
             if (lastparamoffset > 0) {
                 int size = i - lastparamoffset;
                 avcc[avccidx++] = size >> 8;
@@ -366,7 +366,7 @@
                 // there can't be another param here, so use all the rest
                 i = csd1->size();
             }
-            ALOGV("block at %d, last was %d", i, lastparamoffset);
+            ALOGV("block at %zu, last was %d", i, lastparamoffset);
             if (lastparamoffset > 0) {
                 int size = i - lastparamoffset;
                 avcc[avccidx++] = size >> 8;
diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp
index af858b9..e988f6d 100644
--- a/media/libstagefright/VBRISeeker.cpp
+++ b/media/libstagefright/VBRISeeker.cpp
@@ -16,6 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "VBRISeeker"
+
+#include <inttypes.h>
+
 #include <utils/Log.h>
 
 #include "include/VBRISeeker.h"
@@ -75,7 +78,7 @@
     size_t entrySize = U16_AT(&vbriHeader[22]);
     size_t scale = U16_AT(&vbriHeader[20]);
 
-    ALOGV("%d entries, scale=%d, size_per_entry=%d",
+    ALOGV("%zu entries, scale=%zu, size_per_entry=%zu",
          numEntries,
          scale,
          entrySize);
@@ -119,7 +122,7 @@
 
         seeker->mSegments.push(numBytes);
 
-        ALOGV("entry #%d: %u offset 0x%016llx", i, numBytes, offset);
+        ALOGV("entry #%zu: %u offset 0x%016llx", i, numBytes, offset);
         offset += numBytes;
     }
 
@@ -160,7 +163,7 @@
         *pos += mSegments.itemAt(segmentIndex++);
     }
 
-    ALOGV("getOffsetForTime %lld us => 0x%016llx", *timeUs, *pos);
+    ALOGV("getOffsetForTime %" PRId64 " us => 0x%016llx", *timeUs, *pos);
 
     *timeUs = nowUs;
 
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index fe9058b..7124fd3 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -414,7 +414,7 @@
         } else {
             pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * (mBitsPerSample >> 3);
         }
-        if (pos > mSize) {
+        if (pos > (off64_t)mSize) {
             pos = mSize;
         }
         mCurrentPos = pos + mOffset;
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 64bf2b6..ab30865 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -65,10 +65,9 @@
       mInputBufferCount(0),
       mOutputBufferCount(0),
       mSignalledError(false),
+      mLastInHeader(NULL),
+      mCurrentInputTime(0),
       mOutputPortSettingsChange(NONE) {
-    for (unsigned int i = 0; i < kNumDelayBlocksMax; i++) {
-        mAnchorTimeUs[i] = 0;
-    }
     initPorts();
     CHECK_EQ(initDecoder(), (status_t)OK);
 }
@@ -496,14 +495,11 @@
             } else {
                 mEndOfInput = false;
             }
-            if (inHeader->nOffset == 0) { // TODO: does nOffset != 0 happen?
-                mAnchorTimeUs[mInputBufferCount % kNumDelayBlocksMax] =
-                        inHeader->nTimeStamp;
-            }
 
             if (inHeader->nFilledLen == 0) {
                 inInfo->mOwnedByUs = false;
                 inQueue.erase(inQueue.begin());
+                mLastInHeader = NULL;
                 inInfo = NULL;
                 notifyEmptyBufferDone(inHeader);
                 inHeader = NULL;
@@ -568,6 +564,18 @@
                 INT prevSampleRate = mStreamInfo->sampleRate;
                 INT prevNumChannels = mStreamInfo->numChannels;
 
+                if (inHeader != mLastInHeader) {
+                    mLastInHeader = inHeader;
+                    mCurrentInputTime = inHeader->nTimeStamp;
+                } else {
+                    if (mStreamInfo->sampleRate) {
+                        mCurrentInputTime += mStreamInfo->aacSamplesPerFrame *
+                                1000000ll / mStreamInfo->sampleRate;
+                    } else {
+                        ALOGW("no sample rate yet");
+                    }
+                }
+                mAnchorTimes.add(mCurrentInputTime);
                 aacDecoder_Fill(mAACDecoder,
                                 inBuffer,
                                 inBufferLength,
@@ -671,6 +679,7 @@
                             inInfo->mOwnedByUs = false;
                             mInputBufferCount++;
                             inQueue.erase(inQueue.begin());
+                            mLastInHeader = NULL;
                             inInfo = NULL;
                             notifyEmptyBufferDone(inHeader);
                             inHeader = NULL;
@@ -687,11 +696,12 @@
                     inInfo->mOwnedByUs = false;
                     mInputBufferCount++;
                     inQueue.erase(inQueue.begin());
+                    mLastInHeader = NULL;
                     inInfo = NULL;
                     notifyEmptyBufferDone(inHeader);
                     inHeader = NULL;
                 } else {
-                    ALOGW("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+                    ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen);
                 }
             }
         }
@@ -779,8 +789,8 @@
                 outHeader->nFlags = 0;
             }
 
-            outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
-                    % kNumDelayBlocksMax];
+            outHeader->nTimeStamp = mAnchorTimes.isEmpty() ? 0 : mAnchorTimes.itemAt(0);
+            mAnchorTimes.removeAt(0);
 
             mOutputBufferCount++;
             outInfo->mOwnedByUs = false;
@@ -820,8 +830,8 @@
                     outHeader->nFilledLen = 0;
                     outHeader->nFlags = OMX_BUFFERFLAG_EOS;
 
-                    outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
-                            % kNumDelayBlocksMax];
+                    outHeader->nTimeStamp = mAnchorTimes.itemAt(0);
+                    mAnchorTimes.removeAt(0);
 
                     mOutputBufferCount++;
                     outInfo->mOwnedByUs = false;
@@ -842,6 +852,8 @@
         // depend on fragments from the last one decoded.
         // drain all existing data
         drainDecoder();
+        mAnchorTimes.clear();
+        mLastInHeader = NULL;
     } else {
         while (outputDelayRingBufferSamplesAvailable() > 0) {
             int32_t ns = outputDelayRingBufferGetSamples(0,
@@ -896,6 +908,8 @@
     mOutputDelayRingBufferReadPos = 0;
     mEndOfInput = false;
     mEndOfOutput = false;
+    mAnchorTimes.clear();
+    mLastInHeader = NULL;
 
     // To make the codec behave the same before and after a reset, we need to invalidate the
     // streaminfo struct. This does that:
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 5cde03a..865bd15 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -58,7 +58,9 @@
     size_t mInputBufferCount;
     size_t mOutputBufferCount;
     bool mSignalledError;
-    int64_t mAnchorTimeUs[kNumDelayBlocksMax];
+    OMX_BUFFERHEADERTYPE *mLastInHeader;
+    int64_t mCurrentInputTime;
+    Vector<int64_t> mAnchorTimes;
 
     CDrcPresModeWrapper mDrcWrap;
 
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index dc38ea8..cabd6bd 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -379,7 +379,7 @@
         }
         default:
         {
-            ALOGE("Wrong number of temporal layers %u", mTemporalLayers);
+            ALOGE("Wrong number of temporal layers %zu", mTemporalLayers);
             return UNKNOWN_ERROR;
         }
     }
diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
index 5499c32..beb5cc0 100644
--- a/media/libstagefright/foundation/ABitReader.cpp
+++ b/media/libstagefright/foundation/ABitReader.cpp
@@ -27,6 +27,9 @@
       mNumBitsLeft(0) {
 }
 
+ABitReader::~ABitReader() {
+}
+
 void ABitReader::fillReservoir() {
     CHECK_GT(mSize, 0u);
 
@@ -99,4 +102,69 @@
     return mData - (mNumBitsLeft + 7) / 8;
 }
 
+NALBitReader::NALBitReader(const uint8_t *data, size_t size)
+    : ABitReader(data, size),
+      mNumZeros(0) {
+}
+
+bool NALBitReader::atLeastNumBitsLeft(size_t n) const {
+    // check against raw size and reservoir bits first
+    size_t numBits = numBitsLeft();
+    if (n > numBits) {
+        return false;
+    }
+
+    ssize_t numBitsRemaining = n - mNumBitsLeft;
+
+    size_t size = mSize;
+    const uint8_t *data = mData;
+    int32_t numZeros = mNumZeros;
+    while (size > 0 && numBitsRemaining > 0) {
+        bool isEmulationPreventionByte = (numZeros >= 2 && *data == 3);
+
+        if (*data == 0) {
+            ++numZeros;
+        } else {
+            numZeros = 0;
+        }
+
+        if (!isEmulationPreventionByte) {
+            numBitsRemaining -= 8;
+        }
+
+        ++data;
+        --size;
+    }
+
+    return (numBitsRemaining <= 0);
+}
+
+void NALBitReader::fillReservoir() {
+    CHECK_GT(mSize, 0u);
+
+    mReservoir = 0;
+    size_t i = 0;
+    while (mSize > 0 && i < 4) {
+        bool isEmulationPreventionByte = (mNumZeros >= 2 && *mData == 3);
+
+        if (*mData == 0) {
+            ++mNumZeros;
+        } else {
+            mNumZeros = 0;
+        }
+
+        // skip emulation_prevention_three_byte
+        if (!isEmulationPreventionByte) {
+            mReservoir = (mReservoir << 8) | *mData;
+            ++i;
+        }
+
+        ++mData;
+        --mSize;
+    }
+
+    mNumBitsLeft = 8 * i;
+    mReservoir <<= 32 - mNumBitsLeft;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
index f7a00d8..5f7c70d 100644
--- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
+++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
@@ -51,7 +51,7 @@
 AHierarchicalStateMachine::~AHierarchicalStateMachine() {
 }
 
-void AHierarchicalStateMachine::onMessageReceived(const sp<AMessage> &msg) {
+void AHierarchicalStateMachine::handleMessage(const sp<AMessage> &msg) {
     sp<AState> save = mState;
 
     sp<AState> cur = mState;
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index f2d501e..894f65c 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -328,6 +328,20 @@
     return !strcmp(mData + mSize - suffixLen, suffix);
 }
 
+bool AString::startsWithIgnoreCase(const char *prefix) const {
+    return !strncasecmp(mData, prefix, strlen(prefix));
+}
+
+bool AString::endsWithIgnoreCase(const char *suffix) const {
+    size_t suffixLen = strlen(suffix);
+
+    if (mSize < suffixLen) {
+        return false;
+    }
+
+    return !strcasecmp(mData + mSize - suffixLen, suffix);
+}
+
 AString StringPrintf(const char *format, ...) {
     va_list ap;
     va_start(ap, format);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 326d85b..10437c9 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -588,7 +588,10 @@
 void PlaylistFetcher::queueDiscontinuity(
         ATSParser::DiscontinuityType type, const sp<AMessage> &extra) {
     for (size_t i = 0; i < mPacketSources.size(); ++i) {
-        mPacketSources.valueAt(i)->queueDiscontinuity(type, extra);
+        // do not discard buffer upon #EXT-X-DISCONTINUITY tag
+        // (seek will discard buffer by abandoning old fetchers)
+        mPacketSources.valueAt(i)->queueDiscontinuity(
+                type, extra, false /* discard */);
     }
 }
 
@@ -723,8 +726,7 @@
         firstSeqNumberInPlaylist = 0;
     }
 
-    bool seekDiscontinuity = false;
-    bool explicitDiscontinuity = false;
+    bool discontinuity = false;
 
     const int32_t lastSeqNumberInPlaylist =
         firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
@@ -792,7 +794,7 @@
             if (mSeqNumber < firstSeqNumberInPlaylist) {
                 mSeqNumber = firstSeqNumberInPlaylist;
             }
-            explicitDiscontinuity = true;
+            discontinuity = true;
 
             // fall through
         } else {
@@ -817,7 +819,7 @@
 
     int32_t val;
     if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
-        explicitDiscontinuity = true;
+        discontinuity = true;
     }
 
     int64_t range_offset, range_length;
@@ -877,7 +879,7 @@
             return;
         }
 
-        if (mStartup || seekDiscontinuity || explicitDiscontinuity) {
+        if (mStartup || discontinuity) {
             // Signal discontinuity.
 
             if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
@@ -887,15 +889,14 @@
                 mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
             }
 
-            if (seekDiscontinuity || explicitDiscontinuity) {
-                ALOGI("queueing discontinuity (seek=%d, explicit=%d)",
-                     seekDiscontinuity, explicitDiscontinuity);
+            if (discontinuity) {
+                ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
 
                 queueDiscontinuity(
-                        explicitDiscontinuity
-                            ? ATSParser::DISCONTINUITY_FORMATCHANGE
-                            : ATSParser::DISCONTINUITY_SEEK,
+                        ATSParser::DISCONTINUITY_FORMATCHANGE,
                         NULL /* extra */);
+
+                discontinuity = false;
             }
         }
 
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 7b4bc6d..1fe6fcf 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -39,6 +39,14 @@
     uint32_t mDurationUs;
 };
 
+struct Trex {
+    uint32_t track_ID;
+    uint32_t default_sample_description_index;
+    uint32_t default_sample_duration;
+    uint32_t default_sample_size;
+    uint32_t default_sample_flags;
+};
+
 class MPEG4Extractor : public MediaExtractor {
 public:
     // Extractor assumes ownership of "source".
@@ -74,11 +82,12 @@
     };
 
     Vector<SidxEntry> mSidxEntries;
-    uint64_t mSidxDuration;
     off64_t mMoofOffset;
 
     Vector<PsshInfo> mPssh;
 
+    Vector<Trex> mTrex;
+
     sp<DataSource> mDataSource;
     status_t mInitCheck;
     bool mHasVideo;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index d7bec59..2587ec7 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -20,8 +20,6 @@
 
 #include "MatroskaExtractor.h"
 
-#include "mkvparser.hpp"
-
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/DataSource.h>
@@ -89,7 +87,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 struct BlockIterator {
-    BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum);
+    BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum, unsigned long index);
 
     bool eos() const;
 
@@ -106,6 +104,7 @@
 private:
     MatroskaExtractor *mExtractor;
     long long mTrackNum;
+    unsigned long mIndex;
 
     const mkvparser::Cluster *mCluster;
     const mkvparser::BlockEntry *mBlockEntry;
@@ -157,6 +156,53 @@
     MatroskaSource &operator=(const MatroskaSource &);
 };
 
+const mkvparser::Track* MatroskaExtractor::TrackInfo::getTrack() const {
+    return mExtractor->mSegment->GetTracks()->GetTrackByNumber(mTrackNum);
+}
+
+// This function does exactly the same as mkvparser::Cues::Find, except that it
+// searches in our own track based vectors. We should not need this once mkvparser
+// adds the same functionality.
+const mkvparser::CuePoint::TrackPosition *MatroskaExtractor::TrackInfo::find(
+        long long timeNs) const {
+    ALOGV("mCuePoints.size %zu", mCuePoints.size());
+    if (mCuePoints.empty()) {
+        return NULL;
+    }
+
+    const mkvparser::CuePoint* cp = mCuePoints.itemAt(0);
+    const mkvparser::Track* track = getTrack();
+    if (timeNs <= cp->GetTime(mExtractor->mSegment)) {
+        return cp->Find(track);
+    }
+
+    // Binary searches through relevant cues; assumes cues are ordered by timecode.
+    // If we do detect out-of-order cues, return NULL.
+    size_t lo = 0;
+    size_t hi = mCuePoints.size();
+    while (lo < hi) {
+        const size_t mid = lo + (hi - lo) / 2;
+        const mkvparser::CuePoint* const midCp = mCuePoints.itemAt(mid);
+        const long long cueTimeNs = midCp->GetTime(mExtractor->mSegment);
+        if (cueTimeNs <= timeNs) {
+            lo = mid + 1;
+        } else {
+            hi = mid;
+        }
+    }
+
+    if (lo == 0) {
+        return NULL;
+    }
+
+    cp = mCuePoints.itemAt(lo - 1);
+    if (cp->GetTime(mExtractor->mSegment) > timeNs) {
+        return NULL;
+    }
+
+    return cp->Find(track);
+}
+
 MatroskaSource::MatroskaSource(
         const sp<MatroskaExtractor> &extractor, size_t index)
     : mExtractor(extractor),
@@ -164,7 +210,8 @@
       mType(OTHER),
       mIsAudio(false),
       mBlockIter(mExtractor.get(),
-                 mExtractor->mTracks.itemAt(index).mTrackNum),
+                 mExtractor->mTracks.itemAt(index).mTrackNum,
+                 index),
       mNALSizeLen(0) {
     sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta;
 
@@ -214,9 +261,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 BlockIterator::BlockIterator(
-        MatroskaExtractor *extractor, unsigned long trackNum)
+        MatroskaExtractor *extractor, unsigned long trackNum, unsigned long index)
     : mExtractor(extractor),
       mTrackNum(trackNum),
+      mIndex(index),
       mCluster(NULL),
       mBlockEntry(NULL),
       mBlockEntryIndex(0) {
@@ -364,9 +412,20 @@
     }
 
     const mkvparser::CuePoint* pCP;
+    mkvparser::Tracks const *pTracks = pSegment->GetTracks();
+    unsigned long int trackCount = pTracks->GetTracksCount();
     while (!pCues->DoneParsing()) {
         pCues->LoadCuePoint();
         pCP = pCues->GetLast();
+        CHECK(pCP);
+
+        for (size_t index = 0; index < trackCount; ++index) {
+            const mkvparser::Track *pTrack = pTracks->GetTrackByIndex(index);
+            if (pTrack && pTrack->GetType() == 1 && pCP->Find(pTrack)) { // VIDEO_TRACK
+                MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(index);
+                track.mCuePoints.push_back(pCP);
+            }
+        }
 
         if (pCP->GetTime(pSegment) >= seekTimeNs) {
             ALOGV("Parsed past relevant Cue");
@@ -374,22 +433,25 @@
         }
     }
 
-    // The Cue index is built around video keyframes
-    mkvparser::Tracks const *pTracks = pSegment->GetTracks();
-    const mkvparser::Track *pTrack = NULL;
-    for (size_t index = 0; index < pTracks->GetTracksCount(); ++index) {
-        pTrack = pTracks->GetTrackByIndex(index);
-        if (pTrack && pTrack->GetType() == 1) { // VIDEO_TRACK
-            ALOGV("Video track located at %zu", index);
-            break;
+    const mkvparser::CuePoint::TrackPosition *pTP = NULL;
+    const mkvparser::Track *thisTrack = pTracks->GetTrackByIndex(mIndex);
+    if (thisTrack->GetType() == 1) { // video
+        MatroskaExtractor::TrackInfo& track = mExtractor->mTracks.editItemAt(mIndex);
+        pTP = track.find(seekTimeNs);
+    } else {
+        // The Cue index is built around video keyframes
+        for (size_t index = 0; index < trackCount; ++index) {
+            const mkvparser::Track *pTrack = pTracks->GetTrackByIndex(index);
+            if (pTrack && pTrack->GetType() == 1 && pCues->Find(seekTimeNs, pTrack, pCP, pTP)) {
+                ALOGV("Video track located at %zu", index);
+                break;
+            }
         }
     }
 
+
     // Always *search* based on the video track, but finalize based on mTrackNum
-    const mkvparser::CuePoint::TrackPosition* pTP;
-    if (pTrack && pTrack->GetType() == 1) {
-        pCues->Find(seekTimeNs, pTrack, pCP, pTP);
-    } else {
+    if (!pTP) {
         ALOGE("Did not locate the video track for seeking");
         return;
     }
@@ -410,10 +472,13 @@
 
         if (isAudio || block()->IsKey()) {
             // Accept the first key frame
-            *actualFrameTimeUs = (block()->GetTime(mCluster) + 500LL) / 1000LL;
-            ALOGV("Requested seek point: %" PRId64 " actual: %" PRId64,
-                  seekTimeUs, *actualFrameTimeUs);
-            break;
+            int64_t frameTimeUs = (block()->GetTime(mCluster) + 500LL) / 1000LL;
+            if (thisTrack->GetType() == 1 || frameTimeUs >= seekTimeUs) {
+                *actualFrameTimeUs = frameTimeUs;
+                ALOGV("Requested seek point: %" PRId64 " actual: %" PRId64,
+                      seekTimeUs, *actualFrameTimeUs);
+                break;
+            }
         }
     }
 }
@@ -964,6 +1029,7 @@
         TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
         trackInfo->mTrackNum = track->GetNumber();
         trackInfo->mMeta = meta;
+        trackInfo->mExtractor = this;
     }
 }
 
@@ -978,7 +1044,7 @@
             continue;
         }
 
-        BlockIterator iter(this, info->mTrackNum);
+        BlockIterator iter(this, info->mTrackNum, i);
         int32_t j = 0;
         int64_t thumbnailTimeUs = 0;
         size_t maxBlockSize = 0;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index cf200f3..db36bf8 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -18,14 +18,12 @@
 
 #define MATROSKA_EXTRACTOR_H_
 
+#include "mkvparser.hpp"
+
 #include <media/stagefright/MediaExtractor.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
 
-namespace mkvparser {
-struct Segment;
-};
-
 namespace android {
 
 struct AMessage;
@@ -58,6 +56,11 @@
     struct TrackInfo {
         unsigned long mTrackNum;
         sp<MetaData> mMeta;
+        const MatroskaExtractor *mExtractor;
+        Vector<const mkvparser::CuePoint*> mCuePoints;
+
+        const mkvparser::Track* getTrack() const;
+        const mkvparser::CuePoint::TrackPosition *find(long long timeNs) const;
     };
 
     Mutex mLock;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 338e899..3d241e0 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -669,7 +669,7 @@
     }
 
     if (mSource != NULL) {
-        mSource->queueDiscontinuity(type, extra);
+        mSource->queueDiscontinuity(type, extra, true);
     }
 }
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 021b640..871824a 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -217,22 +217,25 @@
 
 void AnotherPacketSource::queueDiscontinuity(
         ATSParser::DiscontinuityType type,
-        const sp<AMessage> &extra) {
+        const sp<AMessage> &extra,
+        bool discard) {
     Mutex::Autolock autoLock(mLock);
 
-    // Leave only discontinuities in the queue.
-    List<sp<ABuffer> >::iterator it = mBuffers.begin();
-    while (it != mBuffers.end()) {
-        sp<ABuffer> oldBuffer = *it;
+    if (discard) {
+        // Leave only discontinuities in the queue.
+        List<sp<ABuffer> >::iterator it = mBuffers.begin();
+        while (it != mBuffers.end()) {
+            sp<ABuffer> oldBuffer = *it;
 
-        int32_t oldDiscontinuityType;
-        if (!oldBuffer->meta()->findInt32(
-                    "discontinuity", &oldDiscontinuityType)) {
-            it = mBuffers.erase(it);
-            continue;
+            int32_t oldDiscontinuityType;
+            if (!oldBuffer->meta()->findInt32(
+                        "discontinuity", &oldDiscontinuityType)) {
+                it = mBuffers.erase(it);
+                continue;
+            }
+
+            ++it;
         }
-
-        ++it;
     }
 
     mEOSResult = OK;
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 9b193a2..06c49bd 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -54,7 +54,9 @@
     void queueAccessUnit(const sp<ABuffer> &buffer);
 
     void queueDiscontinuity(
-            ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
+            ATSParser::DiscontinuityType type,
+            const sp<AMessage> &extra,
+            bool discard);
 
     void signalEOS(status_t result);
 
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 16f6c58..67e6d7b 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -114,7 +114,7 @@
 
 void GraphicBufferSource::omxExecuting() {
     Mutex::Autolock autoLock(mMutex);
-    ALOGV("--> executing; avail=%d, codec vec size=%zd",
+    ALOGV("--> executing; avail=%zu, codec vec size=%zd",
             mNumFramesAvailable, mCodecBuffers.size());
     CHECK(!mExecuting);
     mExecuting = true;
@@ -136,7 +136,7 @@
         }
     }
 
-    ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable);
+    ALOGV("done loading initial frames, avail=%zu", mNumFramesAvailable);
 
     // If EOS has already been signaled, and there are no more frames to
     // submit, try to send EOS now as well.
@@ -188,7 +188,7 @@
         mLooper.clear();
     }
 
-    ALOGV("--> loaded; avail=%d eos=%d eosSent=%d",
+    ALOGV("--> loaded; avail=%zu eos=%d eosSent=%d",
             mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
 
     // Codec is no longer executing.  Discard all codec-related state.
@@ -291,7 +291,7 @@
     if (mNumFramesAvailable) {
         // Fill this codec buffer.
         CHECK(!mEndOfStreamSent);
-        ALOGV("buffer freed, %d frames avail (eos=%d)",
+        ALOGV("buffer freed, %zu frames avail (eos=%d)",
                 mNumFramesAvailable, mEndOfStream);
         fillCodecBuffer_l();
     } else if (mEndOfStream) {
@@ -320,7 +320,8 @@
         ssize_t index = mOriginalTimeUs.indexOfKey(header->nTimeStamp);
         if (index >= 0) {
             ALOGV("OUT timestamp: %lld -> %lld",
-                    header->nTimeStamp, mOriginalTimeUs[index]);
+                    static_cast<long long>(header->nTimeStamp),
+                    static_cast<long long>(mOriginalTimeUs[index]));
             header->nTimeStamp = mOriginalTimeUs[index];
             mOriginalTimeUs.removeItemsAt(index);
         } else {
@@ -331,7 +332,7 @@
         }
         if (mOriginalTimeUs.size() > BufferQueue::NUM_BUFFER_SLOTS) {
             // something terribly wrong must have happened, giving up...
-            ALOGE("mOriginalTimeUs has too many entries (%d)",
+            ALOGE("mOriginalTimeUs has too many entries (%zu)",
                     mOriginalTimeUs.size());
             mMaxTimestampGapUs = -1ll;
         }
@@ -388,12 +389,12 @@
     int cbi = findAvailableCodecBuffer_l();
     if (cbi < 0) {
         // No buffers available, bail.
-        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d",
+        ALOGV("fillCodecBuffer_l: no codec buffers, avail now %zu",
                 mNumFramesAvailable);
         return false;
     }
 
-    ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d",
+    ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%zu",
             mNumFramesAvailable);
     BufferQueue::BufferItem item;
     status_t err = mConsumer->acquireBuffer(&item, 0);
@@ -540,7 +541,7 @@
 
 status_t GraphicBufferSource::signalEndOfInputStream() {
     Mutex::Autolock autoLock(mMutex);
-    ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d",
+    ALOGV("signalEndOfInputStream: exec=%d avail=%zu eos=%d",
             mExecuting, mNumFramesAvailable, mEndOfStream);
 
     if (mEndOfStream) {
@@ -580,7 +581,7 @@
                     / mTimePerCaptureUs;
             if (nFrames <= 0) {
                 // skip this frame as it's too close to previous capture
-                ALOGV("skipping frame, timeUs %lld", timeUs);
+                ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
                 return -1;
             }
             mPrevCaptureUs = mPrevCaptureUs + nFrames * mTimePerCaptureUs;
@@ -588,7 +589,9 @@
         }
 
         ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
-                timeUs, mPrevCaptureUs, mPrevFrameUs);
+                static_cast<long long>(timeUs),
+                static_cast<long long>(mPrevCaptureUs),
+                static_cast<long long>(mPrevFrameUs));
 
         return mPrevFrameUs;
     } else if (mMaxTimestampGapUs > 0ll) {
@@ -615,7 +618,9 @@
         mPrevOriginalTimeUs = originalTimeUs;
         mPrevModifiedTimeUs = timeUs;
         mOriginalTimeUs.add(timeUs, originalTimeUs);
-        ALOGV("IN  timestamp: %lld -> %lld", originalTimeUs, timeUs);
+        ALOGV("IN  timestamp: %lld -> %lld",
+            static_cast<long long>(originalTimeUs),
+            static_cast<long long>(timeUs));
     }
 
     return timeUs;
@@ -723,7 +728,7 @@
 void GraphicBufferSource::onFrameAvailable() {
     Mutex::Autolock autoLock(mMutex);
 
-    ALOGV("onFrameAvailable exec=%d avail=%d",
+    ALOGV("onFrameAvailable exec=%d avail=%zu",
             mExecuting, mNumFramesAvailable);
 
     if (mEndOfStream || mSuspended) {
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 6b6d0ab..ae3cb33 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -91,7 +91,7 @@
     }
 
     if (err != OMX_ErrorNoMore) {
-        ALOGE("OMX plugin failed w/ error 0x%08x after registering %d "
+        ALOGE("OMX plugin failed w/ error 0x%08x after registering %zu "
              "components", err, mPluginByComponentName.size());
     }
 }
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index c4f87a0..e6e19e3 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -363,7 +363,7 @@
 }
 
 int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
-    allocate(length);
+    allocate(length + MTP_CONTAINER_HEADER_SIZE);
     memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length);
     length += MTP_CONTAINER_HEADER_SIZE;
     MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 157f2ce..aa43967 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -325,6 +325,14 @@
         mSendObjectHandle = kInvalidObjectHandle;
     }
 
+    int containertype = mRequest.getContainerType();
+    if (containertype != MTP_CONTAINER_TYPE_COMMAND) {
+        ALOGE("wrong container type %d", containertype);
+        return false;
+    }
+
+    ALOGV("got command %s (%x)", MtpDebug::getOperationCodeName(operation), operation);
+
     switch (operation) {
         case MTP_OPERATION_GET_DEVICE_INFO:
             response = doGetDeviceInfo();
@@ -415,7 +423,8 @@
             response = doEndEditObject();
             break;
         default:
-            ALOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
+            ALOGE("got unsupported command %s (%x)",
+                    MtpDebug::getOperationCodeName(operation), operation);
             response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
             break;
     }
@@ -793,7 +802,7 @@
     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
     if (result != MTP_RESPONSE_OK)
         return result;
-    if (offset + length > fileLength)
+    if (offset + length > (uint64_t)fileLength)
         length = fileLength - offset;
 
     const char* filePath = (const char *)pathBuf;
@@ -950,22 +959,28 @@
     fchmod(mfr.fd, mFilePermission);
     umask(mask);
 
-    if (initialData > 0)
+    if (initialData > 0) {
         ret = write(mfr.fd, mData.getData(), initialData);
+    }
 
-    if (mSendObjectFileSize - initialData > 0) {
-        mfr.offset = initialData;
-        if (mSendObjectFileSize == 0xFFFFFFFF) {
-            // tell driver to read until it receives a short packet
-            mfr.length = 0xFFFFFFFF;
-        } else {
-            mfr.length = mSendObjectFileSize - initialData;
+    if (ret < 0) {
+        ALOGE("failed to write initial data");
+        result = MTP_RESPONSE_GENERAL_ERROR;
+    } else {
+        if (mSendObjectFileSize - initialData > 0) {
+            mfr.offset = initialData;
+            if (mSendObjectFileSize == 0xFFFFFFFF) {
+                // tell driver to read until it receives a short packet
+                mfr.length = 0xFFFFFFFF;
+            } else {
+                mfr.length = mSendObjectFileSize - initialData;
+            }
+
+            ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
+            // transfer the file
+            ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+            ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
         }
-
-        ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
-        // transfer the file
-        ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
-        ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
     }
     close(mfr.fd);
 
@@ -990,7 +1005,7 @@
 
 static void deleteRecursive(const char* path) {
     char pathbuf[PATH_MAX];
-    int pathLength = strlen(path);
+    size_t pathLength = strlen(path);
     if (pathLength >= sizeof(pathbuf) - 1) {
         ALOGE("path too long: %s\n", path);
     }
@@ -1112,12 +1127,13 @@
 
     // can't start writing past the end of the file
     if (offset > edit->mSize) {
-        ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
+        ALOGD("writing past end of object, offset: %" PRIu64 ", edit->mSize: %" PRIu64,
+            offset, edit->mSize);
         return MTP_RESPONSE_GENERAL_ERROR;
     }
 
     const char* filePath = (const char *)edit->mPath;
-    ALOGV("receiving partial %s %lld %" PRIu32 "\n", filePath, offset, length);
+    ALOGV("receiving partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
 
     // read the header, and possibly some data
     int ret = mData.read(mFD);
@@ -1131,15 +1147,19 @@
         length -= initialData;
     }
 
-    if (length > 0) {
-        mtp_file_range  mfr;
-        mfr.fd = edit->mFD;
-        mfr.offset = offset;
-        mfr.length = length;
+    if (ret < 0) {
+        ALOGE("failed to write initial data");
+    } else {
+        if (length > 0) {
+            mtp_file_range  mfr;
+            mfr.fd = edit->mFD;
+            mfr.offset = offset;
+            mfr.length = length;
 
-        // transfer the file
-        ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
-        ALOGV("MTP_RECEIVE_FILE returned %d", ret);
+            // transfer the file
+            ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+            ALOGV("MTP_RECEIVE_FILE returned %d", ret);
+        }
     }
     if (ret < 0) {
         mResponse.setParameter(1, 0);
diff --git a/media/mtp/MtpStorageInfo.cpp b/media/mtp/MtpStorageInfo.cpp
index dcd37cd..2b1a9ae 100644
--- a/media/mtp/MtpStorageInfo.cpp
+++ b/media/mtp/MtpStorageInfo.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "MtpStorageInfo"
 
+#include <inttypes.h>
+
 #include "MtpDebug.h"
 #include "MtpDataPacket.h"
 #include "MtpStorageInfo.h"
@@ -63,7 +65,7 @@
 void MtpStorageInfo::print() {
     ALOGD("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
             mStorageID, mStorageType, mFileSystemType, mAccessCapability);
-    ALOGD("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n",
+    ALOGD("\tmMaxCapacity: %" PRIu64 "\n\tmFreeSpaceBytes: %" PRIu64 "\n\tmFreeSpaceObjects: %d\n",
             mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects);
     ALOGD("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
             mStorageDescription, mVolumeIdentifier);
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index c5d8858..ed00b72 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaCodec"
 
@@ -257,7 +259,7 @@
     if (mData->mCodec->getInputBuffers(&abufs) == 0) {
         size_t n = abufs.size();
         if (idx >= n) {
-            ALOGE("buffer index %d out of range", idx);
+            ALOGE("buffer index %zu out of range", idx);
             return NULL;
         }
         if (out_size != NULL) {
@@ -275,7 +277,7 @@
     if (mData->mCodec->getOutputBuffers(&abufs) == 0) {
         size_t n = abufs.size();
         if (idx >= n) {
-            ALOGE("buffer index %d out of range", idx);
+            ALOGE("buffer index %zu out of range", idx);
             return NULL;
         }
         if (out_size != NULL) {
@@ -345,7 +347,7 @@
 EXPORT
 media_status_t AMediaCodec_releaseOutputBufferAtTime(
         AMediaCodec *mData, size_t idx, int64_t timestampNs) {
-    ALOGV("render @ %lld", timestampNs);
+    ALOGV("render @ %" PRId64, timestampNs);
     return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
 }
 
@@ -413,7 +415,7 @@
     size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
     AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
     if (!ret) {
-        ALOGE("couldn't allocate %d bytes", cryptosize);
+        ALOGE("couldn't allocate %zu bytes", cryptosize);
         return NULL;
     }
     ret->numsubsamples = numsubsamples;
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 492e002..970a43c 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -133,13 +133,13 @@
 
 EXPORT
 media_status_t AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
-    ALOGV("selectTrack(%z)", idx);
+    ALOGV("selectTrack(%zu)", idx);
     return translate_error(mData->mImpl->selectTrack(idx));
 }
 
 EXPORT
 media_status_t AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
-    ALOGV("unselectTrack(%z)", idx);
+    ALOGV("unselectTrack(%zu)", idx);
     return translate_error(mData->mImpl->unselectTrack(idx));
 }
 
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 67dc2c2..a354d58 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaFormat"
 
+#include <inttypes.h>
 
 #include "NdkMediaFormat.h"
 
@@ -89,21 +90,21 @@
             {
                 int32_t val;
                 f->findInt32(name, &val);
-                ret.appendFormat("int32(%d)", val);
+                ret.appendFormat("int32(%" PRId32 ")", val);
                 break;
             }
             case AMessage::kTypeInt64:
             {
                 int64_t val;
                 f->findInt64(name, &val);
-                ret.appendFormat("int64(%lld)", val);
+                ret.appendFormat("int64(%" PRId64 ")", val);
                 break;
             }
             case AMessage::kTypeSize:
             {
                 size_t val;
                 f->findSize(name, &val);
-                ret.appendFormat("size_t(%d)", val);
+                ret.appendFormat("size_t(%zu)", val);
                 break;
             }
             case AMessage::kTypeFloat:
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 527fd65..2124f85 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -668,17 +668,6 @@
     return thread->sampleRate();
 }
 
-int AudioFlinger::channelCount(audio_io_handle_t output) const
-{
-    Mutex::Autolock _l(mLock);
-    PlaybackThread *thread = checkPlaybackThread_l(output);
-    if (thread == NULL) {
-        ALOGW("channelCount() unknown thread %d", output);
-        return 0;
-    }
-    return thread->channelCount();
-}
-
 audio_format_t AudioFlinger::format(audio_io_handle_t output) const
 {
     Mutex::Autolock _l(mLock);
@@ -1344,6 +1333,7 @@
         IAudioFlinger::track_flags_t *flags,
         pid_t tid,
         int *sessionId,
+        size_t *notificationFrames,
         sp<IMemory>& cblk,
         sp<IMemory>& buffers,
         status_t *status)
@@ -1418,7 +1408,7 @@
 
         // TODO: the uid should be passed in as a parameter to openRecord
         recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
-                                                  frameCount, lSessionId,
+                                                  frameCount, lSessionId, notificationFrames,
                                                   IPCThreadState::self()->getCallingUid(),
                                                   flags, tid, &lStatus);
         LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
@@ -1621,6 +1611,19 @@
     mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
 
     audio_stream_out_t *outStream = NULL;
+
+    // FOR TESTING ONLY:
+    // Enable increased sink precision for mixing mode if kEnableExtendedPrecision is true.
+    if (kEnableExtendedPrecision &&  // Check only for Normal Mixing mode
+            !(flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT))) {
+        // Update format
+        //config.format = AUDIO_FORMAT_PCM_FLOAT;
+        //config.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+        //config.format = AUDIO_FORMAT_PCM_32_BIT;
+        //config.format = AUDIO_FORMAT_PCM_8_24_BIT;
+        // ALOGV("openOutput() upgrading format to %#08x", config.format);
+    }
+
     status_t status = hwDevHal->open_output_stream(hwDevHal,
                                           id,
                                           *pDevices,
@@ -1644,9 +1647,9 @@
         if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
             thread = new OffloadThread(this, output, id, *pDevices);
             ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
-        } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
-            (config.format != AUDIO_FORMAT_PCM_16_BIT) ||
-            (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
+        } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
+                || !isValidPcmSinkFormat(config.format)
+                || (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
             thread = new DirectOutputThread(this, output, id, *pDevices);
             ALOGV("openOutput() created direct output: ID %d thread %p", id, thread);
         } else {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6e73a14..4df0921 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -120,12 +120,12 @@
                                 IAudioFlinger::track_flags_t *flags,
                                 pid_t tid,
                                 int *sessionId,
+                                size_t *notificationFrames,
                                 sp<IMemory>& cblk,
                                 sp<IMemory>& buffers,
                                 status_t *status /*non-NULL*/);
 
     virtual     uint32_t    sampleRate(audio_io_handle_t output) const;
-    virtual     int         channelCount(audio_io_handle_t output) const;
     virtual     audio_format_t format(audio_io_handle_t output) const;
     virtual     size_t      frameCount(audio_io_handle_t output) const;
     virtual     uint32_t    latency(audio_io_handle_t output) const;
@@ -326,6 +326,24 @@
                                                 audio_devices_t devices);
     void                    purgeStaleEffects_l();
 
+    // Set kEnableExtendedPrecision to true to use extended precision in MixerThread
+    static const bool kEnableExtendedPrecision = false;
+
+    // Returns true if format is permitted for the PCM sink in the MixerThread
+    static inline bool isValidPcmSinkFormat(audio_format_t format) {
+        switch (format) {
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return true;
+        case AUDIO_FORMAT_PCM_FLOAT:
+        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+        case AUDIO_FORMAT_PCM_32_BIT:
+        case AUDIO_FORMAT_PCM_8_24_BIT:
+            return kEnableExtendedPrecision;
+        default:
+            return false;
+        }
+    }
+
     // standby delay for MIXER and DUPLICATING playback threads is read from property
     // ro.audio.flinger_standbytime_ms or defaults to kDefaultStandbyTimeInNsecs
     static nsecs_t          mStandbyTimeInNsecs;
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index d73292e..4dbbc43 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -40,8 +40,36 @@
 
 #include <media/EffectsFactoryApi.h>
 
+#include "AudioMixerOps.h"
 #include "AudioMixer.h"
 
+// Use the FCC_2 macro for code assuming Fixed Channel Count of 2 and
+// whose stereo assumption may need to be revisited later.
+#ifndef FCC_2
+#define FCC_2 2
+#endif
+
+/* VERY_VERY_VERBOSE_LOGGING will show exactly which process hook and track hook is
+ * being used. This is a considerable amount of log spam, so don't enable unless you
+ * are verifying the hook based code.
+ */
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+//define ALOGVV printf  // for test-mixer.cpp
+#else
+#define ALOGVV(a...) do { } while (0)
+#endif
+
+// Set kUseNewMixer to true to use the new mixer engine. Otherwise the
+// original code will be used.  This is false for now.
+static const bool kUseNewMixer = false;
+
+// Set kUseFloat to true to allow floating input into the mixer engine.
+// If kUseNewMixer is false, this is ignored or may be overridden internally
+// because of downmix/upmix support.
+static const bool kUseFloat = true;
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -300,15 +328,19 @@
         t->downmixerBufferProvider = NULL;
         t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
         t->mFormat = format;
-        t->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT;
-        if (t->mFormat != t->mMixerInFormat) {
-            prepareTrackForReformat(t, n);
-        }
-        status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
+        t->mMixerInFormat = kUseFloat && kUseNewMixer
+                ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+        // Check the downmixing (or upmixing) requirements.
+        status_t status = initTrackDownmix(t, n, channelMask);
         if (status != OK) {
             ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
             return -1;
         }
+        // initTrackDownmix() may change the input format requirement.
+        // If you desire floating point input to the mixer, it may change
+        // to integer because the downmixer requires integer to process.
+        ALOGVV("mMixerFormat:%#x  mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
+        prepareTrackForReformat(t, n);
         mTrackNames |= 1 << n;
         return TRACK0 + n;
     }
@@ -443,6 +475,7 @@
     }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack"
 
     // initialization successful:
+    pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // 16 bit input is required for downmix
     pTrack->downmixerBufferProvider = pDbp;
     reconfigureBufferProviders(pTrack);
     return NO_ERROR;
@@ -467,12 +500,15 @@
 {
     ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat);
     // discard the previous reformatter if there was one
-     unprepareTrackForReformat(pTrack, trackName);
-     pTrack->mReformatBufferProvider = new ReformatBufferProvider(
-             audio_channel_count_from_out_mask(pTrack->channelMask),
-             pTrack->mFormat, pTrack->mMixerInFormat);
-     reconfigureBufferProviders(pTrack);
-     return NO_ERROR;
+    unprepareTrackForReformat(pTrack, trackName);
+    // only configure reformatter if needed
+    if (pTrack->mFormat != pTrack->mMixerInFormat) {
+        pTrack->mReformatBufferProvider = new ReformatBufferProvider(
+                audio_channel_count_from_out_mask(pTrack->channelMask),
+                pTrack->mFormat, pTrack->mMixerInFormat);
+        reconfigureBufferProviders(pTrack);
+    }
+    return NO_ERROR;
 }
 
 void AudioMixer::reconfigureBufferProviders(track_t* pTrack)
@@ -596,8 +632,15 @@
                 track.channelMask = mask;
                 track.channelCount = channelCount;
                 // the mask has changed, does this track need a downmixer?
-                initTrackDownmix(&mState.tracks[name], name, mask);
+                // update to try using our desired format (if we aren't already using it)
+                track.mMixerInFormat = kUseFloat && kUseNewMixer
+                        ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+                status_t status = initTrackDownmix(&mState.tracks[name], name, mask);
+                ALOGE_IF(status != OK,
+                        "Invalid channel mask %#x, initTrackDownmix returned %d",
+                        mask, status);
                 ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
+                prepareTrackForReformat(&track, name); // format may have changed
                 invalidateState(1 << name);
             }
             } break;
@@ -621,11 +664,7 @@
                 ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
                 track.mFormat = format;
                 ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
-                //if (track.mFormat != track.mMixerInFormat)
-                {
-                    ALOGD("Reformatting!");
-                    prepareTrackForReformat(&track, name);
-                }
+                prepareTrackForReformat(&track, name);
                 invalidateState(1 << name);
             }
             } break;
@@ -723,9 +762,10 @@
                 } else {
                     quality = AudioResampler::DEFAULT_QUALITY;
                 }
-                const int bits = mMixerInFormat == AUDIO_FORMAT_PCM_16_BIT ? 16 : /* FLOAT */ 32;
+
+                ALOGVV("Creating resampler with %d bits\n", bits);
                 resampler = AudioResampler::create(
-                        bits,
+                        mMixerInFormat,
                         // the resampler sees the number of channels after the downmixer, if any
                         (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
                         devSampleRate, quality);
@@ -848,16 +888,19 @@
             if (n & NEEDS_RESAMPLE) {
                 all16BitsStereoNoResample = false;
                 resampling = true;
-                t.hook = track__genericResample;
+                t.hook = getTrackHook(TRACKTYPE_RESAMPLE, FCC_2,
+                        t.mMixerInFormat, t.mMixerFormat);
                 ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                         "Track %d needs downmix + resample", i);
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
-                    t.hook = track__16BitsMono;
+                    t.hook = getTrackHook(TRACKTYPE_NORESAMPLEMONO, FCC_2,
+                            t.mMixerInFormat, t.mMixerFormat);
                     all16BitsStereoNoResample = false;
                 }
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
-                    t.hook = track__16BitsStereo;
+                    t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, FCC_2,
+                            t.mMixerInFormat, t.mMixerFormat);
                     ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                             "Track %d needs downmix", i);
                 }
@@ -888,7 +931,10 @@
             state->hook = process__genericNoResampling;
             if (all16BitsStereoNoResample && !volumeRamp) {
                 if (countActiveTracks == 1) {
-                    state->hook = process__OneTrack16BitsStereoNoResampling;
+                    const int i = 31 - __builtin_clz(state->enabledTracks);
+                    track_t& t = state->tracks[i];
+                    state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, FCC_2,
+                            t.mMixerInFormat, t.mMixerFormat);
                 }
             }
         }
@@ -931,6 +977,7 @@
 void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount,
         int32_t* temp, int32_t* aux)
 {
+    ALOGVV("track__genericResample\n");
     t->resampler->setSampleRate(t->sampleRate);
 
     // ramp gain - resample to temp buffer and scale/mix in 2nd step
@@ -1042,6 +1089,7 @@
 void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount,
         int32_t* temp __unused, int32_t* aux)
 {
+    ALOGVV("track__16BitsStereo\n");
     const int16_t *in = static_cast<const int16_t *>(t->in);
 
     if (CC_UNLIKELY(aux != NULL)) {
@@ -1133,6 +1181,7 @@
 void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
         int32_t* temp __unused, int32_t* aux)
 {
+    ALOGVV("track__16BitsMono\n");
     const int16_t *in = static_cast<int16_t const *>(t->in);
 
     if (CC_UNLIKELY(aux != NULL)) {
@@ -1220,6 +1269,7 @@
 // no-op case
 void AudioMixer::process__nop(state_t* state, int64_t pts)
 {
+    ALOGVV("process__nop\n");
     uint32_t e0 = state->enabledTracks;
     size_t sampleCount = state->frameCount * MAX_NUM_CHANNELS;
     while (e0) {
@@ -1267,6 +1317,7 @@
 // generic code without resampling
 void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
 {
+    ALOGVV("process__genericNoResampling\n");
     int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
 
     // acquire each track's buffer
@@ -1349,18 +1400,12 @@
                     }
                 }
             }
-            switch (t1.mMixerFormat) {
-            case AUDIO_FORMAT_PCM_FLOAT:
-                memcpy_to_float_from_q4_27(reinterpret_cast<float *>(out), outTemp, BLOCKSIZE * 2);
-                out += BLOCKSIZE * 2; // output is 2 floats/frame.
-                break;
-            case AUDIO_FORMAT_PCM_16_BIT:
-                ditherAndClamp(out, outTemp, BLOCKSIZE);
-                out += BLOCKSIZE; // output is 1 int32_t (2 int16_t samples)/frame
-                break;
-            default:
-                LOG_ALWAYS_FATAL("bad mixer format: %d", t1.mMixerFormat);
-            }
+
+            convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat,
+                    BLOCKSIZE * FCC_2);
+            // TODO: fix ugly casting due to choice of out pointer type
+            out = reinterpret_cast<int32_t*>((uint8_t*)out
+                    + BLOCKSIZE * FCC_2 * audio_bytes_per_sample(t1.mMixerFormat));
             numFrames += BLOCKSIZE;
         } while (numFrames < state->frameCount);
     }
@@ -1379,6 +1424,7 @@
 // generic code with resampling
 void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
 {
+    ALOGVV("process__genericResampling\n");
     // this const just means that local variable outTemp doesn't change
     int32_t* const outTemp = state->outputTemp;
     const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
@@ -1442,16 +1488,7 @@
                 }
             }
         }
-        switch (t1.mMixerFormat) {
-        case AUDIO_FORMAT_PCM_FLOAT:
-            memcpy_to_float_from_q4_27(reinterpret_cast<float*>(out), outTemp, numFrames*2);
-            break;
-        case AUDIO_FORMAT_PCM_16_BIT:
-            ditherAndClamp(out, outTemp, numFrames);
-            break;
-        default:
-            LOG_ALWAYS_FATAL("bad mixer format: %d", t1.mMixerFormat);
-        }
+        convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat, numFrames * FCC_2);
     }
 }
 
@@ -1459,6 +1496,7 @@
 void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
                                                            int64_t pts)
 {
+    ALOGVV("process__OneTrack16BitsStereoNoResampling\n");
     // This method is only called when state->enabledTracks has exactly
     // one bit set.  The asserts below would verify this, but are commented out
     // since the whole point of this method is to optimize performance.
@@ -1683,5 +1721,275 @@
     ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
 }
 
+/* This process hook is called when there is a single track without
+ * aux buffer, volume ramp, or resampling.
+ * TODO: Update the hook selection: this can properly handle aux and ramp.
+ */
+template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
+{
+    ALOGVV("process_NoResampleOneTrack\n");
+    // CLZ is faster than CTZ on ARM, though really not sure if true after 31 - clz.
+    const int i = 31 - __builtin_clz(state->enabledTracks);
+    ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled");
+    track_t *t = &state->tracks[i];
+    TO* out = reinterpret_cast<TO*>(t->mainBuffer);
+    TA* aux = reinterpret_cast<TA*>(t->auxBuffer);
+    const bool ramp = t->needsRamp();
+
+    for (size_t numFrames = state->frameCount; numFrames; ) {
+        AudioBufferProvider::Buffer& b(t->buffer);
+        // get input buffer
+        b.frameCount = numFrames;
+        const int64_t outputPTS = calculateOutputPTS(*t, pts, state->frameCount - numFrames);
+        t->bufferProvider->getNextBuffer(&b, outputPTS);
+        const TI *in = reinterpret_cast<TI*>(b.raw);
+
+        // in == NULL can happen if the track was flushed just after having
+        // been enabled for mixing.
+        if (in == NULL || (((uintptr_t)in) & 3)) {
+            memset(out, 0, numFrames
+                    * NCHAN * audio_bytes_per_sample(t->mMixerFormat));
+            ALOGE_IF((((uintptr_t)in) & 3), "process_NoResampleOneTrack: bus error: "
+                    "buffer %p track %p, channels %d, needs %#x",
+                    in, t, t->channelCount, t->needs);
+            return;
+        }
+
+        const size_t outFrames = b.frameCount;
+        if (ramp) {
+            volumeRampMulti<MIXTYPE_MULTI_SAVEONLY, NCHAN>(out, outFrames, in, aux,
+                    t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
+        } else {
+            volumeMulti<MIXTYPE_MULTI_SAVEONLY, NCHAN>(out, outFrames, in, aux,
+                    t->volume, t->auxLevel);
+        }
+        out += outFrames * NCHAN;
+        if (aux != NULL) {
+            aux += NCHAN;
+        }
+        numFrames -= b.frameCount;
+
+        // release buffer
+        t->bufferProvider->releaseBuffer(&b);
+    }
+    if (ramp) {
+        t->adjustVolumeRamp(aux != NULL);
+    }
+}
+
+/* This track hook is called to do resampling then mixing,
+ * pulling from the track's upstream AudioBufferProvider.
+ */
+template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO* temp, TA* aux)
+{
+    ALOGVV("track__Resample\n");
+    t->resampler->setSampleRate(t->sampleRate);
+
+    const bool ramp = t->needsRamp();
+    if (ramp || aux != NULL) {
+        // if ramp:        resample with unity gain to temp buffer and scale/mix in 2nd step.
+        // if aux != NULL: resample with unity gain to temp buffer then apply send level.
+
+        t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
+        memset(temp, 0, outFrameCount * NCHAN * sizeof(TO));
+        t->resampler->resample((int32_t*)temp, outFrameCount, t->bufferProvider);
+        if (ramp) {
+            volumeRampMulti<MIXTYPE_MULTI, NCHAN>(out, outFrameCount, temp, aux,
+                    t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
+            t->adjustVolumeRamp(aux != NULL);
+        } else {
+            volumeMulti<MIXTYPE_MULTI, NCHAN>(out, outFrameCount, temp, aux,
+                    t->volume, t->auxLevel);
+        }
+    } else { // constant volume gain
+        t->resampler->setVolume(t->volume[0], t->volume[1]);
+        t->resampler->resample((int32_t*)out, outFrameCount, t->bufferProvider);
+    }
+}
+
+/* This track hook is called to mix a track, when no resampling is required.
+ * The input buffer should be present in t->in.
+ */
+template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+void AudioMixer::track__NoResample(track_t* t, TO* out, size_t frameCount,
+        TO* temp __unused, TA* aux)
+{
+    ALOGVV("track__NoResample\n");
+    const TI *in = static_cast<const TI *>(t->in);
+
+    if (t->needsRamp()) {
+        volumeRampMulti<MIXTYPE, NCHAN>(out, frameCount, in, aux,
+                t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
+        t->adjustVolumeRamp(aux != NULL);
+    } else {
+        volumeMulti<MIXTYPE, NCHAN>(out, frameCount, in, aux, t->volume, t->auxLevel);
+    }
+    // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
+    // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
+    in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * NCHAN;
+    t->in = in;
+}
+
+/* The Mixer engine generates either int32_t (Q4_27) or float data.
+ * We use this function to convert the engine buffers
+ * to the desired mixer output format, either int16_t (Q.15) or float.
+ */
+void AudioMixer::convertMixerFormat(void *out, audio_format_t mixerOutFormat,
+        void *in, audio_format_t mixerInFormat, size_t sampleCount)
+{
+    switch (mixerInFormat) {
+    case AUDIO_FORMAT_PCM_FLOAT:
+        switch (mixerOutFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            memcpy(out, in, sampleCount * sizeof(float)); // MEMCPY. TODO optimize out
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            memcpy_to_i16_from_float((int16_t*)out, (float*)in, sampleCount);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+            break;
+        }
+        break;
+    case AUDIO_FORMAT_PCM_16_BIT:
+        switch (mixerOutFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            memcpy_to_float_from_q4_27((float*)out, (int32_t*)in, sampleCount);
+            break;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            // two int16_t are produced per iteration
+            ditherAndClamp((int32_t*)out, (int32_t*)in, sampleCount >> 1);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+            break;
+        }
+        break;
+    default:
+        LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+        break;
+    }
+}
+
+/* Returns the proper track hook to use for mixing the track into the output buffer.
+ */
+AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, int channels,
+        audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
+{
+    if (!kUseNewMixer && channels == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
+        switch (trackType) {
+        case TRACKTYPE_NOP:
+            return track__nop;
+        case TRACKTYPE_RESAMPLE:
+            return track__genericResample;
+        case TRACKTYPE_NORESAMPLEMONO:
+            return track__16BitsMono;
+        case TRACKTYPE_NORESAMPLE:
+            return track__16BitsStereo;
+        default:
+            LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
+            break;
+        }
+    }
+    LOG_ALWAYS_FATAL_IF(channels != FCC_2); // TODO: must be stereo right now
+    switch (trackType) {
+    case TRACKTYPE_NOP:
+        return track__nop;
+    case TRACKTYPE_RESAMPLE:
+        switch (mixerInFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return (AudioMixer::hook_t)
+                    track__Resample<MIXTYPE_MULTI, 2, float, float, int32_t>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return (AudioMixer::hook_t)\
+                    track__Resample<MIXTYPE_MULTI, 2, int32_t, int16_t, int32_t>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+            break;
+        }
+        break;
+    case TRACKTYPE_NORESAMPLEMONO:
+        switch (mixerInFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return (AudioMixer::hook_t)
+                    track__NoResample<MIXTYPE_MONOEXPAND, 2, float, float, int32_t>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return (AudioMixer::hook_t)
+                    track__NoResample<MIXTYPE_MONOEXPAND, 2, int32_t, int16_t, int32_t>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+            break;
+        }
+        break;
+    case TRACKTYPE_NORESAMPLE:
+        switch (mixerInFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return (AudioMixer::hook_t)
+                    track__NoResample<MIXTYPE_MULTI, 2, float, float, int32_t>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return (AudioMixer::hook_t)
+                    track__NoResample<MIXTYPE_MULTI, 2, int32_t, int16_t, int32_t>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+            break;
+        }
+        break;
+    default:
+        LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
+        break;
+    }
+    return NULL;
+}
+
+/* Returns the proper process hook for mixing tracks. Currently works only for
+ * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling.
+ */
+AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, int channels,
+        audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
+{
+    if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
+        LOG_ALWAYS_FATAL("bad processType: %d", processType);
+        return NULL;
+    }
+    if (!kUseNewMixer && channels == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
+        return process__OneTrack16BitsStereoNoResampling;
+    }
+    LOG_ALWAYS_FATAL_IF(channels != FCC_2); // TODO: must be stereo right now
+    switch (mixerInFormat) {
+    case AUDIO_FORMAT_PCM_FLOAT:
+        switch (mixerOutFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+                    float, float, int32_t>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+                    int16_t, float, int32_t>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+            break;
+        }
+        break;
+    case AUDIO_FORMAT_PCM_16_BIT:
+        switch (mixerOutFormat) {
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+                    float, int16_t, int32_t>;
+        case AUDIO_FORMAT_PCM_16_BIT:
+            return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, 2,
+                    int16_t, int16_t, int32_t>;
+        default:
+            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+            break;
+        }
+        break;
+    default:
+        LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+        break;
+    }
+    return NULL;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 766ff60..e6de00c 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -221,6 +221,7 @@
 
         // 16-byte boundary
 
+        bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
         bool        setResampler(uint32_t sampleRate, uint32_t devSampleRate);
         bool        doesResample() const { return resampler != NULL; }
         void        resetResampler() { if (resampler != NULL) resampler->reset(); }
@@ -229,12 +230,14 @@
                                                     resampler->getUnreleasedFrames() : 0; };
     };
 
+    typedef void (*process_hook_t)(state_t* state, int64_t pts);
+
     // pad to 32-bytes to fill cache line
     struct state_t {
         uint32_t        enabledTracks;
         uint32_t        needsChanged;
         size_t          frameCount;
-        void            (*hook)(state_t* state, int64_t pts);   // one of process__*, never NULL
+        process_hook_t  hook;   // one of process__*, never NULL
         int32_t         *outputTemp;
         int32_t         *resampleTemp;
         NBLog::Writer*  mLog;
@@ -345,6 +348,38 @@
     static uint64_t         sLocalTimeFreq;
     static pthread_once_t   sOnceControl;
     static void             sInitRoutine();
+
+    // multi-format process hooks
+    template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+    static void process_NoResampleOneTrack(state_t* state, int64_t pts);
+
+    // multi-format track hooks
+    template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+    static void track__Resample(track_t* t, TO* out, size_t frameCount,
+            TO* temp __unused, TA* aux);
+    template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
+    static void track__NoResample(track_t* t, TO* out, size_t frameCount,
+            TO* temp __unused, TA* aux);
+
+    static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
+            void *in, audio_format_t mixerInFormat, size_t sampleCount);
+
+    // hook types
+    enum {
+        PROCESSTYPE_NORESAMPLEONETRACK,
+    };
+    enum {
+        TRACKTYPE_NOP,
+        TRACKTYPE_RESAMPLE,
+        TRACKTYPE_NORESAMPLE,
+        TRACKTYPE_NORESAMPLEMONO,
+    };
+
+    // functions for determining the proper process and track hooks.
+    static process_hook_t getProcessHook(int processType, int channels,
+            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+    static hook_t getTrackHook(int trackType, int channels,
+            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioMixerOps.h b/services/audioflinger/AudioMixerOps.h
new file mode 100644
index 0000000..de92946
--- /dev/null
+++ b/services/audioflinger/AudioMixerOps.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2014 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_MIXER_OPS_H
+#define ANDROID_AUDIO_MIXER_OPS_H
+
+namespace android {
+
+/* Behavior of is_same<>::value is true if the types are identical,
+ * false otherwise. Identical to the STL std::is_same.
+ */
+template<typename T, typename U>
+struct is_same
+{
+    static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T>  // partial specialization
+{
+    static const bool value = true;
+};
+
+
+/* MixMul is a multiplication operator to scale an audio input signal
+ * by a volume gain, with the formula:
+ *
+ * O(utput) = I(nput) * V(olume)
+ *
+ * The output, input, and volume may have different types.
+ * There are 27 variants, of which 14 are actually defined in an
+ * explicitly templated class.
+ *
+ * The following type variables and the underlying meaning:
+ *
+ * Output type       TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1]
+ * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1]
+ * Volume type       TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1]
+ *
+ * For high precision audio, only the <TO, TI, TV> = <float, float, float>
+ * needs to be accelerated. This is perhaps the easiest form to do quickly as well.
+ */
+
+template <typename TO, typename TI, typename TV>
+inline TO MixMul(TI value, TV volume) {
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(false);
+    // should not be here :-).
+    // To avoid mistakes, this template is always specialized.
+    return value * volume;
+}
+
+template <>
+inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) {
+    return value * volume;
+}
+
+template <>
+inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) {
+    return (value >> 12) * volume;
+}
+
+template <>
+inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) {
+    return value * (volume >> 16);
+}
+
+template <>
+inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) {
+    return (value >> 12) * (volume >> 16);
+}
+
+template <>
+inline float MixMul<float, float, int16_t>(float value, int16_t volume) {
+    static const float norm = 1. / (1 << 12);
+    return value * volume * norm;
+}
+
+template <>
+inline float MixMul<float, float, int32_t>(float value, int32_t volume) {
+    static const float norm = 1. / (1 << 28);
+    return value * volume * norm;
+}
+
+template <>
+inline int16_t MixMul<int16_t, float, int16_t>(float value, int16_t volume) {
+    return clamp16_from_float(MixMul<float, float, int16_t>(value, volume));
+}
+
+template <>
+inline int16_t MixMul<int16_t, float, int32_t>(float value, int32_t volume) {
+    return clamp16_from_float(MixMul<float, float, int32_t>(value, volume));
+}
+
+template <>
+inline float MixMul<float, int16_t, int16_t>(int16_t value, int16_t volume) {
+    static const float norm = 1. / (1 << (15 + 12));
+    return static_cast<float>(value) * static_cast<float>(volume) * norm;
+}
+
+template <>
+inline float MixMul<float, int16_t, int32_t>(int16_t value, int32_t volume) {
+    static const float norm = 1. / (1ULL << (15 + 28));
+    return static_cast<float>(value) * static_cast<float>(volume) * norm;
+}
+
+template <>
+inline int16_t MixMul<int16_t, int16_t, int16_t>(int16_t value, int16_t volume) {
+    return clamp16(MixMul<int32_t, int16_t, int16_t>(value, volume) >> 12);
+}
+
+template <>
+inline int16_t MixMul<int16_t, int32_t, int16_t>(int32_t value, int16_t volume) {
+    return clamp16(MixMul<int32_t, int32_t, int16_t>(value, volume) >> 12);
+}
+
+template <>
+inline int16_t MixMul<int16_t, int16_t, int32_t>(int16_t value, int32_t volume) {
+    return clamp16(MixMul<int32_t, int16_t, int32_t>(value, volume) >> 12);
+}
+
+template <>
+inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume) {
+    return clamp16(MixMul<int32_t, int32_t, int32_t>(value, volume) >> 12);
+}
+
+/*
+ * MixAccum is used to add into an accumulator register of a possibly different
+ * type. The TO and TI types are the same as MixMul.
+ */
+
+template <typename TO, typename TI>
+inline void MixAccum(TO *auxaccum, TI value) {
+    if (!is_same<TO, TI>::value) {
+        LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %d %d\n",
+                sizeof(TO), sizeof(TI));
+    }
+    *auxaccum += value;
+}
+
+template<>
+inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) {
+    static const float norm = 1. / (1 << 15);
+    *auxaccum += norm * value;
+}
+
+template<>
+inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) {
+    static const float norm = 1. / (1 << 27);
+    *auxaccum += norm * value;
+}
+
+template<>
+inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) {
+    *auxaccum += value << 12;
+}
+
+template<>
+inline void MixAccum<int32_t, float>(int32_t *auxaccum, float value) {
+    *auxaccum += clampq4_27_from_float(value);
+}
+
+/* MixMulAux is just like MixMul except it combines with
+ * an accumulator operation MixAccum.
+ */
+
+template <typename TO, typename TI, typename TV, typename TA>
+inline TO MixMulAux(TI value, TV volume, TA *auxaccum) {
+    MixAccum<TA, TI>(auxaccum, value);
+    return MixMul<TO, TI, TV>(value, volume);
+}
+
+/* MIXTYPE is used to determine how the samples in the input frame
+ * are mixed with volume gain into the output frame.
+ * See the volumeRampMulti functions below for more details.
+ */
+enum {
+    MIXTYPE_MULTI,
+    MIXTYPE_MONOEXPAND,
+    MIXTYPE_MULTI_SAVEONLY,
+};
+
+/*
+ * The volumeRampMulti and volumeRamp functions take a MIXTYPE
+ * which indicates the per-frame mixing and accumulation strategy.
+ *
+ * MIXTYPE_MULTI:
+ *   NCHAN represents number of input and output channels.
+ *   TO: int32_t (Q4.27) or float
+ *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ *   TV: int32_t (U4.28) or int16_t (U4.12) or float
+ *   vol: represents a volume array.
+ *
+ *   This accumulates into the out pointer.
+ *
+ * MIXTYPE_MONOEXPAND:
+ *   Single input channel. NCHAN represents number of output channels.
+ *   TO: int32_t (Q4.27) or float
+ *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ *   TV: int32_t (U4.28) or int16_t (U4.12) or float
+ *   Input channel count is 1.
+ *   vol: represents volume array.
+ *
+ *   This accumulates into the out pointer.
+ *
+ * MIXTYPE_MULTI_SAVEONLY:
+ *   NCHAN represents number of input and output channels.
+ *   TO: int16_t (Q.15) or float
+ *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ *   TV: int32_t (U4.28) or int16_t (U4.12) or float
+ *   vol: represents a volume array.
+ *
+ *   MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer.
+ */
+
+template <int MIXTYPE, int NCHAN,
+        typename TO, typename TI, typename TV, typename TA, typename TAV>
+inline void volumeRampMulti(TO* out, size_t frameCount,
+        const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
+{
+#ifdef ALOGVV
+    ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE);
+#endif
+    if (aux != NULL) {
+        do {
+            TA auxaccum = 0;
+            switch (MIXTYPE) {
+            case MIXTYPE_MULTI:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
+                    vol[i] += volinc[i];
+                }
+                break;
+            case MIXTYPE_MULTI_SAVEONLY:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
+                    vol[i] += volinc[i];
+                }
+                break;
+            case MIXTYPE_MONOEXPAND:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
+                    vol[i] += volinc[i];
+                }
+                in++;
+                break;
+            default:
+                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
+                break;
+            }
+            auxaccum /= NCHAN;
+            *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
+            vola[0] += volainc;
+        } while (--frameCount);
+    } else {
+        do {
+            switch (MIXTYPE) {
+            case MIXTYPE_MULTI:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
+                    vol[i] += volinc[i];
+                }
+                break;
+            case MIXTYPE_MULTI_SAVEONLY:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
+                    vol[i] += volinc[i];
+                }
+                break;
+            case MIXTYPE_MONOEXPAND:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
+                    vol[i] += volinc[i];
+                }
+                in++;
+                break;
+            default:
+                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
+                break;
+            }
+        } while (--frameCount);
+    }
+}
+
+template <int MIXTYPE, int NCHAN,
+        typename TO, typename TI, typename TV, typename TA, typename TAV>
+inline void volumeMulti(TO* out, size_t frameCount,
+        const TI* in, TA* aux, const TV *vol, TAV vola)
+{
+#ifdef ALOGVV
+    ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE);
+#endif
+    if (aux != NULL) {
+        do {
+            TA auxaccum = 0;
+            switch (MIXTYPE) {
+            case MIXTYPE_MULTI:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
+                }
+                break;
+            case MIXTYPE_MULTI_SAVEONLY:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
+                }
+                break;
+            case MIXTYPE_MONOEXPAND:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
+                }
+                in++;
+                break;
+            default:
+                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
+                break;
+            }
+            auxaccum /= NCHAN;
+            *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
+        } while (--frameCount);
+    } else {
+        do {
+            switch (MIXTYPE) {
+            case MIXTYPE_MULTI:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
+                }
+                break;
+            case MIXTYPE_MULTI_SAVEONLY:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
+                }
+                break;
+            case MIXTYPE_MONOEXPAND:
+                for (int i = 0; i < NCHAN; ++i) {
+                    *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
+                }
+                in++;
+                break;
+            default:
+                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
+                break;
+            }
+        } while (--frameCount);
+    }
+}
+
+};
+
+#endif /* ANDROID_AUDIO_MIXER_OPS_H */
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 562c4ea..38c9061 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -40,8 +40,8 @@
 
 class AudioResamplerOrder1 : public AudioResampler {
 public:
-    AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) :
-        AudioResampler(bitDepth, inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
+    AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) :
+        AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
     }
     virtual void resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
@@ -145,7 +145,7 @@
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 static uint32_t currentMHz = 0;
 
-AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
+AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount,
         int32_t sampleRate, src_quality quality) {
 
     bool atFinalQuality;
@@ -216,33 +216,40 @@
     default:
     case LOW_QUALITY:
         ALOGV("Create linear Resampler");
-        resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
+        LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
+        resampler = new AudioResamplerOrder1(inChannelCount, sampleRate);
         break;
     case MED_QUALITY:
         ALOGV("Create cubic Resampler");
-        resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
+        LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
+        resampler = new AudioResamplerCubic(inChannelCount, sampleRate);
         break;
     case HIGH_QUALITY:
         ALOGV("Create HIGH_QUALITY sinc Resampler");
-        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
+        LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
+        resampler = new AudioResamplerSinc(inChannelCount, sampleRate);
         break;
     case VERY_HIGH_QUALITY:
         ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
-        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality);
+        LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
+        resampler = new AudioResamplerSinc(inChannelCount, sampleRate, quality);
         break;
     case DYN_LOW_QUALITY:
     case DYN_MED_QUALITY:
     case DYN_HIGH_QUALITY:
         ALOGV("Create dynamic Resampler = %d", quality);
-        if (bitDepth == 32) { /* bitDepth == 32 signals float precision */
-            resampler = new AudioResamplerDyn<float, float, float>(bitDepth, inChannelCount,
-                    sampleRate, quality);
-        } else if (quality == DYN_HIGH_QUALITY) {
-            resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(bitDepth, inChannelCount,
+        if (format == AUDIO_FORMAT_PCM_FLOAT) {
+            resampler = new AudioResamplerDyn<float, float, float>(inChannelCount,
                     sampleRate, quality);
         } else {
-            resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(bitDepth, inChannelCount,
-                    sampleRate, quality);
+            LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT);
+            if (quality == DYN_HIGH_QUALITY) {
+                resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(inChannelCount,
+                        sampleRate, quality);
+            } else {
+                resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(inChannelCount,
+                        sampleRate, quality);
+            }
         }
         break;
     }
@@ -252,26 +259,25 @@
     return resampler;
 }
 
-AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
+AudioResampler::AudioResampler(int inChannelCount,
         int32_t sampleRate, src_quality quality) :
-    mBitDepth(bitDepth), mChannelCount(inChannelCount),
-            mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
-            mPhaseFraction(0), mLocalTimeFreq(0),
-            mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
-    // sanity check on format
-    if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
-        ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
-                inChannelCount);
-        // ALOG_ASSERT(0);
+        mChannelCount(inChannelCount),
+        mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
+        mPhaseFraction(0), mLocalTimeFreq(0),
+        mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
+
+    if (inChannelCount < 1
+            || inChannelCount > (quality < DYN_LOW_QUALITY ? 2 : 8)) {
+        LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d channels",
+                quality, inChannelCount);
     }
     if (sampleRate <= 0) {
-        ALOGE("Unsupported sample rate %d Hz", sampleRate);
+        LOG_ALWAYS_FATAL("Unsupported sample rate %d Hz", sampleRate);
     }
 
     // initialize common members
     mVolume[0] = mVolume[1] = 0;
     mBuffer.frameCount = 0;
-
 }
 
 AudioResampler::~AudioResampler() {
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index b84567e..be747f6 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -22,6 +22,7 @@
 #include <cutils/compiler.h>
 
 #include <media/AudioBufferProvider.h>
+#include <system/audio.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -46,7 +47,7 @@
         DYN_HIGH_QUALITY=7,
     };
 
-    static AudioResampler* create(int bitDepth, int inChannelCount,
+    static AudioResampler* create(audio_format_t format, int inChannelCount,
             int32_t sampleRate, src_quality quality=DEFAULT_QUALITY);
 
     virtual ~AudioResampler();
@@ -86,7 +87,7 @@
     // multiplier to calculate fixed point phase increment
     static const double kPhaseMultiplier;
 
-    AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality);
+    AudioResampler(int inChannelCount, int32_t sampleRate, src_quality quality);
 
     // prevent copying
     AudioResampler(const AudioResampler&);
@@ -94,7 +95,6 @@
 
     int64_t calculateOutputPTS(int outputFrameIndex);
 
-    const int32_t mBitDepth;
     const int32_t mChannelCount;
     const int32_t mSampleRate;
     int32_t mInSampleRate;
diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
index 203b933..b315da5 100644
--- a/services/audioflinger/AudioResamplerCubic.h
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -28,8 +28,8 @@
 
 class AudioResamplerCubic : public AudioResampler {
 public:
-    AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) :
-        AudioResampler(bitDepth, inChannelCount, sampleRate, MED_QUALITY) {
+    AudioResamplerCubic(int inChannelCount, int32_t sampleRate) :
+        AudioResampler(inChannelCount, sampleRate, MED_QUALITY) {
     }
     virtual void resample(int32_t* out, size_t outFrameCount,
             AudioBufferProvider* provider);
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 318eb57..043c803 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -38,11 +38,6 @@
 
 namespace android {
 
-// generate a unique resample type compile-time constant (constexpr)
-#define RESAMPLETYPE(CHANNELS, LOCKED, STRIDE) \
-    ((((CHANNELS)-1)&1) | !!(LOCKED)<<1 \
-    | ((STRIDE)==8 ? 1 : (STRIDE)==16 ? 2 : 0)<<2)
-
 /*
  * InBuffer is a type agnostic input buffer.
  *
@@ -167,9 +162,9 @@
 }
 
 template<typename TC, typename TI, typename TO>
-AudioResamplerDyn<TC, TI, TO>::AudioResamplerDyn(int bitDepth,
+AudioResamplerDyn<TC, TI, TO>::AudioResamplerDyn(
         int inChannelCount, int32_t sampleRate, src_quality quality)
-    : AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
+    : AudioResampler(inChannelCount, sampleRate, quality),
       mResampleFunc(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY),
     mCoefBuffer(NULL)
 {
@@ -403,12 +398,76 @@
     // determine which resampler to use
     // check if locked phase (works only if mPhaseIncrement has no "fractional phase bits")
     int locked = (mPhaseIncrement << (sizeof(mPhaseIncrement)*8 - c.mShift)) == 0;
-    int stride = (c.mHalfNumCoefs&7)==0 ? 16 : (c.mHalfNumCoefs&3)==0 ? 8 : 2;
     if (locked) {
         mPhaseFraction = mPhaseFraction >> c.mShift << c.mShift; // remove fractional phase
     }
 
-    setResampler(RESAMPLETYPE(mChannelCount, locked, stride));
+    // stride is the minimum number of filter coefficients processed per loop iteration.
+    // We currently only allow a stride of 16 to match with SIMD processing.
+    // This means that the filter length must be a multiple of 16,
+    // or half the filter length (mHalfNumCoefs) must be a multiple of 8.
+    //
+    // Note: A stride of 2 is achieved with non-SIMD processing.
+    int stride = ((c.mHalfNumCoefs & 7) == 0) ? 16 : 2;
+    LOG_ALWAYS_FATAL_IF(stride < 16, "Resampler stride must be 16 or more");
+    LOG_ALWAYS_FATAL_IF(mChannelCount > 8 || mChannelCount < 1,
+            "Resampler channels(%d) must be between 1 to 8", mChannelCount);
+    // stride 16 (falls back to stride 2 for machines that do not support NEON)
+    if (locked) {
+        switch (mChannelCount) {
+        case 1:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>;
+            break;
+        case 2:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>;
+            break;
+        case 3:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, true, 16>;
+            break;
+        case 4:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, true, 16>;
+            break;
+        case 5:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, true, 16>;
+            break;
+        case 6:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, true, 16>;
+            break;
+        case 7:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, true, 16>;
+            break;
+        case 8:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, true, 16>;
+            break;
+        }
+    } else {
+        switch (mChannelCount) {
+        case 1:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>;
+            break;
+        case 2:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>;
+            break;
+        case 3:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, false, 16>;
+            break;
+        case 4:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, false, 16>;
+            break;
+        case 5:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, false, 16>;
+            break;
+        case 6:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, false, 16>;
+            break;
+        case 7:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, false, 16>;
+            break;
+        case 8:
+            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, false, 16>;
+            break;
+        }
+    }
 #ifdef DEBUG_RESAMPLER
     printf("channels:%d  %s  stride:%d  %s  coef:%d  shift:%d\n",
             mChannelCount, locked ? "locked" : "interpolated",
@@ -424,34 +483,12 @@
 }
 
 template<typename TC, typename TI, typename TO>
-void AudioResamplerDyn<TC, TI, TO>::setResampler(unsigned resampleType)
-{
-    // stride 16 (falls back to stride 2 for machines that do not support NEON)
-    switch (resampleType) {
-    case RESAMPLETYPE(1, true, 16):
-        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>;
-        return;
-    case RESAMPLETYPE(2, true, 16):
-        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>;
-        return;
-    case RESAMPLETYPE(1, false, 16):
-        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>;
-        return;
-    case RESAMPLETYPE(2, false, 16):
-        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>;
-        return;
-    default:
-        LOG_ALWAYS_FATAL("Invalid resampler type: %u", resampleType);
-        mResampleFunc = NULL;
-        return;
-    }
-}
-
-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,
         AudioBufferProvider* provider)
 {
+    // TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out.
+    const int OUTPUT_CHANNELS = (CHANNELS < 2) ? 2 : CHANNELS;
     const Constants& c(mConstants);
     const TC* const coefs = mConstants.mFirCoefs;
     TI* impulse = mInBuffer.getImpulse();
@@ -459,7 +496,7 @@
     uint32_t phaseFraction = mPhaseFraction;
     const uint32_t phaseIncrement = mPhaseIncrement;
     size_t outputIndex = 0;
-    size_t outputSampleCount = outFrameCount * 2;   // stereo output
+    size_t outputSampleCount = outFrameCount * OUTPUT_CHANNELS;
     const uint32_t phaseWrapLimit = c.mL << c.mShift;
     size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction)
             / phaseWrapLimit;
@@ -490,7 +527,7 @@
         while (mBuffer.frameCount == 0 && inFrameCount > 0) {
             mBuffer.frameCount = inFrameCount;
             provider->getNextBuffer(&mBuffer,
-                    calculateOutputPTS(outputIndex / 2));
+                    calculateOutputPTS(outputIndex / OUTPUT_CHANNELS));
             if (mBuffer.raw == NULL) {
                 goto resample_exit;
             }
@@ -538,7 +575,8 @@
                     phaseFraction, phaseWrapLimit,
                     coefShift, halfNumCoefs, coefs,
                     impulse, volumeSimd);
-            outputIndex += 2;
+
+            outputIndex += OUTPUT_CHANNELS;
 
             phaseFraction += phaseIncrement;
             while (phaseFraction >= phaseWrapLimit) {
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
index 8c56319..3044bfb 100644
--- a/services/audioflinger/AudioResamplerDyn.h
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -41,7 +41,7 @@
 template<typename TC, typename TI, typename TO>
 class AudioResamplerDyn: public AudioResampler {
 public:
-    AudioResamplerDyn(int bitDepth, int inChannelCount,
+    AudioResamplerDyn(int inChannelCount,
             int32_t sampleRate, src_quality quality);
 
     virtual ~AudioResamplerDyn();
@@ -110,12 +110,10 @@
     void createKaiserFir(Constants &c, double stopBandAtten,
             int inSampleRate, int outSampleRate, double tbwCheat);
 
-    void setResampler(unsigned resampleType);
-
     template<int CHANNELS, bool LOCKED, int STRIDE>
     void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
 
-    // declare a pointer to member function for resample
+    // define a pointer to member function type for resample
     typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
             size_t outFrameCount, AudioBufferProvider* provider);
 
diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h
index 76d2d66..bb0f1c9 100644
--- a/services/audioflinger/AudioResamplerFirProcess.h
+++ b/services/audioflinger/AudioResamplerFirProcess.h
@@ -44,14 +44,14 @@
 void mac(float& l, float& r, TC coef,  const float* samples)
 {
     l += *samples++ * coef;
-    r += *samples++ * coef;
+    r += *samples * coef;
 }
 
 template<typename TC>
 static inline
 void mac(float& l, TC coef,  const float* samples)
 {
-    l += *samples++ * coef;
+    l += *samples * coef;
 }
 
 /* variant for output type TO = int32_t output samples */
@@ -69,62 +69,48 @@
 }
 
 /*
- * Calculates a single output frame (two samples).
+ * Helper template functions for loop unrolling accumulator operations.
  *
- * This function computes both the positive half FIR dot product and
- * the negative half FIR dot product, accumulates, and then applies the volume.
- *
- * This is a locked phase filter (it does not compute the interpolation).
- *
- * Use fir() to compute the proper coefficient pointers for a polyphase
- * filter bank.
+ * Unrolling the loops achieves about 2x gain.
+ * Using a recursive template rather than an array of TO[] for the accumulator
+ * values is an additional 10-20% gain.
  */
 
-template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO>
-static inline
-void ProcessL(TO* const out,
-        int count,
-        const TC* coefsP,
-        const TC* coefsN,
-        const TI* sP,
-        const TI* sN,
-        const TO* const volumeLR)
+template<int CHANNELS, typename TO>
+class Accumulator : public Accumulator<CHANNELS-1, TO> // recursive
 {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS >= 1 && CHANNELS <= 2)
-    if (CHANNELS == 2) {
-        TO l = 0;
-        TO r = 0;
-        do {
-            mac(l, r, *coefsP++, sP);
-            sP -= CHANNELS;
-            mac(l, r, *coefsN++, sN);
-            sN += CHANNELS;
-        } while (--count > 0);
-        out[0] += volumeAdjust(l, volumeLR[0]);
-        out[1] += volumeAdjust(r, volumeLR[1]);
-    } else { /* CHANNELS == 1 */
-        TO l = 0;
-        do {
-            mac(l, *coefsP++, sP);
-            sP -= CHANNELS;
-            mac(l, *coefsN++, sN);
-            sN += CHANNELS;
-        } while (--count > 0);
-        out[0] += volumeAdjust(l, volumeLR[0]);
-        out[1] += volumeAdjust(l, volumeLR[1]);
+public:
+    inline void clear() {
+        value = 0;
+        Accumulator<CHANNELS-1, TO>::clear();
     }
-}
+    template<typename TC, typename TI>
+    inline void acc(TC coef, const TI*& data) {
+        mac(value, coef, data++);
+        Accumulator<CHANNELS-1, TO>::acc(coef, data);
+    }
+    inline void volume(TO*& out, TO gain) {
+        *out++ = volumeAdjust(value, gain);
+        Accumulator<CHANNELS-1, TO>::volume(out, gain);
+    }
+
+    TO value; // one per recursive inherited base class
+};
+
+template<typename TO>
+class Accumulator<0, TO> {
+public:
+    inline void clear() {
+    }
+    template<typename TC, typename TI>
+    inline void acc(TC coef __unused, const TI*& data __unused) {
+    }
+    inline void volume(TO*& out __unused, TO gain __unused) {
+    }
+};
 
 /*
- * Calculates a single output frame (two samples) interpolating phase.
- *
- * This function computes both the positive half FIR dot product and
- * the negative half FIR dot product, accumulates, and then applies the volume.
- *
- * This is an interpolated phase filter.
- *
- * Use fir() to compute the proper coefficient pointers for a polyphase
- * filter bank.
+ * Helper template functions for interpolating filter coefficients.
  */
 
 template<typename TC, typename T>
@@ -159,6 +145,131 @@
     return mulAdd(static_cast<int16_t>(lerp), (coef_1-coef_0)<<1, coef_0);
 }
 
+/* class scope for passing in functions into templates */
+struct InterpCompute {
+    template<typename TC, typename TINTERP>
+    static inline
+    TC interpolatep(TC coef_0, TC coef_1, TINTERP lerp) {
+        return interpolate(coef_0, coef_1, lerp);
+    }
+
+    template<typename TC, typename TINTERP>
+    static inline
+    TC interpolaten(TC coef_0, TC coef_1, TINTERP lerp) {
+        return interpolate(coef_0, coef_1, lerp);
+    }
+};
+
+struct InterpNull {
+    template<typename TC, typename TINTERP>
+    static inline
+    TC interpolatep(TC coef_0, TC coef_1 __unused, TINTERP lerp __unused) {
+        return coef_0;
+    }
+
+    template<typename TC, typename TINTERP>
+    static inline
+    TC interpolaten(TC coef_0 __unused, TC coef_1, TINTERP lerp __unused) {
+        return coef_1;
+    }
+};
+
+/*
+ * Calculates a single output frame (two samples).
+ *
+ * The Process*() functions compute both the positive half FIR dot product and
+ * the negative half FIR dot product, accumulates, and then applies the volume.
+ *
+ * Use fir() to compute the proper coefficient pointers for a polyphase
+ * filter bank.
+ *
+ * ProcessBase() is the fundamental processing template function.
+ *
+ * ProcessL() calls ProcessBase() with TFUNC = InterpNull, for fixed/locked phase.
+ * Process() calls ProcessBase() with TFUNC = InterpCompute, for interpolated phase.
+ */
+
+template <int CHANNELS, int STRIDE, typename TFUNC, typename TC, typename TI, typename TO, typename TINTERP>
+static inline
+void ProcessBase(TO* const out,
+        int count,
+        const TC* coefsP,
+        const TC* coefsN,
+        const TI* sP,
+        const TI* sN,
+        TINTERP lerpP,
+        const TO* const volumeLR)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS > 0)
+
+    if (CHANNELS > 2) {
+        // TO accum[CHANNELS];
+        Accumulator<CHANNELS, TO> accum;
+
+        // for (int j = 0; j < CHANNELS; ++j) accum[j] = 0;
+        accum.clear();
+        for (size_t i = 0; i < count; ++i) {
+            TC c = TFUNC::interpolatep(coefsP[0], coefsP[count], lerpP);
+
+            // for (int j = 0; j < CHANNELS; ++j) mac(accum[j], c, sP + j);
+            const TI *tmp_data = sP; // tmp_ptr seems to work better
+            accum.acc(c, tmp_data);
+
+            coefsP++;
+            sP -= CHANNELS;
+            c = TFUNC::interpolaten(coefsN[count], coefsN[0], lerpP);
+
+            // for (int j = 0; j < CHANNELS; ++j) mac(accum[j], c, sN + j);
+            tmp_data = sN; // tmp_ptr seems faster than directly using sN
+            accum.acc(c, tmp_data);
+
+            coefsN++;
+            sN += CHANNELS;
+        }
+        // for (int j = 0; j < CHANNELS; ++j) out[j] += volumeAdjust(accum[j], volumeLR[0]);
+        TO *tmp_out = out; // may remove if const out definition changes.
+        accum.volume(tmp_out, volumeLR[0]);
+    } else if (CHANNELS == 2) {
+        TO l = 0;
+        TO r = 0;
+        for (size_t i = 0; i < count; ++i) {
+            mac(l, r, TFUNC::interpolatep(coefsP[0], coefsP[count], lerpP), sP);
+            coefsP++;
+            sP -= CHANNELS;
+            mac(l, r, TFUNC::interpolaten(coefsN[count], coefsN[0], lerpP), sN);
+            coefsN++;
+            sN += CHANNELS;
+        }
+        out[0] += volumeAdjust(l, volumeLR[0]);
+        out[1] += volumeAdjust(r, volumeLR[1]);
+    } else { /* CHANNELS == 1 */
+        TO l = 0;
+        for (size_t i = 0; i < count; ++i) {
+            mac(l, TFUNC::interpolatep(coefsP[0], coefsP[count], lerpP), sP);
+            coefsP++;
+            sP -= CHANNELS;
+            mac(l, TFUNC::interpolaten(coefsN[count], coefsN[0], lerpP), sN);
+            coefsN++;
+            sN += CHANNELS;
+        }
+        out[0] += volumeAdjust(l, volumeLR[0]);
+        out[1] += volumeAdjust(l, volumeLR[1]);
+    }
+}
+
+template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO>
+static inline
+void ProcessL(TO* const out,
+        int count,
+        const TC* coefsP,
+        const TC* coefsN,
+        const TI* sP,
+        const TI* sN,
+        const TO* const volumeLR)
+{
+    ProcessBase<CHANNELS, STRIDE, InterpNull>(out, count, coefsP, coefsN, sP, sN, 0, volumeLR);
+}
+
 template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO, typename TINTERP>
 static inline
 void Process(TO* const out,
@@ -172,35 +283,8 @@
         TINTERP lerpP,
         const TO* const volumeLR)
 {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS >= 1 && CHANNELS <= 2)
-    adjustLerp<TC, TINTERP>(lerpP); // coefficient type adjustment for interpolation
-
-    if (CHANNELS == 2) {
-        TO l = 0;
-        TO r = 0;
-        for (size_t i = 0; i < count; ++i) {
-            mac(l, r, interpolate(coefsP[0], coefsP[count], lerpP), sP);
-            coefsP++;
-            sP -= CHANNELS;
-            mac(l, r, interpolate(coefsN[count], coefsN[0], lerpP), sN);
-            coefsN++;
-            sN += CHANNELS;
-        }
-        out[0] += volumeAdjust(l, volumeLR[0]);
-        out[1] += volumeAdjust(r, volumeLR[1]);
-    } else { /* CHANNELS == 1 */
-        TO l = 0;
-        for (size_t i = 0; i < count; ++i) {
-            mac(l, interpolate(coefsP[0], coefsP[count], lerpP), sP);
-            coefsP++;
-            sP -= CHANNELS;
-            mac(l, interpolate(coefsN[count], coefsN[0], lerpP), sN);
-            coefsN++;
-            sN += CHANNELS;
-        }
-        out[0] += volumeAdjust(l, volumeLR[0]);
-        out[1] += volumeAdjust(l, volumeLR[1]);
-    }
+    adjustLerp<TC, TINTERP>(lerpP); // coefficient type adjustment for interpolations
+    ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, volumeLR);
 }
 
 /*
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 35553ef..60ff88e 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -452,9 +452,9 @@
 
 // ----------------------------------------------------------------------------
 
-AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
+AudioResamplerSinc::AudioResamplerSinc(
         int inChannelCount, int32_t sampleRate, src_quality quality)
-    : AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
+    : AudioResampler(inChannelCount, sampleRate, quality),
     mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0)
 {
     /*
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 1ea4474..97ae3d0 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -34,7 +34,7 @@
 
 class AudioResamplerSinc : public AudioResampler {
 public:
-    AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate,
+    AudioResamplerSinc(int inChannelCount, int32_t sampleRate,
             src_quality quality = HIGH_QUALITY);
 
     virtual ~AudioResamplerSinc();
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 6130084..79bdfe8 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -54,6 +54,7 @@
                 return mStreamType;
             }
             bool        isOffloaded() const { return (mFlags & IAudioFlinger::TRACK_OFFLOAD) != 0; }
+            bool        isDirect() const { return (mFlags & IAudioFlinger::TRACK_DIRECT) != 0; }
             status_t    setParameters(const String8& keyValuePairs);
             status_t    attachAuxEffect(int EffectId);
             void        setAuxBuffer(int EffectId, int32_t *buffer);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d6390b1..11d13a8 100755
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1157,12 +1157,12 @@
                                              type_t type)
     :   ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
         mNormalFrameCount(0), mSinkBuffer(NULL),
-        mMixerBufferEnabled(false),
+        mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
         mMixerBuffer(NULL),
         mMixerBufferSize(0),
         mMixerBufferFormat(AUDIO_FORMAT_INVALID),
         mMixerBufferValid(false),
-        mEffectBufferEnabled(false),
+        mEffectBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
         mEffectBuffer(NULL),
         mEffectBufferSize(0),
         mEffectBufferFormat(AUDIO_FORMAT_INVALID),
@@ -1401,9 +1401,10 @@
                 frameCount, mFrameCount);
       } else {
         ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
-                "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+                "mFrameCount=%d format=%#x mFormat=%#x isLinear=%d channelMask=%#x "
+                "sampleRate=%u mSampleRate=%u "
                 "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
-                isTimed, sharedBuffer.get(), frameCount, mFrameCount, format,
+                isTimed, sharedBuffer.get(), frameCount, mFrameCount, format, mFormat,
                 audio_is_linear_pcm(format),
                 channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
         *flags &= ~IAudioFlinger::TRACK_FAST;
@@ -1660,7 +1661,7 @@
     track->mState = TrackBase::STOPPED;
     if (!trackActive) {
         removeTrack_l(track);
-    } else if (track->isFastTrack() || track->isOffloaded()) {
+    } else if (track->isFastTrack() || track->isOffloaded() || track->isDirect()) {
         track->mState = TrackBase::STOPPING_1;
     }
 
@@ -1809,11 +1810,12 @@
     if (!audio_is_valid_format(mFormat)) {
         LOG_ALWAYS_FATAL("HAL format %#x not valid for output", mFormat);
     }
-    if ((mType == MIXER || mType == DUPLICATING) && mFormat != AUDIO_FORMAT_PCM_16_BIT) {
-        LOG_ALWAYS_FATAL("HAL format %#x not supported for mixed output; "
-                "must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+    if ((mType == MIXER || mType == DUPLICATING)
+            && !isValidPcmSinkFormat(mFormat)) {
+        LOG_FATAL("HAL format %#x not supported for mixed output",
+                mFormat);
     }
-    mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
+    mFrameSize = audio_stream_out_frame_size(mOutput->stream);
     mBufferSize = mOutput->stream->common.get_buffer_size(&mOutput->stream->common);
     mFrameCount = mBufferSize / mFrameSize;
     if (mFrameCount & 15) {
@@ -1868,7 +1870,9 @@
     }
     mNormalFrameCount = multiplier * mFrameCount;
     // round up to nearest 16 frames to satisfy AudioMixer
-    mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
+    if (mType == MIXER || mType == DUPLICATING) {
+        mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
+    }
     ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount,
             mNormalFrameCount);
 
@@ -2656,7 +2660,7 @@
     if (mNormalSink != 0) {
         return mNormalSink->getTimestamp(timestamp);
     }
-    if (mType == OFFLOAD && mOutput->stream->get_presentation_position) {
+    if ((mType == OFFLOAD || mType == DIRECT) && mOutput->stream->get_presentation_position) {
         uint64_t position64;
         int ret = mOutput->stream->get_presentation_position(
                                                 mOutput->stream, &position64, &timestamp.mTime);
@@ -2860,8 +2864,6 @@
         }
 #endif
 
-    } else {
-        mFastMixer = NULL;
     }
 
     switch (kUseFastMixer) {
@@ -2880,7 +2882,7 @@
 
 AudioFlinger::MixerThread::~MixerThread()
 {
-    if (mFastMixer != NULL) {
+    if (mFastMixer != 0) {
         FastMixerStateQueue *sq = mFastMixer->sq();
         FastMixerState *state = sq->begin();
         if (state->mCommand == FastMixerState::COLD_IDLE) {
@@ -2902,7 +2904,7 @@
         ALOG_ASSERT(fastTrack->mBufferProvider != NULL);
         delete fastTrack->mBufferProvider;
         sq->end(false /*didModify*/);
-        delete mFastMixer;
+        mFastMixer.clear();
 #ifdef AUDIO_WATCHDOG
         if (mAudioWatchdog != 0) {
             mAudioWatchdog->requestExit();
@@ -2918,7 +2920,7 @@
 
 uint32_t AudioFlinger::MixerThread::correctLatency_l(uint32_t latency) const
 {
-    if (mFastMixer != NULL) {
+    if (mFastMixer != 0) {
         MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
         latency += (pipe->getAvgFrames() * 1000) / mSampleRate;
     }
@@ -2935,7 +2937,7 @@
 {
     // FIXME we should only do one push per cycle; confirm this is true
     // Start the fast mixer if it's not already running
-    if (mFastMixer != NULL) {
+    if (mFastMixer != 0) {
         FastMixerStateQueue *sq = mFastMixer->sq();
         FastMixerState *state = sq->begin();
         if (state->mCommand != FastMixerState::MIX_WRITE &&
@@ -2969,7 +2971,7 @@
 void AudioFlinger::MixerThread::threadLoop_standby()
 {
     // Idle the fast mixer if it's currently running
-    if (mFastMixer != NULL) {
+    if (mFastMixer != 0) {
         FastMixerStateQueue *sq = mFastMixer->sq();
         FastMixerState *state = sq->begin();
         if (!(state->mCommand & FastMixerState::IDLE)) {
@@ -3132,7 +3134,7 @@
     FastMixerState *state = NULL;
     bool didModify = false;
     FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED;
-    if (mFastMixer != NULL) {
+    if (mFastMixer != 0) {
         sq = mFastMixer->sq();
         state = sq->begin();
     }
@@ -3675,7 +3677,7 @@
 
     // if !&IDLE, holds the FastMixer state to restore after new parameters processed
     FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
-    if (mFastMixer != NULL) {
+    if (mFastMixer != 0) {
         FastMixerStateQueue *sq = mFastMixer->sq();
         FastMixerState *state = sq->begin();
         if (!(state->mCommand & FastMixerState::IDLE)) {
@@ -3780,7 +3782,7 @@
     }
 
     if (!(previousCommand & FastMixerState::IDLE)) {
-        ALOG_ASSERT(mFastMixer != NULL);
+        ALOG_ASSERT(mFastMixer != 0);
         FastMixerStateQueue *sq = mFastMixer->sq();
         FastMixerState *state = sq->begin();
         ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE);
@@ -3947,14 +3949,16 @@
         // The first time a track is added we wait
         // for all its buffers to be filled before processing it
         uint32_t minFrames;
-        if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing()) {
+        if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()) {
             minFrames = mNormalFrameCount;
         } else {
             minFrames = 1;
         }
 
-        if ((track->framesReady() >= minFrames) && track->isReady() &&
-                !track->isPaused() && !track->isTerminated())
+        ALOGI("prepareTracks_l minFrames %d state %d frames ready %d, ",
+              minFrames, track->mState, track->framesReady());
+        if ((track->framesReady() >= minFrames) && track->isReady() && !track->isPaused() &&
+                !track->isStopping_2() && !track->isStopped())
         {
             ALOGVV("track %d s=%08x [OK]", track->name(), cblk->mServer);
 
@@ -3981,17 +3985,26 @@
             if (!mEffectChains.isEmpty() && last) {
                 mEffectChains[0]->clearInputBuffer();
             }
-
-            ALOGVV("track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
-            if ((track->sharedBuffer() != 0) || track->isTerminated() ||
-                    track->isStopped() || track->isPaused()) {
+            if (track->isStopping_1()) {
+                track->mState = TrackBase::STOPPING_2;
+            }
+            if ((track->sharedBuffer() != 0) || track->isStopped() ||
+                    track->isStopping_2() || track->isPaused()) {
                 // We have consumed all the buffers of this track.
                 // Remove it from the list of active tracks.
-                // TODO: implement behavior for compressed audio
-                size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
+                size_t audioHALFrames;
+                if (audio_is_linear_pcm(mFormat)) {
+                    audioHALFrames = (latency_l() * mSampleRate) / 1000;
+                } else {
+                    audioHALFrames = 0;
+                }
+
                 size_t framesWritten = mBytesWritten / mFrameSize;
                 if (mStandby || !last ||
                         track->presentationComplete(framesWritten, audioHALFrames)) {
+                    if (track->isStopping_2()) {
+                        track->mState = TrackBase::STOPPED;
+                    }
                     if (track->isStopped()) {
                         track->reset();
                     }
@@ -4453,7 +4466,7 @@
                     size_t audioHALFrames =
                             (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
                     size_t framesWritten =
-                            mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
+                            mBytesWritten / audio_stream_out_frame_size(mOutput->stream);
                     track->presentationComplete(framesWritten, audioHALFrames);
                     track->reset();
                     tracksToRemove->add(track);
@@ -5430,6 +5443,7 @@
         audio_channel_mask_t channelMask,
         size_t *pFrameCount,
         int sessionId,
+        size_t *notificationFrames,
         int uid,
         IAudioFlinger::track_flags_t *flags,
         pid_t tid,
@@ -5499,6 +5513,7 @@
       }
     }
     *pFrameCount = frameCount;
+    *notificationFrames = 0;    // FIXME implement
 
     lStatus = initCheck();
     if (lStatus != NO_ERROR) {
@@ -5998,7 +6013,7 @@
     if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
         ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
     }
-    mFrameSize = audio_stream_frame_size(&mInput->stream->common);
+    mFrameSize = audio_stream_in_frame_size(mInput->stream);
     mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
     mFrameCount = mBufferSize / mFrameSize;
     // This is the formula for calculating the temporary buffer size.
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 07887fb..c265833 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -233,7 +233,6 @@
 
                 // dynamic externally-visible
                 uint32_t    sampleRate() const { return mSampleRate; }
-                uint32_t    channelCount() const { return mChannelCount; }
                 audio_channel_mask_t channelMask() const { return mChannelMask; }
                 audio_format_t format() const { return mFormat; }
                 // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
@@ -852,7 +851,7 @@
                 AudioMixer* mAudioMixer;    // normal mixer
 private:
                 // one-time initialization, no locks required
-                FastMixer*  mFastMixer;         // non-NULL if there is also a fast mixer
+                sp<FastMixer>     mFastMixer;     // non-0 if there is also a fast mixer
                 sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread
 
                 // contents are not guaranteed to be consistent, no locks required
@@ -868,7 +867,7 @@
                 int32_t     mFastMixerFutex;    // for cold idle
 
 public:
-    virtual     bool        hasFastMixer() const { return mFastMixer != NULL; }
+    virtual     bool        hasFastMixer() const { return mFastMixer != 0; }
     virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const {
                               ALOG_ASSERT(fastIndex < FastMixerState::kMaxFastTracks);
                               return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
@@ -1073,6 +1072,7 @@
                     audio_channel_mask_t channelMask,
                     size_t *pFrameCount,
                     int sessionId,
+                    size_t *notificationFrames,
                     int uid,
                     IAudioFlinger::track_flags_t *flags,
                     pid_t tid,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 1e906ad..0ef9fe5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -706,7 +706,7 @@
             if (playbackThread->mActiveTracks.indexOf(this) < 0) {
                 reset();
                 mState = STOPPED;
-            } else if (!isFastTrack() && !isOffloaded()) {
+            } else if (!isFastTrack() && !isOffloaded() && !isDirect()) {
                 mState = STOPPED;
             } else {
                 // For fast tracks prepareTracks_l() will set state to STOPPING_2
@@ -860,7 +860,7 @@
     }
     Mutex::Autolock _l(thread->mLock);
     PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
-    if (!isOffloaded()) {
+    if (!isOffloaded() && !isDirect()) {
         if (!playbackThread->mLatchQValid) {
             mPreviousValid = false;
             return INVALID_OPERATION;
@@ -980,8 +980,6 @@
     }
 
     if (framesWritten >= mPresentationCompleteFrames || isOffloaded()) {
-        ALOGV("presentationComplete() session %d complete: framesWritten %d",
-                  mSessionId, framesWritten);
         triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
         mAudioTrackServerProxy->setStreamEndDone();
         return true;
@@ -1897,7 +1895,8 @@
     if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 &&
             channelCount <= FCC_2) {
         // sink SR
-        mResampler = AudioResampler::create(16, thread->mChannelCount, sampleRate);
+        mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
+                thread->mChannelCount, sampleRate);
         // source SR
         mResampler->setSampleRate(thread->mSampleRate);
         mResampler->setVolume(AudioMixer::UNITY_GAIN_INT, AudioMixer::UNITY_GAIN_INT);
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
index e14b4ae..78c9927 100644
--- a/services/audioflinger/test-resample.cpp
+++ b/services/audioflinger/test-resample.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "AudioResampler.h"
-#include <media/AudioBufferProvider.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,6 +28,8 @@
 #include <audio_utils/primitives.h>
 #include <audio_utils/sndfile.h>
 #include <utils/Vector.h>
+#include <media/AudioBufferProvider.h>
+#include "AudioResampler.h"
 
 using namespace android;
 
@@ -329,7 +329,7 @@
         printf("%zu input frames\n", input_frames);
     }
 
-    int bit_depth = useFloat ? 32 : 16;
+    audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
     int output_channels = channels > 2 ? channels : 2; // output is at least stereo samples
     size_t output_framesize = output_channels * (useFloat ? sizeof(float) : sizeof(int32_t));
     size_t output_frames = ((int64_t) input_frames * output_freq) / input_freq;
@@ -342,7 +342,7 @@
         //
         // On fast devices, filters should be generated between 0.1ms - 1ms.
         // (single threaded).
-        AudioResampler* resampler = AudioResampler::create(bit_depth, channels,
+        AudioResampler* resampler = AudioResampler::create(format, channels,
                 8000, quality);
         int looplimit = 100;
         timespec start, end;
@@ -380,7 +380,7 @@
     }
 
     void* output_vaddr = malloc(output_size);
-    AudioResampler* resampler = AudioResampler::create(bit_depth, channels,
+    AudioResampler* resampler = AudioResampler::create(format, channels,
             output_freq, quality);
 
 
diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk
index 874f18f..7bba05b 100644
--- a/services/audioflinger/tests/Android.mk
+++ b/services/audioflinger/tests/Android.mk
@@ -1,5 +1,8 @@
 # Build the unit tests for audioflinger
 
+#
+# resampler unit test
+#
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -20,6 +23,7 @@
 	bionic/libstdc++/include \
 	external/gtest/include \
 	external/stlport/stlport \
+	$(call include-path-for, audio-utils) \
 	frameworks/av/services/audioflinger
 
 LOCAL_SRC_FILES := \
@@ -29,3 +33,41 @@
 LOCAL_MODULE_TAGS := tests
 
 include $(BUILD_EXECUTABLE)
+
+#
+# audio mixer test tool
+#
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	test-mixer.cpp \
+	../AudioMixer.cpp.arm \
+
+LOCAL_C_INCLUDES := \
+	bionic \
+	bionic/libstdc++/include \
+	external/stlport/stlport \
+	$(call include-path-for, audio-effects) \
+	$(call include-path-for, audio-utils) \
+	frameworks/av/services/audioflinger
+
+LOCAL_STATIC_LIBRARIES := \
+	libsndfile
+
+LOCAL_SHARED_LIBRARIES := \
+	libstlport \
+	libeffects \
+	libnbaio \
+	libcommon_time_client \
+	libaudioresampler \
+	libaudioutils \
+	libdl \
+	libcutils \
+	libutils \
+	liblog
+
+LOCAL_MODULE:= test-mixer
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh
new file mode 100755
index 0000000..93bff47
--- /dev/null
+++ b/services/audioflinger/tests/mixer_to_wav_tests.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+#
+# This script uses test-mixer to generate WAV files
+# for evaluation of the AudioMixer component.
+#
+# Sine and chirp signals are used for input because they
+# show up as clear lines, either horizontal or diagonal,
+# on a spectrogram. This means easy verification of multiple
+# track mixing.
+#
+# After execution, look for created subdirectories like
+# mixer_i_i
+# mixer_i_f
+# mixer_f_f
+#
+# Recommend using a program such as audacity to evaluate
+# the output WAV files, e.g.
+#
+# cd testdir
+# audacity *.wav
+#
+# Using Audacity:
+#
+# Under "Waveform" view mode you can zoom into the
+# start of the WAV file to verify proper ramping.
+#
+# Select "Spectrogram" to see verify the lines
+# (sine = horizontal, chirp = diagonal) which should
+# be clear (except for around the start as the volume
+# ramping causes spectral distortion).
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+    echo "Android build environment not set"
+    exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+pushd $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/
+
+# build
+pwd
+mm
+
+# send to device
+echo "waiting for device"
+adb root && adb wait-for-device remount
+adb push $OUT/system/lib/libaudioresampler.so /system/lib
+adb push $OUT/system/bin/test-mixer /system/bin
+
+# createwav creates a series of WAV files testing various
+# mixer settings
+# $1 = flags
+# $2 = directory
+function createwav() {
+# create directory if it doesn't exist
+    if [ ! -d $2 ]; then
+        mkdir $2
+    fi
+
+# Test:
+# process__genericResampling
+# track__Resample / track__genericResample
+    adb shell test-mixer $1 -s 48000 \
+        -o /sdcard/tm48000gr.wav \
+        sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000
+    adb pull /sdcard/tm48000gr.wav $2
+
+# Test:
+# process__genericResample
+# track__Resample / track__genericResample
+# track__NoResample / track__16BitsStereo / track__16BitsMono
+# Aux buffer
+    adb shell test-mixer $1 -s 9307 \
+        -a /sdcard/aux9307gra.wav -o /sdcard/tm9307gra.wav \
+        sine:2,1000,3000 sine:1,2000,9307 chirp:2,9307
+    adb pull /sdcard/tm9307gra.wav $2
+    adb pull /sdcard/aux9307gra.wav $2
+
+# Test:
+# process__genericNoResampling
+# track__NoResample / track__16BitsStereo / track__16BitsMono
+    adb shell test-mixer $1 -s 32000 \
+        -o /sdcard/tm32000gnr.wav \
+        sine:2,1000,32000 chirp:2,32000  sine:1,3000,32000
+    adb pull /sdcard/tm32000gnr.wav $2
+
+# Test:
+# process__genericNoResampling
+# track__NoResample / track__16BitsStereo / track__16BitsMono
+# Aux buffer
+    adb shell test-mixer $1 -s 32000 \
+        -a /sdcard/aux32000gnra.wav -o /sdcard/tm32000gnra.wav \
+        sine:2,1000,32000 chirp:2,32000  sine:1,3000,32000
+    adb pull /sdcard/tm32000gnra.wav $2
+    adb pull /sdcard/aux32000gnra.wav $2
+
+# Test:
+# process__NoResampleOneTrack / process__OneTrack16BitsStereoNoResampling
+# Downmixer
+    adb shell test-mixer $1 -s 32000 \
+        -o /sdcard/tm32000nrot.wav \
+        sine:6,1000,32000
+    adb pull /sdcard/tm32000nrot.wav $2
+
+# Test:
+# process__NoResampleOneTrack / OneTrack16BitsStereoNoResampling
+# Aux buffer
+    adb shell test-mixer $1 -s 44100 \
+        -a /sdcard/aux44100nrota.wav -o /sdcard/tm44100nrota.wav \
+        sine:2,2000,44100
+    adb pull /sdcard/tm44100nrota.wav $2
+    adb pull /sdcard/aux44100nrota.wav $2
+}
+
+#
+# Call createwav to generate WAV files in various combinations
+#
+# i_i = integer input track, integer mixer output
+# f_f = float input track,   float mixer output
+# i_f = integer input track, float_mixer output
+#
+# If the mixer output is float, then the output WAV file is pcm float.
+#
+# TODO: create a "snr" like "diff" to automatically
+# compare files in these directories together.
+#
+
+createwav "" "tests/mixer_i_i"
+createwav "-f -m" "tests/mixer_f_f"
+createwav "-m" "tests/mixer_i_f"
+
+popd
diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp
index 8f9c270..987162c 100644
--- a/services/audioflinger/tests/resampler_tests.cpp
+++ b/services/audioflinger/tests/resampler_tests.cpp
@@ -33,202 +33,10 @@
 #include <gtest/gtest.h>
 #include <media/AudioBufferProvider.h>
 #include "AudioResampler.h"
+#include "test_utils.h"
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
-template<typename T, typename U>
-struct is_same
-{
-    static const bool value = false;
-};
-
-template<typename T>
-struct is_same<T, T>  // partial specialization
-{
-    static const bool value = true;
-};
-
-template<typename T>
-static inline T convertValue(double val)
-{
-    if (is_same<T, int16_t>::value) {
-        return floor(val * 32767.0 + 0.5);
-    } else if (is_same<T, int32_t>::value) {
-        return floor(val * (1UL<<31) + 0.5);
-    }
-    return val; // assume float or double
-}
-
-/* Creates a type-independent audio buffer provider from
- * a buffer base address, size, framesize, and input increment array.
- *
- * No allocation or deallocation of the provided buffer is done.
- */
-class TestProvider : public android::AudioBufferProvider {
-public:
-    TestProvider(const void* addr, size_t frames, size_t frameSize,
-            const std::vector<size_t>& inputIncr)
-    : mAddr(addr),
-      mNumFrames(frames),
-      mFrameSize(frameSize),
-      mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
-    {
-    }
-
-    virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS )
-    {
-        size_t requestedFrames = buffer->frameCount;
-        if (requestedFrames > mNumFrames - mNextFrame) {
-            buffer->frameCount = mNumFrames - mNextFrame;
-        }
-        if (!mInputIncr.empty()) {
-            size_t provided = mInputIncr[mNextIdx++];
-            ALOGV("getNextBuffer() mValue[%d]=%u not %u",
-                    mNextIdx-1, provided, buffer->frameCount);
-            if (provided < buffer->frameCount) {
-                buffer->frameCount = provided;
-            }
-            if (mNextIdx >= mInputIncr.size()) {
-                mNextIdx = 0;
-            }
-        }
-        ALOGV("getNextBuffer() requested %u frames out of %u frames available"
-                " and returned %u frames\n",
-                requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
-        mUnrel = buffer->frameCount;
-        if (buffer->frameCount > 0) {
-            buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
-            return android::NO_ERROR;
-        } else {
-            buffer->raw = NULL;
-            return android::NOT_ENOUGH_DATA;
-        }
-    }
-
-    virtual void releaseBuffer(Buffer* buffer)
-    {
-        if (buffer->frameCount > mUnrel) {
-            ALOGE("releaseBuffer() released %u frames but only %u available "
-                    "to release\n", buffer->frameCount, mUnrel);
-            mNextFrame += mUnrel;
-            mUnrel = 0;
-        } else {
-
-            ALOGV("releaseBuffer() released %u frames out of %u frames available "
-                    "to release\n", buffer->frameCount, mUnrel);
-            mNextFrame += buffer->frameCount;
-            mUnrel -= buffer->frameCount;
-        }
-        buffer->frameCount = 0;
-        buffer->raw = NULL;
-    }
-
-    void reset()
-    {
-        mNextFrame = 0;
-    }
-
-    size_t getNumFrames()
-    {
-        return mNumFrames;
-    }
-
-    void setIncr(const std::vector<size_t> inputIncr)
-    {
-        mNextIdx = 0;
-        mInputIncr = inputIncr;
-    }
-
-protected:
-    const void* mAddr;   // base address
-    size_t mNumFrames;   // total frames
-    int mFrameSize;      // frame size (# channels * bytes per sample)
-    size_t mNextFrame;   // index of next frame to provide
-    size_t mUnrel;       // number of frames not yet released
-    std::vector<size_t> mInputIncr; // number of frames provided per call
-    size_t mNextIdx;     // index of next entry in mInputIncr to use
-};
-
-/* Creates a buffer filled with a sine wave.
- *
- * Returns a pair consisting of the sine signal buffer and the number of frames.
- * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
- */
-template<typename T>
-static std::pair<T*, size_t> createSine(size_t channels,
-        double freq, double samplingRate, double time)
-{
-    double tscale = 1. / samplingRate;
-    size_t frames = static_cast<size_t>(samplingRate * time);
-    T* buffer = new T[frames * channels];
-    for (size_t i = 0; i < frames; ++i) {
-        double t = i * tscale;
-        double y = sin(2. * M_PI * freq * t);
-        T yt = convertValue<T>(y);
-
-        for (size_t j = 0; j < channels; ++j) {
-            buffer[i*channels + j] = yt / (j + 1);
-        }
-    }
-    return std::make_pair(buffer, frames);
-}
-
-/* Creates a buffer filled with a chirp signal (a sine wave sweep).
- *
- * Returns a pair consisting of the chirp signal buffer and the number of frames.
- * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
- *
- * When creating the Chirp, note that the frequency is the true sinusoidal
- * frequency not the sampling rate.
- *
- * http://en.wikipedia.org/wiki/Chirp
- */
-template<typename T>
-static std::pair<T*, size_t> createChirp(size_t channels,
-        double minfreq, double maxfreq, double samplingRate, double time)
-{
-    double tscale = 1. / samplingRate;
-    size_t frames = static_cast<size_t>(samplingRate * time);
-    T *buffer = new T[frames * channels];
-    // note the chirp constant k has a divide-by-two.
-    double k = (maxfreq - minfreq) / (2. * time);
-    for (size_t i = 0; i < frames; ++i) {
-        double t = i * tscale;
-        double y = sin(2. * M_PI * (k * t + minfreq) * t);
-        T yt = convertValue<T>(y);
-
-        for (size_t j = 0; j < channels; ++j) {
-            buffer[i*channels + j] = yt / (j + 1);
-        }
-    }
-    return std::make_pair(buffer, frames);
-}
-
-/* This derived class creates a buffer provider of datatype T,
- * consisting of an input signal, e.g. from createChirp().
- * The number of frames can be obtained from the base class
- * TestProvider::getNumFrames().
- */
-template <typename T>
-class SignalProvider : public TestProvider {
-public:
-    SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels,
-            const std::vector<size_t>& values)
-    : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values),
-      mManagedPtr(bufferInfo.first)
-    {
-    }
-
-    virtual ~SignalProvider()
-    {
-        delete[] mManagedPtr;
-    }
-
-protected:
-    T* mManagedPtr;
-};
-
-void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
+void resample(int channels, void *output,
+        size_t outputFrames, const std::vector<size_t> &outputIncr,
         android::AudioBufferProvider *provider, android::AudioResampler *resampler)
 {
     for (size_t i = 0, j = 0; i < outputFrames; ) {
@@ -239,7 +47,7 @@
         if (thisFrames == 0 || thisFrames > outputFrames - i) {
             thisFrames = outputFrames - i;
         }
-        resampler->resample((int32_t*) output + 2*i, thisFrames, provider);
+        resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
         i += thisFrames;
     }
 }
@@ -257,18 +65,26 @@
     }
 }
 
-void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq,
+void testBufferIncrement(size_t channels, bool useFloat,
+        unsigned inputFreq, unsigned outputFreq,
         enum android::AudioResampler::src_quality quality)
 {
+    const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
     // create the provider
-    std::vector<size_t> inputIncr;
-    SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
-            0., outputFreq/2., outputFreq, outputFreq/2000.),
-            channels, inputIncr);
+    std::vector<int> inputIncr;
+    SignalProvider provider;
+    if (useFloat) {
+        provider.setChirp<float>(channels,
+                0., outputFreq/2., outputFreq, outputFreq/2000.);
+    } else {
+        provider.setChirp<int16_t>(channels,
+                0., outputFreq/2., outputFreq, outputFreq/2000.);
+    }
+    provider.setIncr(inputIncr);
 
     // calculate the output size
     size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
-    size_t outputFrameSize = 2 * sizeof(int32_t);
+    size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t));
     size_t outputSize = outputFrameSize * outputFrames;
     outputSize &= ~7;
 
@@ -276,7 +92,7 @@
     const int volumePrecision = 12; /* typical unity gain */
     android::AudioResampler* resampler;
 
-    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
+    resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
     resampler->setSampleRate(inputFreq);
     resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
 
@@ -284,7 +100,7 @@
     std::vector<size_t> refIncr;
     refIncr.push_back(outputFrames);
     void* reference = malloc(outputSize);
-    resample(reference, outputFrames, refIncr, &provider, resampler);
+    resample(channels, reference, outputFrames, refIncr, &provider, resampler);
 
     provider.reset();
 
@@ -293,7 +109,7 @@
     resampler->reset();
 #else
     delete resampler;
-    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
+    resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
     resampler->setSampleRate(inputFreq);
     resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
 #endif
@@ -304,7 +120,10 @@
     outIncr.push_back(2);
     outIncr.push_back(3);
     void* test = malloc(outputSize);
-    resample(test, outputFrames, outIncr, &provider, resampler);
+    inputIncr.push_back(1);
+    inputIncr.push_back(3);
+    provider.setIncr(inputIncr);
+    resample(channels, test, outputFrames, outIncr, &provider, resampler);
 
     // check
     buffercmp(reference, test, outputFrameSize, outputFrames);
@@ -339,14 +158,15 @@
         enum android::AudioResampler::src_quality quality)
 {
     // create the provider
-    std::vector<size_t> inputIncr;
-    SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
-            0., inputFreq/2., inputFreq, inputFreq/2000.),
-            channels, inputIncr);
+    std::vector<int> inputIncr;
+    SignalProvider provider;
+    provider.setChirp<int16_t>(channels,
+            0., inputFreq/2., inputFreq, inputFreq/2000.);
+    provider.setIncr(inputIncr);
 
     // calculate the output size
     size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
-    size_t outputFrameSize = 2 * sizeof(int32_t);
+    size_t outputFrameSize = channels * sizeof(int32_t);
     size_t outputSize = outputFrameSize * outputFrames;
     outputSize &= ~7;
 
@@ -354,7 +174,8 @@
     const int volumePrecision = 12; /* typical unity gain */
     android::AudioResampler* resampler;
 
-    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
+    resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
+            channels, outputFreq, quality);
     resampler->setSampleRate(inputFreq);
     resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
 
@@ -362,7 +183,7 @@
     std::vector<size_t> refIncr;
     refIncr.push_back(outputFrames);
     void* reference = malloc(outputSize);
-    resample(reference, outputFrames, refIncr, &provider, resampler);
+    resample(channels, reference, outputFrames, refIncr, &provider, resampler);
 
     int32_t *out = reinterpret_cast<int32_t *>(reference);
 
@@ -417,7 +238,7 @@
     };
 
     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
-        testBufferIncrement(2, 48000, 32000, kQualityArray[i]);
+        testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
     }
 }
 
@@ -434,7 +255,33 @@
     };
 
     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
-        testBufferIncrement(2, 22050, 48000, kQualityArray[i]);
+        testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
+    }
+}
+
+TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
+    // only dynamic quality
+    static const enum android::AudioResampler::src_quality kQualityArray[] = {
+            android::AudioResampler::DYN_LOW_QUALITY,
+            android::AudioResampler::DYN_MED_QUALITY,
+            android::AudioResampler::DYN_HIGH_QUALITY,
+    };
+
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
+    }
+}
+
+TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
+    // only dynamic quality
+    static const enum android::AudioResampler::src_quality kQualityArray[] = {
+            android::AudioResampler::DYN_LOW_QUALITY,
+            android::AudioResampler::DYN_MED_QUALITY,
+            android::AudioResampler::DYN_HIGH_QUALITY,
+    };
+
+    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
+        testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
     }
 }
 
diff --git a/services/audioflinger/tests/test-mixer.cpp b/services/audioflinger/tests/test-mixer.cpp
new file mode 100644
index 0000000..3940702
--- /dev/null
+++ b/services/audioflinger/tests/test-mixer.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <math.h>
+#include <vector>
+#include <audio_utils/primitives.h>
+#include <audio_utils/sndfile.h>
+#include <media/AudioBufferProvider.h>
+#include "AudioMixer.h"
+#include "test_utils.h"
+
+/* Testing is typically through creation of an output WAV file from several
+ * source inputs, to be later analyzed by an audio program such as Audacity.
+ *
+ * Sine or chirp functions are typically more useful as input to the mixer
+ * as they show up as straight lines on a spectrogram if successfully mixed.
+ *
+ * A sample shell script is provided: mixer_to_wave_tests.sh
+ */
+
+using namespace android;
+
+static void usage(const char* name) {
+    fprintf(stderr, "Usage: %s [-f] [-m]"
+                    " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]"
+                    " (<input-file> | <command>)+\n", name);
+    fprintf(stderr, "    -f    enable floating point input track\n");
+    fprintf(stderr, "    -m    enable floating point mixer output\n");
+    fprintf(stderr, "    -s    mixer sample-rate\n");
+    fprintf(stderr, "    -o    <output-file> WAV file, pcm16 (or float if -m specified)\n");
+    fprintf(stderr, "    -a    <aux-buffer-file>\n");
+    fprintf(stderr, "    -P    # frames provided per call to resample() in CSV format\n");
+    fprintf(stderr, "    <input-file> is a WAV file\n");
+    fprintf(stderr, "    <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n");
+    fprintf(stderr, "                     'chirp:<channels>,<samplerate>'\n");
+}
+
+static int writeFile(const char *filename, const void *buffer,
+        uint32_t sampleRate, uint32_t channels, size_t frames, bool isBufferFloat) {
+    if (filename == NULL) {
+        return 0; // ok to pass in NULL filename
+    }
+    // write output to file.
+    SF_INFO info;
+    info.frames = 0;
+    info.samplerate = sampleRate;
+    info.channels = channels;
+    info.format = SF_FORMAT_WAV | (isBufferFloat ? SF_FORMAT_FLOAT : SF_FORMAT_PCM_16);
+    printf("saving file:%s  channels:%d  samplerate:%d  frames:%d\n",
+            filename, info.channels, info.samplerate, frames);
+    SNDFILE *sf = sf_open(filename, SFM_WRITE, &info);
+    if (sf == NULL) {
+        perror(filename);
+        return EXIT_FAILURE;
+    }
+    if (isBufferFloat) {
+        (void) sf_writef_float(sf, (float*)buffer, frames);
+    } else {
+        (void) sf_writef_short(sf, (short*)buffer, frames);
+    }
+    sf_close(sf);
+    return EXIT_SUCCESS;
+}
+
+int main(int argc, char* argv[]) {
+    const char* const progname = argv[0];
+    bool useInputFloat = false;
+    bool useMixerFloat = false;
+    bool useRamp = true;
+    uint32_t outputSampleRate = 48000;
+    uint32_t outputChannels = 2; // stereo for now
+    std::vector<int> Pvalues;
+    const char* outputFilename = NULL;
+    const char* auxFilename = NULL;
+    std::vector<int32_t> Names;
+    std::vector<SignalProvider> Providers;
+
+    for (int ch; (ch = getopt(argc, argv, "fms:o:a:P:")) != -1;) {
+        switch (ch) {
+        case 'f':
+            useInputFloat = true;
+            break;
+        case 'm':
+            useMixerFloat = true;
+            break;
+        case 's':
+            outputSampleRate = atoi(optarg);
+            break;
+        case 'o':
+            outputFilename = optarg;
+            break;
+        case 'a':
+            auxFilename = optarg;
+            break;
+        case 'P':
+            if (parseCSV(optarg, Pvalues) < 0) {
+                fprintf(stderr, "incorrect syntax for -P option\n");
+                return EXIT_FAILURE;
+            }
+            break;
+        case '?':
+        default:
+            usage(progname);
+            return EXIT_FAILURE;
+        }
+    }
+    argc -= optind;
+    argv += optind;
+
+    if (argc == 0) {
+        usage(progname);
+        return EXIT_FAILURE;
+    }
+    if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) {
+        fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS);
+        return EXIT_FAILURE;
+    }
+
+    size_t outputFrames = 0;
+
+    // create providers for each track
+    Providers.resize(argc);
+    for (int i = 0; i < argc; ++i) {
+        static const char chirp[] = "chirp:";
+        static const char sine[] = "sine:";
+        static const double kSeconds = 1;
+
+        if (!strncmp(argv[i], chirp, strlen(chirp))) {
+            std::vector<int> v;
+
+            parseCSV(argv[i] + strlen(chirp), v);
+            if (v.size() == 2) {
+                printf("creating chirp(%d %d)\n", v[0], v[1]);
+                if (useInputFloat) {
+                    Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
+                } else {
+                    Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
+                }
+                Providers[i].setIncr(Pvalues);
+            } else {
+                fprintf(stderr, "malformed input '%s'\n", argv[i]);
+            }
+        } else if (!strncmp(argv[i], sine, strlen(sine))) {
+            std::vector<int> v;
+
+            parseCSV(argv[i] + strlen(sine), v);
+            if (v.size() == 3) {
+                printf("creating sine(%d %d)\n", v[0], v[1]);
+                if (useInputFloat) {
+                    Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
+                } else {
+                    Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
+                }
+                Providers[i].setIncr(Pvalues);
+            } else {
+                fprintf(stderr, "malformed input '%s'\n", argv[i]);
+            }
+        } else {
+            printf("creating filename(%s)\n", argv[i]);
+            if (useInputFloat) {
+                Providers[i].setFile<float>(argv[i]);
+            } else {
+                Providers[i].setFile<short>(argv[i]);
+            }
+            Providers[i].setIncr(Pvalues);
+        }
+        // calculate the number of output frames
+        size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate
+                / Providers[i].getSampleRate();
+        if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames
+            outputFrames = nframes;
+        }
+    }
+
+    // create the output buffer.
+    const size_t outputFrameSize = outputChannels
+            * (useMixerFloat ? sizeof(float) : sizeof(int16_t));
+    const size_t outputSize = outputFrames * outputFrameSize;
+    void *outputAddr = NULL;
+    (void) posix_memalign(&outputAddr, 32, outputSize);
+    memset(outputAddr, 0, outputSize);
+
+    // create the aux buffer, if needed.
+    const size_t auxFrameSize = sizeof(int32_t); // Q4.27 always
+    const size_t auxSize = outputFrames * auxFrameSize;
+    void *auxAddr = NULL;
+    if (auxFilename) {
+        (void) posix_memalign(&auxAddr, 32, auxSize);
+        memset(auxAddr, 0, auxSize);
+    }
+
+    // create the mixer.
+    const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960
+    AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate);
+    audio_format_t inputFormat = useInputFloat
+            ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+    audio_format_t mixerFormat = useMixerFloat
+            ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+    float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks
+    static float f0; // zero
+
+    // set up the tracks.
+    for (size_t i = 0; i < Providers.size(); ++i) {
+        //printf("track %d out of %d\n", i, Providers.size());
+        uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels());
+        int32_t name = mixer->getTrackName(channelMask,
+                inputFormat, AUDIO_SESSION_OUTPUT_MIX);
+        ALOG_ASSERT(name >= 0);
+        Names.push_back(name);
+        mixer->setBufferProvider(name, &Providers[i]);
+        mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+                (void *) outputAddr);
+        mixer->setParameter(
+                name,
+                AudioMixer::TRACK,
+                AudioMixer::MIXER_FORMAT, (void *)mixerFormat);
+        mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+                (void *)(uintptr_t)inputFormat);
+        mixer->setParameter(
+                name,
+                AudioMixer::RESAMPLE,
+                AudioMixer::SAMPLE_RATE,
+                (void *)(uintptr_t)Providers[i].getSampleRate());
+        if (useRamp) {
+            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
+            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0);
+            mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f);
+            mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f);
+        } else {
+            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
+            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
+        }
+        if (auxFilename) {
+            mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
+                    (void *) auxAddr);
+            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0);
+            mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f);
+        }
+        mixer->enable(name);
+    }
+
+    // pump the mixer to process data.
+    size_t i;
+    for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
+        for (size_t j = 0; j < Names.size(); ++j) {
+            mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+                    (char *) outputAddr + i * outputFrameSize);
+            if (auxFilename) {
+                mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
+                        (char *) auxAddr + i * auxFrameSize);
+            }
+        }
+        mixer->process(AudioBufferProvider::kInvalidPTS);
+    }
+    outputFrames = i; // reset output frames to the data actually produced.
+
+    // write to files
+    writeFile(outputFilename, outputAddr,
+            outputSampleRate, outputChannels, outputFrames, useMixerFloat);
+    if (auxFilename) {
+        // Aux buffer is always in q4_27 format for now.
+        // memcpy_to_i16_from_q4_27(), but with stereo frame count (not sample count)
+        ditherAndClamp((int32_t*)auxAddr, (int32_t*)auxAddr, outputFrames >> 1);
+        writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false);
+    }
+
+    delete mixer;
+    free(outputAddr);
+    free(auxAddr);
+    return EXIT_SUCCESS;
+}
diff --git a/services/audioflinger/tests/test_utils.h b/services/audioflinger/tests/test_utils.h
new file mode 100644
index 0000000..f954292
--- /dev/null
+++ b/services/audioflinger/tests/test_utils.h
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2014 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_TEST_UTILS_H
+#define ANDROID_AUDIO_TEST_UTILS_H
+
+#include <audio_utils/sndfile.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+template<typename T, typename U>
+struct is_same
+{
+    static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T>  // partial specialization
+{
+    static const bool value = true;
+};
+
+template<typename T>
+static inline T convertValue(double val)
+{
+    if (is_same<T, int16_t>::value) {
+        return floor(val * 32767.0 + 0.5);
+    } else if (is_same<T, int32_t>::value) {
+        return floor(val * (1UL<<31) + 0.5);
+    }
+    return val; // assume float or double
+}
+
+// Convert a list of integers in CSV format to a Vector of those values.
+// Returns the number of elements in the list, or -1 on error.
+static inline int parseCSV(const char *string, std::vector<int>& values)
+{
+    // pass 1: count the number of values and do syntax check
+    size_t numValues = 0;
+    bool hadDigit = false;
+    for (const char *p = string; ; ) {
+        switch (*p++) {
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+            hadDigit = true;
+            break;
+        case '\0':
+            if (hadDigit) {
+                // pass 2: allocate and initialize vector of values
+                values.resize(++numValues);
+                values[0] = atoi(p = string);
+                for (size_t i = 1; i < numValues; ) {
+                    if (*p++ == ',') {
+                        values[i++] = atoi(p);
+                    }
+                }
+                return numValues;
+            }
+            // fall through
+        case ',':
+            if (hadDigit) {
+                hadDigit = false;
+                numValues++;
+                break;
+            }
+            // fall through
+        default:
+            return -1;
+        }
+    }
+}
+
+/* Creates a type-independent audio buffer provider from
+ * a buffer base address, size, framesize, and input increment array.
+ *
+ * No allocation or deallocation of the provided buffer is done.
+ */
+class TestProvider : public android::AudioBufferProvider {
+public:
+    TestProvider(void* addr, size_t frames, size_t frameSize,
+            const std::vector<int>& inputIncr)
+    : mAddr(addr),
+      mNumFrames(frames),
+      mFrameSize(frameSize),
+      mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
+    {
+    }
+
+    TestProvider()
+    : mAddr(NULL), mNumFrames(0), mFrameSize(0),
+      mNextFrame(0), mUnrel(0), mNextIdx(0)
+    {
+    }
+
+    void setIncr(const std::vector<int>& inputIncr) {
+        mInputIncr = inputIncr;
+        mNextIdx = 0;
+    }
+
+    virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS)
+    {
+        size_t requestedFrames = buffer->frameCount;
+        if (requestedFrames > mNumFrames - mNextFrame) {
+            buffer->frameCount = mNumFrames - mNextFrame;
+        }
+        if (!mInputIncr.empty()) {
+            size_t provided = mInputIncr[mNextIdx++];
+            ALOGV("getNextBuffer() mValue[%d]=%u not %u",
+                    mNextIdx-1, provided, buffer->frameCount);
+            if (provided < buffer->frameCount) {
+                buffer->frameCount = provided;
+            }
+            if (mNextIdx >= mInputIncr.size()) {
+                mNextIdx = 0;
+            }
+        }
+        ALOGV("getNextBuffer() requested %u frames out of %u frames available"
+                " and returned %u frames\n",
+                requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
+        mUnrel = buffer->frameCount;
+        if (buffer->frameCount > 0) {
+            buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
+            return android::NO_ERROR;
+        } else {
+            buffer->raw = NULL;
+            return android::NOT_ENOUGH_DATA;
+        }
+    }
+
+    virtual void releaseBuffer(Buffer* buffer)
+    {
+        if (buffer->frameCount > mUnrel) {
+            ALOGE("releaseBuffer() released %u frames but only %u available "
+                    "to release\n", buffer->frameCount, mUnrel);
+            mNextFrame += mUnrel;
+            mUnrel = 0;
+        } else {
+
+            ALOGV("releaseBuffer() released %u frames out of %u frames available "
+                    "to release\n", buffer->frameCount, mUnrel);
+            mNextFrame += buffer->frameCount;
+            mUnrel -= buffer->frameCount;
+        }
+        buffer->frameCount = 0;
+        buffer->raw = NULL;
+    }
+
+    void reset()
+    {
+        mNextFrame = 0;
+    }
+
+    size_t getNumFrames()
+    {
+        return mNumFrames;
+    }
+
+
+protected:
+    void* mAddr;   // base address
+    size_t mNumFrames;   // total frames
+    int mFrameSize;      // frame size (# channels * bytes per sample)
+    size_t mNextFrame;   // index of next frame to provide
+    size_t mUnrel;       // number of frames not yet released
+    std::vector<int> mInputIncr; // number of frames provided per call
+    size_t mNextIdx;     // index of next entry in mInputIncr to use
+};
+
+/* Creates a buffer filled with a sine wave.
+ */
+template<typename T>
+static void createSine(void *vbuffer, size_t frames,
+        size_t channels, double sampleRate, double freq)
+{
+    double tscale = 1. / sampleRate;
+    T* buffer = reinterpret_cast<T*>(vbuffer);
+    for (size_t i = 0; i < frames; ++i) {
+        double t = i * tscale;
+        double y = sin(2. * M_PI * freq * t);
+        T yt = convertValue<T>(y);
+
+        for (size_t j = 0; j < channels; ++j) {
+            buffer[i*channels + j] = yt / (j + 1);
+        }
+    }
+}
+
+/* Creates a buffer filled with a chirp signal (a sine wave sweep).
+ *
+ * When creating the Chirp, note that the frequency is the true sinusoidal
+ * frequency not the sampling rate.
+ *
+ * http://en.wikipedia.org/wiki/Chirp
+ */
+template<typename T>
+static void createChirp(void *vbuffer, size_t frames,
+        size_t channels, double sampleRate,  double minfreq, double maxfreq)
+{
+    double tscale = 1. / sampleRate;
+    T *buffer = reinterpret_cast<T*>(vbuffer);
+    // note the chirp constant k has a divide-by-two.
+    double k = (maxfreq - minfreq) / (2. * tscale * frames);
+    for (size_t i = 0; i < frames; ++i) {
+        double t = i * tscale;
+        double y = sin(2. * M_PI * (k * t + minfreq) * t);
+        T yt = convertValue<T>(y);
+
+        for (size_t j = 0; j < channels; ++j) {
+            buffer[i*channels + j] = yt / (j + 1);
+        }
+    }
+}
+
+/* This derived class creates a buffer provider of datatype T,
+ * consisting of an input signal, e.g. from createChirp().
+ * The number of frames can be obtained from the base class
+ * TestProvider::getNumFrames().
+ */
+
+class SignalProvider : public TestProvider {
+public:
+    SignalProvider()
+    : mSampleRate(0),
+      mChannels(0)
+    {
+    }
+
+    virtual ~SignalProvider()
+    {
+        free(mAddr);
+        mAddr = NULL;
+    }
+
+    template <typename T>
+    void setChirp(size_t channels, double minfreq, double maxfreq, double sampleRate, double time)
+    {
+        createBufferByFrames<T>(channels, sampleRate, sampleRate*time);
+        createChirp<T>(mAddr, mNumFrames, mChannels, mSampleRate, minfreq, maxfreq);
+    }
+
+    template <typename T>
+    void setSine(size_t channels,
+            double freq, double sampleRate, double time)
+    {
+        createBufferByFrames<T>(channels, sampleRate, sampleRate*time);
+        createSine<T>(mAddr, mNumFrames,  mChannels, mSampleRate, freq);
+    }
+
+    template <typename T>
+    void setFile(const char *file_in)
+    {
+        SF_INFO info;
+        info.format = 0;
+        SNDFILE *sf = sf_open(file_in, SFM_READ, &info);
+        if (sf == NULL) {
+            perror(file_in);
+            return;
+        }
+        createBufferByFrames<T>(info.channels, info.samplerate, info.frames);
+        if (is_same<T, float>::value) {
+            (void) sf_readf_float(sf, (float *) mAddr, mNumFrames);
+        } else if (is_same<T, short>::value) {
+            (void) sf_readf_short(sf, (short *) mAddr, mNumFrames);
+        }
+        sf_close(sf);
+    }
+
+    template <typename T>
+    void createBufferByFrames(size_t channels, uint32_t sampleRate, size_t frames)
+    {
+        mNumFrames = frames;
+        mChannels = channels;
+        mFrameSize = mChannels * sizeof(T);
+        free(mAddr);
+        mAddr = malloc(mFrameSize * mNumFrames);
+        mSampleRate = sampleRate;
+    }
+
+    uint32_t getSampleRate() const {
+        return mSampleRate;
+    }
+
+    uint32_t getNumChannels() const {
+        return mChannels;
+    }
+
+protected:
+    uint32_t mSampleRate;
+    uint32_t mChannels;
+};
+
+#endif // ANDROID_AUDIO_TEST_UTILS_H
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
old mode 100644
new mode 100755
index cddc503..f3be42d
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -3,7 +3,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    AudioPolicyService.cpp
+    AudioPolicyService.cpp \
+    AudioPolicyEffects.cpp
 
 ifeq ($(USE_LEGACY_AUDIO_POLICY), 1)
 LOCAL_SRC_FILES += \
diff --git a/services/audiopolicy/AudioPolicyEffects.cpp b/services/audiopolicy/AudioPolicyEffects.cpp
new file mode 100755
index 0000000..185e1cc
--- /dev/null
+++ b/services/audiopolicy/AudioPolicyEffects.cpp
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2014 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 "AudioPolicyEffects"
+#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cutils/misc.h>
+#include <media/AudioEffect.h>
+#include <system/audio.h>
+#include <hardware/audio_effect.h>
+#include <audio_effects/audio_effects_conf.h>
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
+#include <cutils/config_utils.h>
+#include "AudioPolicyEffects.h"
+#include "ServiceUtilities.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// AudioPolicyEffects Implementation
+// ----------------------------------------------------------------------------
+
+AudioPolicyEffects::AudioPolicyEffects()
+{
+    // load automatic audio effect modules
+    if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
+        loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
+    } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
+        loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
+    }
+}
+
+
+AudioPolicyEffects::~AudioPolicyEffects()
+{
+    size_t i = 0;
+    // release audio input processing resources
+    for (i = 0; i < mInputSources.size(); i++) {
+        delete mInputSources.valueAt(i);
+    }
+    mInputSources.clear();
+
+    for (i = 0; i < mInputs.size(); i++) {
+        mInputs.valueAt(i)->mEffects.clear();
+        delete mInputs.valueAt(i);
+    }
+    mInputs.clear();
+
+    // release audio output processing resources
+    for (i = 0; i < mOutputStreams.size(); i++) {
+        delete mOutputStreams.valueAt(i);
+    }
+    mOutputStreams.clear();
+
+    for (i = 0; i < mOutputSessions.size(); i++) {
+        mOutputSessions.valueAt(i)->mEffects.clear();
+        delete mOutputSessions.valueAt(i);
+    }
+    mOutputSessions.clear();
+}
+
+
+status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input,
+                             audio_source_t inputSource,
+                             int audioSession)
+{
+    status_t status = NO_ERROR;
+
+    // create audio pre processors according to input source
+    audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
+                                    AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
+
+    ssize_t index = mInputSources.indexOfKey(aliasSource);
+    if (index < 0) {
+        ALOGV("addInputEffects(): no processing needs to be attached to this source");
+        return status;
+    }
+    ssize_t idx = mInputs.indexOfKey(input);
+    EffectVector *inputDesc;
+    if (idx < 0) {
+        inputDesc = new EffectVector(audioSession);
+        mInputs.add(input, inputDesc);
+    } else {
+        inputDesc = mInputs.valueAt(idx);
+    }
+
+    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);
+        status_t status = fx->initCheck();
+        if (status != NO_ERROR && status != ALREADY_EXISTS) {
+            ALOGW("addInputEffects(): failed to create Fx %s on source %d",
+                  effect->mName, (int32_t)aliasSource);
+            // fx goes out of scope and strong ref on AudioEffect is released
+            continue;
+        }
+        for (size_t j = 0; j < effect->mParams.size(); j++) {
+            fx->setParameter(effect->mParams[j]);
+        }
+        ALOGV("addInputEffects(): added Fx %s on source: %d", effect->mName, (int32_t)aliasSource);
+        inputDesc->mEffects.add(fx);
+    }
+    setProcessorEnabled(inputDesc, true);
+
+    return status;
+}
+
+
+status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input)
+{
+    status_t status = NO_ERROR;
+
+    ssize_t index = mInputs.indexOfKey(input);
+    if (index < 0) {
+        return status;
+    }
+    EffectVector *inputDesc = mInputs.valueAt(index);
+    setProcessorEnabled(inputDesc, false);
+    delete inputDesc;
+    mInputs.removeItemsAt(index);
+    ALOGV("releaseInputEffects(): all effects released");
+    return status;
+}
+
+status_t AudioPolicyEffects::queryDefaultInputEffects(int audioSession,
+                                                      effect_descriptor_t *descriptors,
+                                                      uint32_t *count)
+{
+    status_t status = NO_ERROR;
+
+    size_t index;
+    for (index = 0; index < mInputs.size(); index++) {
+        if (mInputs.valueAt(index)->mSessionId == audioSession) {
+            break;
+        }
+    }
+    if (index == mInputs.size()) {
+        *count = 0;
+        return BAD_VALUE;
+    }
+    Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects;
+
+    for (size_t i = 0; i < effects.size(); i++) {
+        effect_descriptor_t desc = effects[i]->descriptor();
+        if (i < *count) {
+            descriptors[i] = desc;
+        }
+    }
+    if (effects.size() > *count) {
+        status = NO_MEMORY;
+    }
+    *count = effects.size();
+    return status;
+}
+
+
+status_t AudioPolicyEffects::queryDefaultOutputSessionEffects(int audioSession,
+                         effect_descriptor_t *descriptors,
+                         uint32_t *count)
+{
+    status_t status = NO_ERROR;
+
+    size_t index;
+    for (index = 0; index < mOutputSessions.size(); index++) {
+        if (mOutputSessions.valueAt(index)->mSessionId == audioSession) {
+            break;
+        }
+    }
+    if (index == mOutputSessions.size()) {
+        *count = 0;
+        return BAD_VALUE;
+    }
+    Vector< sp<AudioEffect> > effects = mOutputSessions.valueAt(index)->mEffects;
+
+    for (size_t i = 0; i < effects.size(); i++) {
+        effect_descriptor_t desc = effects[i]->descriptor();
+        if (i < *count) {
+            descriptors[i] = desc;
+        }
+    }
+    if (effects.size() > *count) {
+        status = NO_MEMORY;
+    }
+    *count = effects.size();
+    return status;
+}
+
+
+status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output,
+                         audio_stream_type_t stream,
+                         int audioSession)
+{
+    status_t status = NO_ERROR;
+
+    // create audio processors according to stream
+    ssize_t index = mOutputStreams.indexOfKey(stream);
+    if (index < 0) {
+        ALOGV("addOutputSessionEffects(): no output processing needed for this stream");
+        return NO_ERROR;
+    }
+
+    ssize_t idx = mOutputSessions.indexOfKey(audioSession);
+    EffectVector *procDesc;
+    if (idx < 0) {
+        procDesc = new EffectVector(audioSession);
+        mOutputSessions.add(audioSession, procDesc);
+    } else {
+        procDesc = mOutputSessions.valueAt(idx);
+    }
+
+    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, audioSession, output);
+        status_t status = fx->initCheck();
+        if (status != NO_ERROR && status != ALREADY_EXISTS) {
+            ALOGE("addOutputSessionEffects(): failed to create Fx  %s on session %d",
+                  effect->mName, audioSession);
+            // fx goes out of scope and strong ref on AudioEffect is released
+            continue;
+        }
+        ALOGV("addOutputSessionEffects(): added Fx %s on session: %d for stream: %d",
+              effect->mName, audioSession, (int32_t)stream);
+        procDesc->mEffects.add(fx);
+    }
+
+    setProcessorEnabled(procDesc, true);
+
+    return status;
+}
+
+status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output,
+                         audio_stream_type_t stream,
+                         int audioSession)
+{
+    status_t status = NO_ERROR;
+    (void) output; // argument not used for now
+    (void) stream; // argument not used for now
+
+    ssize_t index = mOutputSessions.indexOfKey(audioSession);
+    if (index < 0) {
+        ALOGV("releaseOutputSessionEffects: no output processing was attached to this stream");
+        return NO_ERROR;
+    }
+
+    EffectVector *procDesc = mOutputSessions.valueAt(index);
+    setProcessorEnabled(procDesc, false);
+    procDesc->mEffects.clear();
+    delete procDesc;
+    mOutputSessions.removeItemsAt(index);
+    ALOGV("releaseOutputSessionEffects(): output processing released from session: %d",
+          audioSession);
+    return status;
+}
+
+
+void AudioPolicyEffects::setProcessorEnabled(const EffectVector *effectVector, bool enabled)
+{
+    const Vector<sp<AudioEffect> > &fxVector = effectVector->mEffects;
+    for (size_t i = 0; i < fxVector.size(); i++) {
+        fxVector.itemAt(i)->setEnabled(enabled);
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// Audio processing configuration
+// ----------------------------------------------------------------------------
+
+/*static*/ const char * const AudioPolicyEffects::kInputSourceNames[AUDIO_SOURCE_CNT -1] = {
+    MIC_SRC_TAG,
+    VOICE_UL_SRC_TAG,
+    VOICE_DL_SRC_TAG,
+    VOICE_CALL_SRC_TAG,
+    CAMCORDER_SRC_TAG,
+    VOICE_REC_SRC_TAG,
+    VOICE_COMM_SRC_TAG
+};
+
+// returns the audio_source_t enum corresponding to the input source name or
+// AUDIO_SOURCE_CNT is no match found
+audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name)
+{
+    int i;
+    for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) {
+        if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) {
+            ALOGV("inputSourceNameToEnum found source %s %d", name, i);
+            break;
+        }
+    }
+    return (audio_source_t)i;
+}
+
+const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_CNT+1] = {
+    AUDIO_STREAM_DEFAULT_TAG,
+    AUDIO_STREAM_VOICE_CALL_TAG,
+    AUDIO_STREAM_SYSTEM_TAG,
+    AUDIO_STREAM_RING_TAG,
+    AUDIO_STREAM_MUSIC_TAG,
+    AUDIO_STREAM_ALARM_TAG,
+    AUDIO_STREAM_NOTIFICATION_TAG,
+    AUDIO_STREAM_BLUETOOTH_SCO_TAG,
+    AUDIO_STREAM_ENFORCED_AUDIBLE_TAG,
+    AUDIO_STREAM_DTMF_TAG,
+    AUDIO_STREAM_TTS_TAG
+};
+
+// returns the audio_stream_t enum corresponding to the output stream name or
+// AUDIO_STREAM_CNT is no match found
+audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name)
+{
+    int i;
+    for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_CNT; i++) {
+        if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) {
+            ALOGV("streamNameToEnum found stream %s %d", name, i);
+            break;
+        }
+    }
+    return (audio_stream_type_t)i;
+}
+
+// ----------------------------------------------------------------------------
+// Audio Effect Config parser
+// ----------------------------------------------------------------------------
+
+size_t AudioPolicyEffects::growParamSize(char *param,
+                                         size_t size,
+                                         size_t *curSize,
+                                         size_t *totSize)
+{
+    // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int)
+    size_t pos = ((*curSize - 1 ) / size + 1) * size;
+
+    if (pos + size > *totSize) {
+        while (pos + size > *totSize) {
+            *totSize += ((*totSize + 7) / 8) * 4;
+        }
+        param = (char *)realloc(param, *totSize);
+    }
+    *curSize = pos + size;
+    return pos;
+}
+
+size_t AudioPolicyEffects::readParamValue(cnode *node,
+                                          char *param,
+                                          size_t *curSize,
+                                          size_t *totSize)
+{
+    if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(short), curSize, totSize);
+        *(short *)((char *)param + pos) = (short)atoi(node->value);
+        ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos));
+        return sizeof(short);
+    } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(int), curSize, totSize);
+        *(int *)((char *)param + pos) = atoi(node->value);
+        ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos));
+        return sizeof(int);
+    } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(float), curSize, totSize);
+        *(float *)((char *)param + pos) = (float)atof(node->value);
+        ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos));
+        return sizeof(float);
+    } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) {
+        size_t pos = growParamSize(param, sizeof(bool), curSize, totSize);
+        if (strncmp(node->value, "false", strlen("false") + 1) == 0) {
+            *(bool *)((char *)param + pos) = false;
+        } else {
+            *(bool *)((char *)param + pos) = true;
+        }
+        ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false");
+        return sizeof(bool);
+    } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) {
+        size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
+        if (*curSize + len + 1 > *totSize) {
+            *totSize = *curSize + len + 1;
+            param = (char *)realloc(param, *totSize);
+        }
+        strncpy(param + *curSize, node->value, len);
+        *curSize += len;
+        param[*curSize] = '\0';
+        ALOGV("readParamValue() reading string %s", param + *curSize - len);
+        return len;
+    }
+    ALOGW("readParamValue() unknown param type %s", node->name);
+    return 0;
+}
+
+effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root)
+{
+    cnode *param;
+    cnode *value;
+    size_t curSize = sizeof(effect_param_t);
+    size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int);
+    effect_param_t *fx_param = (effect_param_t *)malloc(totSize);
+
+    param = config_find(root, PARAM_TAG);
+    value = config_find(root, VALUE_TAG);
+    if (param == NULL && value == NULL) {
+        // try to parse simple parameter form {int int}
+        param = root->first_child;
+        if (param != NULL) {
+            // Note: that a pair of random strings is read as 0 0
+            int *ptr = (int *)fx_param->data;
+            int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
+            ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2);
+            *ptr++ = atoi(param->name);
+            *ptr = atoi(param->value);
+            fx_param->psize = sizeof(int);
+            fx_param->vsize = sizeof(int);
+            return fx_param;
+        }
+    }
+    if (param == NULL || value == NULL) {
+        ALOGW("loadEffectParameter() invalid parameter description %s", root->name);
+        goto error;
+    }
+
+    fx_param->psize = 0;
+    param = param->first_child;
+    while (param) {
+        ALOGV("loadEffectParameter() reading param of type %s", param->name);
+        size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize);
+        if (size == 0) {
+            goto error;
+        }
+        fx_param->psize += size;
+        param = param->next;
+    }
+
+    // align start of value field on 32 bit boundary
+    curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int);
+
+    fx_param->vsize = 0;
+    value = value->first_child;
+    while (value) {
+        ALOGV("loadEffectParameter() reading value of type %s", value->name);
+        size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize);
+        if (size == 0) {
+            goto error;
+        }
+        fx_param->vsize += size;
+        value = value->next;
+    }
+
+    return fx_param;
+
+error:
+    delete fx_param;
+    return NULL;
+}
+
+void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params)
+{
+    cnode *node = root->first_child;
+    while (node) {
+        ALOGV("loadEffectParameters() loading param %s", node->name);
+        effect_param_t *param = loadEffectParameter(node);
+        if (param == NULL) {
+            node = node->next;
+            continue;
+        }
+        params.add(param);
+        node = node->next;
+    }
+}
+
+
+AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig(
+                                                            cnode *root,
+                                                            const Vector <EffectDesc *>& effects)
+{
+    cnode *node = root->first_child;
+    if (node == NULL) {
+        ALOGW("loadInputSource() empty element %s", root->name);
+        return NULL;
+    }
+    EffectDescVector *desc = new EffectDescVector();
+    while (node) {
+        size_t i;
+        for (i = 0; i < effects.size(); i++) {
+            if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) {
+                ALOGV("loadEffectConfig() found effect %s in list", node->name);
+                break;
+            }
+        }
+        if (i == effects.size()) {
+            ALOGV("loadEffectConfig() effect %s not in list", node->name);
+            node = node->next;
+            continue;
+        }
+        EffectDesc *effect = new EffectDesc(*effects[i]);   // deep copy
+        loadEffectParameters(node, effect->mParams);
+        ALOGV("loadEffectConfig() adding effect %s uuid %08x",
+              effect->mName, effect->mUuid.timeLow);
+        desc->mEffects.add(effect);
+        node = node->next;
+    }
+    if (desc->mEffects.size() == 0) {
+        ALOGW("loadEffectConfig() no valid effects found in config %s", root->name);
+        delete desc;
+        return NULL;
+    }
+    return desc;
+}
+
+status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root,
+                                                           const Vector <EffectDesc *>& effects)
+{
+    cnode *node = config_find(root, PREPROCESSING_TAG);
+    if (node == NULL) {
+        return -ENOENT;
+    }
+    node = node->first_child;
+    while (node) {
+        audio_source_t source = inputSourceNameToEnum(node->name);
+        if (source == AUDIO_SOURCE_CNT) {
+            ALOGW("loadInputSources() invalid input source %s", node->name);
+            node = node->next;
+            continue;
+        }
+        ALOGV("loadInputSources() loading input source %s", node->name);
+        EffectDescVector *desc = loadEffectConfig(node, effects);
+        if (desc == NULL) {
+            node = node->next;
+            continue;
+        }
+        mInputSources.add(source, desc);
+        node = node->next;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root,
+                                                            const Vector <EffectDesc *>& effects)
+{
+    cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG);
+    if (node == NULL) {
+        return -ENOENT;
+    }
+    node = node->first_child;
+    while (node) {
+        audio_stream_type_t stream = streamNameToEnum(node->name);
+        if (stream == AUDIO_STREAM_CNT) {
+            ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name);
+            node = node->next;
+            continue;
+        }
+        ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name);
+        EffectDescVector *desc = loadEffectConfig(node, effects);
+        if (desc == NULL) {
+            node = node->next;
+            continue;
+        }
+        mOutputStreams.add(stream, desc);
+        node = node->next;
+    }
+    return NO_ERROR;
+}
+
+AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root)
+{
+    cnode *node = config_find(root, UUID_TAG);
+    if (node == NULL) {
+        return NULL;
+    }
+    effect_uuid_t uuid;
+    if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) {
+        ALOGW("loadEffect() invalid uuid %s", node->value);
+        return NULL;
+    }
+    return new EffectDesc(root->name, uuid);
+}
+
+status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects)
+{
+    cnode *node = config_find(root, EFFECTS_TAG);
+    if (node == NULL) {
+        return -ENOENT;
+    }
+    node = node->first_child;
+    while (node) {
+        ALOGV("loadEffects() loading effect %s", node->name);
+        EffectDesc *effect = loadEffect(node);
+        if (effect == NULL) {
+            node = node->next;
+            continue;
+        }
+        effects.add(effect);
+        node = node->next;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path)
+{
+    cnode *root;
+    char *data;
+
+    data = (char *)load_file(path, NULL);
+    if (data == NULL) {
+        return -ENODEV;
+    }
+    root = config_node("", "");
+    config_load(root, data);
+
+    Vector <EffectDesc *> effects;
+    loadEffects(root, effects);
+    loadInputEffectConfigurations(root, effects);
+    loadStreamEffectConfigurations(root, effects);
+
+    config_free(root);
+    free(root);
+    free(data);
+
+    return NO_ERROR;
+}
+
+
+}; // namespace android
diff --git a/services/audiopolicy/AudioPolicyEffects.h b/services/audiopolicy/AudioPolicyEffects.h
new file mode 100755
index 0000000..351cb1a
--- /dev/null
+++ b/services/audiopolicy/AudioPolicyEffects.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 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_AUDIOPOLICYEFFECTS_H
+#define ANDROID_AUDIOPOLICYEFFECTS_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <cutils/misc.h>
+#include <media/AudioEffect.h>
+#include <system/audio.h>
+#include <hardware/audio_effect.h>
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// AudioPolicyEffects class
+// This class will manage all effects attached to input and output streams in
+// AudioPolicyService as configured in audio_effects.conf.
+class AudioPolicyEffects : public RefBase
+{
+
+public:
+
+    // The constructor will parse audio_effects.conf
+    // First it will look whether vendor specific file exists,
+    // otherwise it will parse the system default file.
+	         AudioPolicyEffects();
+    virtual ~AudioPolicyEffects();
+
+    // Return a list of effect descriptors for default input effects
+    // associated with audioSession
+    status_t queryDefaultInputEffects(int audioSession,
+                             effect_descriptor_t *descriptors,
+                             uint32_t *count);
+
+    // Add all input effects associated with this input
+    // Effects are attached depending on the audio_source_t
+    status_t addInputEffects(audio_io_handle_t input,
+                             audio_source_t inputSource,
+                             int audioSession);
+
+    // Add all input effects associated to this input
+    status_t releaseInputEffects(audio_io_handle_t input);
+
+
+    // Return a list of effect descriptors for default output effects
+    // associated with audioSession
+    status_t queryDefaultOutputSessionEffects(int audioSession,
+                             effect_descriptor_t *descriptors,
+                             uint32_t *count);
+
+    // Add all output effects associated to this output
+    // Effects are attached depending on the audio_stream_type_t
+    status_t addOutputSessionEffects(audio_io_handle_t output,
+                             audio_stream_type_t stream,
+                             int audioSession);
+
+    // release all output effects associated with this output stream and audiosession
+    status_t releaseOutputSessionEffects(audio_io_handle_t output,
+                             audio_stream_type_t stream,
+                             int audioSession);
+
+private:
+
+    // class to store the description of an effects and its parameters
+    // as defined in audio_effects.conf
+    class EffectDesc {
+    public:
+        EffectDesc(const char *name, const effect_uuid_t& uuid) :
+                        mName(strdup(name)),
+                        mUuid(uuid) { }
+        EffectDesc(const EffectDesc& orig) :
+                        mName(strdup(orig.mName)),
+                        mUuid(orig.mUuid) {
+                            // deep copy mParams
+                            for (size_t k = 0; k < orig.mParams.size(); k++) {
+                                effect_param_t *origParam = orig.mParams[k];
+                                // psize and vsize are rounded up to an int boundary for allocation
+                                size_t origSize = sizeof(effect_param_t) +
+                                                  ((origParam->psize + 3) & ~3) +
+                                                  ((origParam->vsize + 3) & ~3);
+                                effect_param_t *dupParam = (effect_param_t *) malloc(origSize);
+                                memcpy(dupParam, origParam, origSize);
+                                // This works because the param buffer allocation is also done by
+                                // multiples of 4 bytes originally. In theory we should memcpy only
+                                // the actual param size, that is without rounding vsize.
+                                mParams.add(dupParam);
+                            }
+                        }
+        /*virtual*/ ~EffectDesc() {
+            free(mName);
+            for (size_t k = 0; k < mParams.size(); k++) {
+                free(mParams[k]);
+            }
+        }
+        char *mName;
+        effect_uuid_t mUuid;
+        Vector <effect_param_t *> mParams;
+    };
+
+    // class to store voctor of EffectDesc
+    class EffectDescVector {
+    public:
+        EffectDescVector() {}
+        /*virtual*/ ~EffectDescVector() {
+            for (size_t j = 0; j < mEffects.size(); j++) {
+                delete mEffects[j];
+            }
+        }
+        Vector <EffectDesc *> mEffects;
+    };
+
+    // class to store voctor of AudioEffects
+    class EffectVector {
+    public:
+        EffectVector(int session) : mSessionId(session) {}
+        /*virtual*/ ~EffectVector() {}
+        const int mSessionId;
+        Vector< sp<AudioEffect> >mEffects;
+    };
+
+
+    static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
+    audio_source_t inputSourceNameToEnum(const char *name);
+
+    static const char *kStreamNames[AUDIO_STREAM_CNT+1]; //+1 required as streams start from -1
+    audio_stream_type_t streamNameToEnum(const char *name);
+
+    // Enable or disable all effects in effect vector
+    void setProcessorEnabled(const EffectVector *effectVector, bool enabled);
+
+    // Parse audio_effects.conf
+    status_t loadAudioEffectConfig(const char *path);
+
+    // Load all effects descriptors in configuration file
+    status_t loadEffects(cnode *root, Vector <EffectDesc *>& effects);
+    EffectDesc *loadEffect(cnode *root);
+
+    // Load all automatic effect configurations
+    status_t loadInputEffectConfigurations(cnode *root, const Vector <EffectDesc *>& effects);
+    status_t loadStreamEffectConfigurations(cnode *root, const Vector <EffectDesc *>& effects);
+    EffectDescVector *loadEffectConfig(cnode *root, const Vector <EffectDesc *>& effects);
+
+    // Load all automatic effect parameters
+    void loadEffectParameters(cnode *root, Vector <effect_param_t *>& params);
+    effect_param_t *loadEffectParameter(cnode *root);
+    size_t readParamValue(cnode *node,
+                          char *param,
+                          size_t *curSize,
+                          size_t *totSize);
+    size_t growParamSize(char *param,
+                         size_t size,
+                         size_t *curSize,
+                         size_t *totSize);
+
+    // Automatic input effects are configured per audio_source_t
+    KeyedVector< audio_source_t, EffectDescVector* > mInputSources;
+    // Automatic input effects are unique for audio_io_handle_t
+    KeyedVector< audio_io_handle_t, EffectVector* > mInputs;
+
+    // Automatic output effects are organized per audio_stream_type_t
+    KeyedVector< audio_stream_type_t, EffectDescVector* > mOutputStreams;
+    // Automatic output effects are unique for audiosession ID
+    KeyedVector< int32_t, EffectVector* > mOutputSessions;
+};
+
+}; // namespace android
+
+#endif // ANDROID_AUDIOPOLICYEFFECTS_H
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index c025a45..33e4397 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -90,6 +90,12 @@
                                         audio_channel_mask_t channelMask,
                                         audio_output_flags_t flags,
                                         const audio_offload_info_t *offloadInfo) = 0;
+    virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+                                                uint32_t samplingRate,
+                                                audio_format_t format,
+                                                audio_channel_mask_t channelMask,
+                                                audio_output_flags_t flags,
+                                                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,
                                  audio_stream_type_t stream,
diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
old mode 100644
new mode 100755
index 8cc386a..5a13ac2
--- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
@@ -131,6 +131,22 @@
                                     format, channelMask, flags, offloadInfo);
 }
 
+audio_io_handle_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
+                                    uint32_t samplingRate,
+                                    audio_format_t format,
+                                    audio_channel_mask_t channelMask,
+                                    audio_output_flags_t flags,
+                                    const audio_offload_info_t *offloadInfo)
+{
+    if (mAudioPolicyManager == NULL) {
+        return 0;
+    }
+    ALOGV("getOutput()");
+    Mutex::Autolock _l(mLock);
+    return mAudioPolicyManager->getOutputForAttr(attr, samplingRate,
+                                    format, channelMask, flags, offloadInfo);
+}
+
 status_t AudioPolicyService::startOutput(audio_io_handle_t output,
                                          audio_stream_type_t stream,
                                          int session)
@@ -140,6 +156,13 @@
     }
     ALOGV("startOutput()");
     Mutex::Autolock _l(mLock);
+
+    // create audio processors according to stream
+    status_t status = mAudioPolicyEffects->addOutputSessionEffects(output, stream, session);
+    if (status != NO_ERROR && status != ALREADY_EXISTS) {
+        ALOGW("Failed to add effects on session %d", session);
+    }
+
     return mAudioPolicyManager->startOutput(output, stream, session);
 }
 
@@ -161,6 +184,13 @@
 {
     ALOGV("doStopOutput from tid %d", gettid());
     Mutex::Autolock _l(mLock);
+
+    // release audio processors from the stream
+    status_t status = mAudioPolicyEffects->releaseOutputSessionEffects(output, stream, session);
+    if (status != NO_ERROR && status != ALREADY_EXISTS) {
+        ALOGW("Failed to release effects on session %d", session);
+    }
+
     return mAudioPolicyManager->stopOutput(output, stream, session);
 }
 
@@ -206,39 +236,13 @@
     if (input == 0) {
         return input;
     }
+
     // create audio pre processors according to input source
-    audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
-                                    AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
-
-    ssize_t index = mInputSources.indexOfKey(aliasSource);
-    if (index < 0) {
-        return input;
-    }
-    ssize_t idx = mInputs.indexOfKey(input);
-    InputDesc *inputDesc;
-    if (idx < 0) {
-        inputDesc = new InputDesc(audioSession);
-        mInputs.add(input, inputDesc);
-    } else {
-        inputDesc = mInputs.valueAt(idx);
+    status_t status = mAudioPolicyEffects->addInputEffects(input, inputSource, audioSession);
+    if (status != NO_ERROR && status != ALREADY_EXISTS) {
+        ALOGW("Failed to add effects on input %d", input);
     }
 
-    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);
-        status_t status = fx->initCheck();
-        if (status != NO_ERROR && status != ALREADY_EXISTS) {
-            ALOGW("Failed to create Fx %s on input %d", effect->mName, input);
-            // fx goes out of scope and strong ref on AudioEffect is released
-            continue;
-        }
-        for (size_t j = 0; j < effect->mParams.size(); j++) {
-            fx->setParameter(effect->mParams[j]);
-        }
-        inputDesc->mEffects.add(fx);
-    }
-    setPreProcessorEnabled(inputDesc, true);
     return input;
 }
 
@@ -270,14 +274,11 @@
     Mutex::Autolock _l(mLock);
     mAudioPolicyManager->releaseInput(input);
 
-    ssize_t index = mInputs.indexOfKey(input);
-    if (index < 0) {
-        return;
+    // release audio processors from the input
+    status_t status = mAudioPolicyEffects->releaseInputEffects(input);
+    if(status != NO_ERROR) {
+        ALOGW("Failed to release effects on input %d", input);
     }
-    InputDesc *inputDesc = mInputs.valueAt(index);
-    setPreProcessorEnabled(inputDesc, false);
-    delete inputDesc;
-    mInputs.removeItemsAt(index);
 }
 
 status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
@@ -420,37 +421,13 @@
                                                        effect_descriptor_t *descriptors,
                                                        uint32_t *count)
 {
-
     if (mAudioPolicyManager == NULL) {
         *count = 0;
         return NO_INIT;
     }
     Mutex::Autolock _l(mLock);
-    status_t status = NO_ERROR;
 
-    size_t index;
-    for (index = 0; index < mInputs.size(); index++) {
-        if (mInputs.valueAt(index)->mSessionId == audioSession) {
-            break;
-        }
-    }
-    if (index == mInputs.size()) {
-        *count = 0;
-        return BAD_VALUE;
-    }
-    Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects;
-
-    for (size_t i = 0; i < effects.size(); i++) {
-        effect_descriptor_t desc = effects[i]->descriptor();
-        if (i < *count) {
-            descriptors[i] = desc;
-        }
-    }
-    if (effects.size() > *count) {
-        status = NO_MEMORY;
-    }
-    *count = effects.size();
-    return status;
+    return mAudioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count);
 }
 
 bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
old mode 100644
new mode 100755
index 0bf4982..406988c
--- a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
@@ -144,6 +144,13 @@
     }
     ALOGV("startOutput()");
     Mutex::Autolock _l(mLock);
+
+    // create audio processors according to stream
+    status_t status = mAudioPolicyEffects->addOutputSessionEffects(output, stream, session);
+    if (status != NO_ERROR && status != ALREADY_EXISTS) {
+        ALOGW("Failed to add effects on session %d", session);
+    }
+
     return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);
 }
 
@@ -165,6 +172,13 @@
 {
     ALOGV("doStopOutput from tid %d", gettid());
     Mutex::Autolock _l(mLock);
+
+    // release audio processors from the stream
+    status_t status = mAudioPolicyEffects->releaseOutputSessionEffects(output, stream, session);
+    if (status != NO_ERROR && status != ALREADY_EXISTS) {
+        ALOGW("Failed to release effects on session %d", session);
+    }
+
     return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
 }
 
@@ -210,39 +224,13 @@
     if (input == 0) {
         return input;
     }
+
     // create audio pre processors according to input source
-    audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
-                                    AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
-
-    ssize_t index = mInputSources.indexOfKey(aliasSource);
-    if (index < 0) {
-        return input;
-    }
-    ssize_t idx = mInputs.indexOfKey(input);
-    InputDesc *inputDesc;
-    if (idx < 0) {
-        inputDesc = new InputDesc(audioSession);
-        mInputs.add(input, inputDesc);
-    } else {
-        inputDesc = mInputs.valueAt(idx);
+    status_t status = mAudioPolicyEffects->addInputEffects(input, inputSource, audioSession);
+    if (status != NO_ERROR && status != ALREADY_EXISTS) {
+        ALOGW("Failed to add effects on input %d", input);
     }
 
-    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);
-        status_t status = fx->initCheck();
-        if (status != NO_ERROR && status != ALREADY_EXISTS) {
-            ALOGW("Failed to create Fx %s on input %d", effect->mName, input);
-            // fx goes out of scope and strong ref on AudioEffect is released
-            continue;
-        }
-        for (size_t j = 0; j < effect->mParams.size(); j++) {
-            fx->setParameter(effect->mParams[j]);
-        }
-        inputDesc->mEffects.add(fx);
-    }
-    setPreProcessorEnabled(inputDesc, true);
     return input;
 }
 
@@ -274,14 +262,11 @@
     Mutex::Autolock _l(mLock);
     mpAudioPolicy->release_input(mpAudioPolicy, input);
 
-    ssize_t index = mInputs.indexOfKey(input);
-    if (index < 0) {
-        return;
+    // release audio processors from the input
+    status_t status = mAudioPolicyEffects->releaseInputEffects(input);
+    if(status != NO_ERROR) {
+        ALOGW("Failed to release effects on input %d", input);
     }
-    InputDesc *inputDesc = mInputs.valueAt(index);
-    setPreProcessorEnabled(inputDesc, false);
-    delete inputDesc;
-    mInputs.removeItemsAt(index);
 }
 
 status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
@@ -437,37 +422,13 @@
                                                        effect_descriptor_t *descriptors,
                                                        uint32_t *count)
 {
-
     if (mpAudioPolicy == NULL) {
         *count = 0;
         return NO_INIT;
     }
     Mutex::Autolock _l(mLock);
-    status_t status = NO_ERROR;
 
-    size_t index;
-    for (index = 0; index < mInputs.size(); index++) {
-        if (mInputs.valueAt(index)->mSessionId == audioSession) {
-            break;
-        }
-    }
-    if (index == mInputs.size()) {
-        *count = 0;
-        return BAD_VALUE;
-    }
-    Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects;
-
-    for (size_t i = 0; i < effects.size(); i++) {
-        effect_descriptor_t desc = effects[i]->descriptor();
-        if (i < *count) {
-            descriptors[i] = desc;
-        }
-    }
-    if (effects.size() > *count) {
-        status = NO_MEMORY;
-    }
-    *count = effects.size();
-    return status;
+    return mAudioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count);
 }
 
 bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 9708d0f..98f367e 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -123,6 +123,11 @@
     STRING_TO_ENUM(AUDIO_FORMAT_MP3),
     STRING_TO_ENUM(AUDIO_FORMAT_AAC),
     STRING_TO_ENUM(AUDIO_FORMAT_VORBIS),
+    STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1),
+    STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2),
+    STRING_TO_ENUM(AUDIO_FORMAT_OPUS),
+    STRING_TO_ENUM(AUDIO_FORMAT_AC3),
+    STRING_TO_ENUM(AUDIO_FORMAT_E_AC3),
 };
 
 const StringToEnum sOutChannelsNameToEnumTable[] = {
@@ -512,7 +517,10 @@
             config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
             config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
             config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE &&
-            config != AUDIO_POLICY_FORCE_NO_BT_A2DP) {
+            config != AUDIO_POLICY_FORCE_NO_BT_A2DP &&
+            config != AUDIO_POLICY_FORCE_SYSTEM_AUDIO_HDMI_ARC &&
+            config != AUDIO_POLICY_FORCE_SYSTEM_AUDIO_SPDIF &&
+            config != AUDIO_POLICY_FORCE_SYSTEM_AUDIO_LINE) {
             ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
             return;
         }
@@ -624,13 +632,53 @@
                                     audio_output_flags_t flags,
                                     const audio_offload_info_t *offloadInfo)
 {
-    audio_io_handle_t output = 0;
-    uint32_t latency = 0;
+
     routing_strategy strategy = getStrategy(stream);
     audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
     ALOGV("getOutput() device %d, stream %d, samplingRate %d, format %x, channelMask %x, flags %x",
           device, stream, samplingRate, format, channelMask, flags);
 
+    return getOutputForDevice(device, stream, samplingRate,format, channelMask, flags,
+            offloadInfo);
+}
+
+audio_io_handle_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
+                                    uint32_t samplingRate,
+                                    audio_format_t format,
+                                    audio_channel_mask_t channelMask,
+                                    audio_output_flags_t flags,
+                                    const audio_offload_info_t *offloadInfo)
+{
+    if (attr == NULL) {
+        ALOGE("getOutputForAttr() called with NULL audio attributes");
+        return 0;
+    }
+    ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s",
+            attr->usage, attr->content_type, attr->tags);
+
+    // TODO this is where filtering for custom policies (rerouting, dynamic sources) will go
+    routing_strategy strategy = (routing_strategy) getStrategyForAttr(attr);
+    audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
+    ALOGV("getOutputForAttr() device %d, samplingRate %d, format %x, channelMask %x, flags %x",
+          device, samplingRate, format, channelMask, flags);
+
+    audio_stream_type_t stream = streamTypefromAttributesInt(attr);
+    return getOutputForDevice(device, stream, samplingRate, format, channelMask, flags,
+                offloadInfo);
+}
+
+audio_io_handle_t AudioPolicyManager::getOutputForDevice(
+        audio_devices_t device,
+        audio_stream_type_t stream,
+        uint32_t samplingRate,
+        audio_format_t format,
+        audio_channel_mask_t channelMask,
+        audio_output_flags_t flags,
+        const audio_offload_info_t *offloadInfo)
+{
+    audio_io_handle_t output = 0;
+    uint32_t latency = 0;
+
 #ifdef AUDIO_POLICY_TEST
     if (mCurOutput != 0) {
         ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelMask %x, mDirectOutput %d",
@@ -1516,7 +1564,7 @@
     snprintf(buffer, SIZE,
              " Stream  Can be muted  Index Min  Index Max  Index Cur [device : index]...\n");
     write(fd, buffer, strlen(buffer));
-    for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
+    for (size_t i = 0; i < AUDIO_STREAM_CNT; i++) {
         snprintf(buffer, SIZE, " %02zu      ", i);
         write(fd, buffer, strlen(buffer));
         mStreams[i].dump(fd);
@@ -1647,14 +1695,20 @@
             *num_ports += mInputs.size();
         }
         if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
-            for (size_t i = 0; i < mOutputs.size() && portsWritten < portsMax; i++) {
-                mOutputs[i]->toAudioPort(&ports[portsWritten++]);
+            size_t numOutputs = 0;
+            for (size_t i = 0; i < mOutputs.size(); i++) {
+                if (!mOutputs[i]->isDuplicated()) {
+                    numOutputs++;
+                    if (portsWritten < portsMax) {
+                        mOutputs[i]->toAudioPort(&ports[portsWritten++]);
+                    }
+                }
             }
-            *num_ports += mOutputs.size();
+            *num_ports += numOutputs;
         }
     }
     *generation = curAudioPortGeneration();
-    ALOGV("listAudioPorts() got %d ports needed %d", portsWritten, *num_ports);
+    ALOGV("listAudioPorts() got %zu ports needed %d", portsWritten, *num_ports);
     return NO_ERROR;
 }
 
@@ -1782,6 +1836,8 @@
             ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
             return BAD_VALUE;
         }
+        ALOG_ASSERT(!outputDesc->isDuplicated(),"duplicated output %d in source in ports",
+                                                outputDesc->mIoHandle);
         if (patchDesc != 0) {
             if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
                 ALOGV("createAudioPatch() source id differs for patch current id %d new id %d",
@@ -1796,7 +1852,7 @@
             return BAD_VALUE;
         }
 
-        if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+        if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType,
                                                        patch->sources[0].sample_rate,
                                                      patch->sources[0].format,
                                                      patch->sources[0].channel_mask,
@@ -1805,9 +1861,9 @@
         }
         // TODO: reconfigure output format and channels here
         ALOGV("createAudioPatch() setting device %08x on output %d",
-                                              devDesc->mType, outputDesc->mIoHandle);
+                                              devDesc->mDeviceType, outputDesc->mIoHandle);
         setOutputDevice(outputDesc->mIoHandle,
-                        devDesc->mType,
+                        devDesc->mDeviceType,
                        true,
                        0,
                        handle);
@@ -1841,7 +1897,7 @@
                 return BAD_VALUE;
             }
 
-            if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+            if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType,
                                                            patch->sinks[0].sample_rate,
                                                          patch->sinks[0].format,
                                                          patch->sinks[0].channel_mask,
@@ -1850,9 +1906,9 @@
             }
             // TODO: reconfigure output format and channels here
             ALOGV("createAudioPatch() setting device %08x on output %d",
-                                                  devDesc->mType, inputDesc->mIoHandle);
+                                                  devDesc->mDeviceType, inputDesc->mIoHandle);
             setInputDevice(inputDesc->mIoHandle,
-                           devDesc->mType,
+                           devDesc->mDeviceType,
                            true,
                            handle);
             index = mAudioPatches.indexOfKey(*handle);
@@ -1998,7 +2054,7 @@
             generation == NULL) {
         return BAD_VALUE;
     }
-    ALOGV("listAudioPatches() num_patches %d patches %p available patches %d",
+    ALOGV("listAudioPatches() num_patches %d patches %p available patches %zu",
           *num_patches, patches, mAudioPatches.size());
     if (patches == NULL) {
         *num_patches = 0;
@@ -2010,13 +2066,13 @@
             i  < mAudioPatches.size() && patchesWritten < patchesMax; i++) {
         patches[patchesWritten] = mAudioPatches[i]->mPatch;
         patches[patchesWritten++].id = mAudioPatches[i]->mHandle;
-        ALOGV("listAudioPatches() patch %d num_sources %d num_sinks %d",
+        ALOGV("listAudioPatches() patch %zu num_sources %d num_sinks %d",
               i, mAudioPatches[i]->mPatch.num_sources, mAudioPatches[i]->mPatch.num_sinks);
     }
     *num_patches = mAudioPatches.size();
 
     *generation = curAudioPortGeneration();
-    ALOGV("listAudioPatches() got %d patches needed %d", patchesWritten, *num_patches);
+    ALOGV("listAudioPatches() got %zu patches needed %d", patchesWritten, *num_patches);
     return NO_ERROR;
 }
 
@@ -2040,6 +2096,9 @@
             if (outputDesc == NULL) {
                 return BAD_VALUE;
             }
+            ALOG_ASSERT(!outputDesc->isDuplicated(),
+                        "setAudioPortConfig() called on duplicated output %d",
+                        outputDesc->mIoHandle);
             audioPortConfig = outputDesc;
         } else if (config->role == AUDIO_PORT_ROLE_SINK) {
             sp<AudioInputDescriptor> inputDesc = getInputFromId(config->id);
@@ -2780,7 +2839,7 @@
             {
                 if (mHwModules[module_idx]->mInputProfiles[profile_index]->mSupportedDevices.types()
                         & (device & ~AUDIO_DEVICE_BIT_IN)) {
-                    ALOGV("checkInputsForDevice(): adding profile %d from module %d",
+                    ALOGV("checkInputsForDevice(): adding profile %zu from module %zu",
                           profile_index, module_idx);
                     profiles.add(mHwModules[module_idx]->mInputProfiles[profile_index]);
                 }
@@ -2904,7 +2963,7 @@
                  profile_index++) {
                 sp<IOProfile> profile = mHwModules[module_index]->mInputProfiles[profile_index];
                 if (profile->mSupportedDevices.types() & device) {
-                    ALOGV("checkInputsForDevice(): clearing direct input profile %d on module %d",
+                    ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %zu",
                           profile_index, module_index);
                     if (profile->mSamplingRates[0] == 0) {
                         profile->mSamplingRates.clear();
@@ -3240,6 +3299,44 @@
     }
 }
 
+uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
+    // flags to strategy mapping
+    if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
+        return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;
+    }
+
+    // usage to strategy mapping
+    switch (attr->usage) {
+    case AUDIO_USAGE_MEDIA:
+    case AUDIO_USAGE_GAME:
+    case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
+    case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+    case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
+        return (uint32_t) STRATEGY_MEDIA;
+
+    case AUDIO_USAGE_VOICE_COMMUNICATION:
+        return (uint32_t) STRATEGY_PHONE;
+
+    case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+        return (uint32_t) STRATEGY_DTMF;
+
+    case AUDIO_USAGE_ALARM:
+    case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+        return (uint32_t) STRATEGY_SONIFICATION;
+
+    case AUDIO_USAGE_NOTIFICATION:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+    case AUDIO_USAGE_NOTIFICATION_EVENT:
+        return (uint32_t) STRATEGY_SONIFICATION_RESPECTFUL;
+
+    case AUDIO_USAGE_UNKNOWN:
+    default:
+        return (uint32_t) STRATEGY_MEDIA;
+    }
+}
+
 void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) {
     switch(stream) {
     case AUDIO_STREAM_MUSIC:
@@ -3443,10 +3540,32 @@
         if (device2 == AUDIO_DEVICE_NONE) {
             device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER;
         }
+        int device3 = AUDIO_DEVICE_NONE;
+        if (strategy == STRATEGY_MEDIA) {
+            // Hdmi system audio should use manually configured device type.
+            if (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA]
+                    == AUDIO_POLICY_FORCE_SYSTEM_AUDIO_HDMI_ARC) {
+                device3 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_HDMI_ARC;
+            } else if (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA]
+                    == AUDIO_POLICY_FORCE_SYSTEM_AUDIO_SPDIF) {
+                device3 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPDIF;
+            } else if (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA]
+                    == AUDIO_POLICY_FORCE_SYSTEM_AUDIO_LINE) {
+                device3 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE;
+            }
+        }
 
+        // Merge hdmi cec system audio and existing device for media. If system audio is on,
+        // internal speaker will be muted but others are not.
+        device2 |= device3;
         // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
         // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
         device |= device2;
+
+        // If system audio mode is on and proper audio out is set, remove speaker from device.
+        if (device3 != AUDIO_DEVICE_NONE) {
+             device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+        }
         if (device) break;
         device = mDefaultOutputDevice->mDeviceType;
         if (device == AUDIO_DEVICE_NONE) {
@@ -4125,9 +4244,7 @@
     if (stream == AUDIO_STREAM_MUSIC &&
         index != mStreams[stream].mIndexMin &&
         (device == AUDIO_DEVICE_OUT_AUX_DIGITAL ||
-         device == AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET ||
-         device == AUDIO_DEVICE_OUT_USB_ACCESSORY ||
-         device == AUDIO_DEVICE_OUT_USB_DEVICE)) {
+         device == AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) {
         return 1.0;
     }
 
@@ -4501,10 +4618,12 @@
                                                  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) {
-        dstConfig->config_mask &= srcConfig->config_mask;
+        dstConfig->config_mask |= srcConfig->config_mask;
     }
     AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
 
@@ -4519,6 +4638,7 @@
 void AudioPolicyManager::AudioOutputDescriptor::toAudioPort(
                                                     struct audio_port *port) const
 {
+    ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
     mProfile->toAudioPort(port);
     port->id = mId;
     toAudioPortConfig(&port->active_config);
@@ -4584,10 +4704,12 @@
                                                    struct audio_port_config *dstConfig,
                                                    const struct audio_port_config *srcConfig) const
 {
+    ALOG_ASSERT(mProfile != 0,
+                "toAudioPortConfig() called on input with null profile %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) {
-        dstConfig->config_mask &= srcConfig->config_mask;
+        dstConfig->config_mask |= srcConfig->config_mask;
     }
 
     AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
@@ -4603,6 +4725,8 @@
 void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
                                                     struct audio_port *port) const
 {
+    ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle);
+
     mProfile->toAudioPort(port);
     port->id = mId;
     toAudioPortConfig(&port->active_config);
@@ -4912,7 +5036,7 @@
     }
     port->num_formats = i;
 
-    ALOGV("AudioPort::toAudioPort() num gains %d", mGains.size());
+    ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size());
 
     for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
         port->gains[i] = mGains[i]->mGain;
@@ -5610,7 +5734,7 @@
 {
     sp<DeviceDescriptor> device;
     for (size_t i = 0; i < size(); i++) {
-        ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%d)->mId %d", id, i, itemAt(i)->mId);
+        ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%zu)->mId %d", id, i, itemAt(i)->mId);
         if (itemAt(i)->mId == id) {
             device = itemAt(i);
             break;
@@ -5653,7 +5777,7 @@
 {
     dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN;
     if (srcConfig != NULL) {
-        dstConfig->config_mask &= srcConfig->config_mask;
+        dstConfig->config_mask |= srcConfig->config_mask;
     }
 
     AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
@@ -5917,4 +6041,46 @@
     mHwModules.add(module);
 }
 
+audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_attributes_t *attr)
+{
+    // flags to stream type mapping
+    if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
+        return AUDIO_STREAM_ENFORCED_AUDIBLE;
+    }
+    if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) {
+        return AUDIO_STREAM_BLUETOOTH_SCO;
+    }
+
+    // usage to stream type mapping
+    switch (attr->usage) {
+    case AUDIO_USAGE_MEDIA:
+    case AUDIO_USAGE_GAME:
+    case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
+    case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+        return AUDIO_STREAM_MUSIC;
+    case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
+        return AUDIO_STREAM_SYSTEM;
+    case AUDIO_USAGE_VOICE_COMMUNICATION:
+        return AUDIO_STREAM_VOICE_CALL;
+
+    case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+        return AUDIO_STREAM_DTMF;
+
+    case AUDIO_USAGE_ALARM:
+        return AUDIO_STREAM_ALARM;
+    case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+        return AUDIO_STREAM_RING;
+
+    case AUDIO_USAGE_NOTIFICATION:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+    case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+    case AUDIO_USAGE_NOTIFICATION_EVENT:
+        return AUDIO_STREAM_NOTIFICATION;
+
+    case AUDIO_USAGE_UNKNOWN:
+    default:
+        return AUDIO_STREAM_MUSIC;
+    }
+}
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h
index 1abeb6a..c23d994 100644
--- a/services/audiopolicy/AudioPolicyManager.h
+++ b/services/audiopolicy/AudioPolicyManager.h
@@ -84,6 +84,12 @@
                                             audio_channel_mask_t channelMask,
                                             audio_output_flags_t flags,
                                             const audio_offload_info_t *offloadInfo);
+        virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+                                            uint32_t samplingRate,
+                                            audio_format_t format,
+                                            audio_channel_mask_t channelMask,
+                                            audio_output_flags_t flags,
+                                            const audio_offload_info_t *offloadInfo);
         virtual status_t startOutput(audio_io_handle_t output,
                                      audio_stream_type_t stream,
                                      int session = 0);
@@ -116,6 +122,8 @@
 
         // return the strategy corresponding to a given stream type
         virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
+        // return the strategy corresponding to the given audio attributes
+        virtual uint32_t getStrategyForAttr(const audio_attributes_t *attr);
 
         // return the enabled output devices for the given stream type
         virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream);
@@ -755,6 +763,17 @@
         uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; }
         // converts device address to string sent to audio HAL via setParameters
         static String8 addressToParameter(audio_devices_t device, const String8 address);
+        // internal method to return the output handle for the given device and format
+        audio_io_handle_t getOutputForDevice(
+                audio_devices_t device,
+                audio_stream_type_t stream,
+                uint32_t samplingRate,
+                audio_format_t format,
+                audio_channel_mask_t channelMask,
+                audio_output_flags_t flags,
+                const audio_offload_info_t *offloadInfo);
+        // internal function to derive a stream type value from audio attributes
+        audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr);
 };
 
 };
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
old mode 100644
new mode 100755
index 93fab11..9435797
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -40,8 +40,6 @@
 #include <system/audio.h>
 #include <system/audio_policy.h>
 #include <hardware/audio_policy.h>
-#include <audio_effects/audio_effects_conf.h>
-#include <media/AudioParameter.h>
 
 namespace android {
 
@@ -111,12 +109,8 @@
     mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
 #endif
 
-    // load audio pre processing modules
-    if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
-        loadPreProcessorConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
-    } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
-        loadPreProcessorConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
-    }
+    // load audio processing modules
+    mAudioPolicyEffects = new AudioPolicyEffects();
 }
 
 AudioPolicyService::~AudioPolicyService()
@@ -125,18 +119,6 @@
     mAudioCommandThread->exit();
     mOutputCommandThread->exit();
 
-    // release audio pre processing resources
-    for (size_t i = 0; i < mInputSources.size(); i++) {
-        delete mInputSources.valueAt(i);
-    }
-    mInputSources.clear();
-
-    for (size_t i = 0; i < mInputs.size(); i++) {
-        mInputs.valueAt(i)->mEffects.clear();
-        delete mInputs.valueAt(i);
-    }
-    mInputs.clear();
-
 #ifdef USE_LEGACY_AUDIO_POLICY
     if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) {
         mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
@@ -150,6 +132,7 @@
 #endif
 
     mNotificationClients.clear();
+    mAudioPolicyEffects.clear();
 }
 
 // A notification client is always registered by AudioSystem when the client process
@@ -353,14 +336,6 @@
     return NO_ERROR;
 }
 
-void AudioPolicyService::setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled)
-{
-    const Vector<sp<AudioEffect> > &fxVector = inputDesc->mEffects;
-    for (size_t i = 0; i < fxVector.size(); i++) {
-        fxVector.itemAt(i)->setEnabled(enabled);
-    }
-}
-
 status_t AudioPolicyService::onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -936,304 +911,6 @@
     return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
 }
 
-// ----------------------------------------------------------------------------
-// Audio pre-processing configuration
-// ----------------------------------------------------------------------------
-
-/*static*/ const char * const AudioPolicyService::kInputSourceNames[AUDIO_SOURCE_CNT -1] = {
-    MIC_SRC_TAG,
-    VOICE_UL_SRC_TAG,
-    VOICE_DL_SRC_TAG,
-    VOICE_CALL_SRC_TAG,
-    CAMCORDER_SRC_TAG,
-    VOICE_REC_SRC_TAG,
-    VOICE_COMM_SRC_TAG
-};
-
-// returns the audio_source_t enum corresponding to the input source name or
-// AUDIO_SOURCE_CNT is no match found
-audio_source_t AudioPolicyService::inputSourceNameToEnum(const char *name)
-{
-    int i;
-    for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) {
-        if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) {
-            ALOGV("inputSourceNameToEnum found source %s %d", name, i);
-            break;
-        }
-    }
-    return (audio_source_t)i;
-}
-
-size_t AudioPolicyService::growParamSize(char *param,
-                                         size_t size,
-                                         size_t *curSize,
-                                         size_t *totSize)
-{
-    // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int)
-    size_t pos = ((*curSize - 1 ) / size + 1) * size;
-
-    if (pos + size > *totSize) {
-        while (pos + size > *totSize) {
-            *totSize += ((*totSize + 7) / 8) * 4;
-        }
-        param = (char *)realloc(param, *totSize);
-    }
-    *curSize = pos + size;
-    return pos;
-}
-
-size_t AudioPolicyService::readParamValue(cnode *node,
-                                          char *param,
-                                          size_t *curSize,
-                                          size_t *totSize)
-{
-    if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) {
-        size_t pos = growParamSize(param, sizeof(short), curSize, totSize);
-        *(short *)((char *)param + pos) = (short)atoi(node->value);
-        ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos));
-        return sizeof(short);
-    } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) {
-        size_t pos = growParamSize(param, sizeof(int), curSize, totSize);
-        *(int *)((char *)param + pos) = atoi(node->value);
-        ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos));
-        return sizeof(int);
-    } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) {
-        size_t pos = growParamSize(param, sizeof(float), curSize, totSize);
-        *(float *)((char *)param + pos) = (float)atof(node->value);
-        ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos));
-        return sizeof(float);
-    } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) {
-        size_t pos = growParamSize(param, sizeof(bool), curSize, totSize);
-        if (strncmp(node->value, "false", strlen("false") + 1) == 0) {
-            *(bool *)((char *)param + pos) = false;
-        } else {
-            *(bool *)((char *)param + pos) = true;
-        }
-        ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false");
-        return sizeof(bool);
-    } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) {
-        size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
-        if (*curSize + len + 1 > *totSize) {
-            *totSize = *curSize + len + 1;
-            param = (char *)realloc(param, *totSize);
-        }
-        strncpy(param + *curSize, node->value, len);
-        *curSize += len;
-        param[*curSize] = '\0';
-        ALOGV("readParamValue() reading string %s", param + *curSize - len);
-        return len;
-    }
-    ALOGW("readParamValue() unknown param type %s", node->name);
-    return 0;
-}
-
-effect_param_t *AudioPolicyService::loadEffectParameter(cnode *root)
-{
-    cnode *param;
-    cnode *value;
-    size_t curSize = sizeof(effect_param_t);
-    size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int);
-    effect_param_t *fx_param = (effect_param_t *)malloc(totSize);
-
-    param = config_find(root, PARAM_TAG);
-    value = config_find(root, VALUE_TAG);
-    if (param == NULL && value == NULL) {
-        // try to parse simple parameter form {int int}
-        param = root->first_child;
-        if (param != NULL) {
-            // Note: that a pair of random strings is read as 0 0
-            int *ptr = (int *)fx_param->data;
-            int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
-            ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2);
-            *ptr++ = atoi(param->name);
-            *ptr = atoi(param->value);
-            fx_param->psize = sizeof(int);
-            fx_param->vsize = sizeof(int);
-            return fx_param;
-        }
-    }
-    if (param == NULL || value == NULL) {
-        ALOGW("loadEffectParameter() invalid parameter description %s", root->name);
-        goto error;
-    }
-
-    fx_param->psize = 0;
-    param = param->first_child;
-    while (param) {
-        ALOGV("loadEffectParameter() reading param of type %s", param->name);
-        size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize);
-        if (size == 0) {
-            goto error;
-        }
-        fx_param->psize += size;
-        param = param->next;
-    }
-
-    // align start of value field on 32 bit boundary
-    curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int);
-
-    fx_param->vsize = 0;
-    value = value->first_child;
-    while (value) {
-        ALOGV("loadEffectParameter() reading value of type %s", value->name);
-        size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize);
-        if (size == 0) {
-            goto error;
-        }
-        fx_param->vsize += size;
-        value = value->next;
-    }
-
-    return fx_param;
-
-error:
-    free(fx_param);
-    return NULL;
-}
-
-void AudioPolicyService::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params)
-{
-    cnode *node = root->first_child;
-    while (node) {
-        ALOGV("loadEffectParameters() loading param %s", node->name);
-        effect_param_t *param = loadEffectParameter(node);
-        if (param == NULL) {
-            node = node->next;
-            continue;
-        }
-        params.add(param);
-        node = node->next;
-    }
-}
-
-AudioPolicyService::InputSourceDesc *AudioPolicyService::loadInputSource(
-                                                            cnode *root,
-                                                            const Vector <EffectDesc *>& effects)
-{
-    cnode *node = root->first_child;
-    if (node == NULL) {
-        ALOGW("loadInputSource() empty element %s", root->name);
-        return NULL;
-    }
-    InputSourceDesc *source = new InputSourceDesc();
-    while (node) {
-        size_t i;
-        for (i = 0; i < effects.size(); i++) {
-            if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) {
-                ALOGV("loadInputSource() found effect %s in list", node->name);
-                break;
-            }
-        }
-        if (i == effects.size()) {
-            ALOGV("loadInputSource() effect %s not in list", node->name);
-            node = node->next;
-            continue;
-        }
-        EffectDesc *effect = new EffectDesc(*effects[i]);   // deep copy
-        loadEffectParameters(node, effect->mParams);
-        ALOGV("loadInputSource() adding effect %s uuid %08x", effect->mName, effect->mUuid.timeLow);
-        source->mEffects.add(effect);
-        node = node->next;
-    }
-    if (source->mEffects.size() == 0) {
-        ALOGW("loadInputSource() no valid effects found in source %s", root->name);
-        delete source;
-        return NULL;
-    }
-    return source;
-}
-
-status_t AudioPolicyService::loadInputSources(cnode *root, const Vector <EffectDesc *>& effects)
-{
-    cnode *node = config_find(root, PREPROCESSING_TAG);
-    if (node == NULL) {
-        return -ENOENT;
-    }
-    node = node->first_child;
-    while (node) {
-        audio_source_t source = inputSourceNameToEnum(node->name);
-        if (source == AUDIO_SOURCE_CNT) {
-            ALOGW("loadInputSources() invalid input source %s", node->name);
-            node = node->next;
-            continue;
-        }
-        ALOGV("loadInputSources() loading input source %s", node->name);
-        InputSourceDesc *desc = loadInputSource(node, effects);
-        if (desc == NULL) {
-            node = node->next;
-            continue;
-        }
-        mInputSources.add(source, desc);
-        node = node->next;
-    }
-    return NO_ERROR;
-}
-
-AudioPolicyService::EffectDesc *AudioPolicyService::loadEffect(cnode *root)
-{
-    cnode *node = config_find(root, UUID_TAG);
-    if (node == NULL) {
-        return NULL;
-    }
-    effect_uuid_t uuid;
-    if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) {
-        ALOGW("loadEffect() invalid uuid %s", node->value);
-        return NULL;
-    }
-    return new EffectDesc(root->name, uuid);
-}
-
-status_t AudioPolicyService::loadEffects(cnode *root, Vector <EffectDesc *>& effects)
-{
-    cnode *node = config_find(root, EFFECTS_TAG);
-    if (node == NULL) {
-        return -ENOENT;
-    }
-    node = node->first_child;
-    while (node) {
-        ALOGV("loadEffects() loading effect %s", node->name);
-        EffectDesc *effect = loadEffect(node);
-        if (effect == NULL) {
-            node = node->next;
-            continue;
-        }
-        effects.add(effect);
-        node = node->next;
-    }
-    return NO_ERROR;
-}
-
-status_t AudioPolicyService::loadPreProcessorConfig(const char *path)
-{
-    cnode *root;
-    char *data;
-
-    data = (char *)load_file(path, NULL);
-    if (data == NULL) {
-        return -ENODEV;
-    }
-    root = config_node("", "");
-    config_load(root, data);
-
-    Vector <EffectDesc *> effects;
-    loadEffects(root, effects);
-    loadInputSources(root, effects);
-
-    // delete effects to fix memory leak.
-    // as effects is local var and valgrind would treat this as memory leak
-    // and although it only did in mediaserver init, but free it in case mediaserver reboot
-    size_t i;
-    for (i = 0; i < effects.size(); i++) {
-      delete effects[i];
-    }
-
-    config_free(root);
-    free(root);
-    free(data);
-
-    return NO_ERROR;
-}
-
 extern "C" {
 audio_module_handle_t aps_load_hw_module(void *service __unused,
                                              const char *name);
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
old mode 100644
new mode 100755
index 66d9cad..380fd5e
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -31,8 +31,10 @@
 #include <media/ToneGenerator.h>
 #include <media/AudioEffect.h>
 #include <hardware_legacy/AudioPolicyInterface.h>
+#include "AudioPolicyEffects.h"
 #include "AudioPolicyManager.h"
 
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -70,6 +72,12 @@
                                         audio_output_flags_t flags =
                                                 AUDIO_OUTPUT_FLAG_NONE,
                                         const audio_offload_info_t *offloadInfo = NULL);
+    virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr,
+                                            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,
+                                            const audio_offload_info_t *offloadInfo = NULL);
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
                                  int session = 0);
@@ -331,60 +339,6 @@
         wp<AudioPolicyService> mService;
     };
 
-    class EffectDesc {
-    public:
-        EffectDesc(const char *name, const effect_uuid_t& uuid) :
-                        mName(strdup(name)),
-                        mUuid(uuid) { }
-        EffectDesc(const EffectDesc& orig) :
-                        mName(strdup(orig.mName)),
-                        mUuid(orig.mUuid) {
-                            // deep copy mParams
-                            for (size_t k = 0; k < orig.mParams.size(); k++) {
-                                effect_param_t *origParam = orig.mParams[k];
-                                // psize and vsize are rounded up to an int boundary for allocation
-                                size_t origSize = sizeof(effect_param_t) +
-                                                  ((origParam->psize + 3) & ~3) +
-                                                  ((origParam->vsize + 3) & ~3);
-                                effect_param_t *dupParam = (effect_param_t *) malloc(origSize);
-                                memcpy(dupParam, origParam, origSize);
-                                // This works because the param buffer allocation is also done by
-                                // multiples of 4 bytes originally. In theory we should memcpy only
-                                // the actual param size, that is without rounding vsize.
-                                mParams.add(dupParam);
-                            }
-                        }
-        /*virtual*/ ~EffectDesc() {
-            free(mName);
-            for (size_t k = 0; k < mParams.size(); k++) {
-                free(mParams[k]);
-            }
-        }
-        char *mName;
-        effect_uuid_t mUuid;
-        Vector <effect_param_t *> mParams;
-    };
-
-    class InputSourceDesc {
-    public:
-        InputSourceDesc() {}
-        /*virtual*/ ~InputSourceDesc() {
-            for (size_t j = 0; j < mEffects.size(); j++) {
-                delete mEffects[j];
-            }
-        }
-        Vector <EffectDesc *> mEffects;
-    };
-
-
-    class InputDesc {
-    public:
-        InputDesc(int session) : mSessionId(session) {}
-        /*virtual*/ ~InputDesc() {}
-        const int mSessionId;
-        Vector< sp<AudioEffect> >mEffects;
-    };
-
     class AudioPolicyClient : public AudioPolicyClientInterface
     {
      public:
@@ -508,26 +462,6 @@
         const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
     };
 
-    static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
-
-    void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled);
-    status_t loadPreProcessorConfig(const char *path);
-    status_t loadEffects(cnode *root, Vector <EffectDesc *>& effects);
-    EffectDesc *loadEffect(cnode *root);
-    status_t loadInputSources(cnode *root, const Vector <EffectDesc *>& effects);
-    audio_source_t inputSourceNameToEnum(const char *name);
-    InputSourceDesc *loadInputSource(cnode *root, const Vector <EffectDesc *>& effects);
-    void loadEffectParameters(cnode *root, Vector <effect_param_t *>& params);
-    effect_param_t *loadEffectParameter(cnode *root);
-    size_t readParamValue(cnode *node,
-                          char *param,
-                          size_t *curSize,
-                          size_t *totSize);
-    size_t growParamSize(char *param,
-                         size_t size,
-                         size_t *curSize,
-                         size_t *totSize);
-
     // Internal dump utilities.
     status_t dumpPermissionDenial(int fd);
 
@@ -542,10 +476,10 @@
     AudioPolicyInterface *mAudioPolicyManager;
     AudioPolicyClient *mAudioPolicyClient;
 
-    KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
-    KeyedVector< audio_io_handle_t, InputDesc* > mInputs;
-
     DefaultKeyedVector< uid_t, sp<NotificationClient> >    mNotificationClients;
+
+    // Manage all effects configured in audio_effects.conf
+    sp<AudioPolicyEffects> mAudioPolicyEffects;
 };
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 73eccbf..648e82c 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -659,7 +659,8 @@
                                       int clientUid,
                                       int callingPid,
                                       /*out*/
-                                      sp<Client>& client) {
+                                      sp<Client>& client,
+                                      int halVersion) {
 
     int facing = -1;
     int deviceVersion = getDeviceVersion(cameraId, &facing);
@@ -672,28 +673,47 @@
                      cameraId);
     }
 
-    switch(deviceVersion) {
-      case CAMERA_DEVICE_API_VERSION_1_0:
-        client = new CameraClient(this, cameraClient,
-                clientPackageName, cameraId,
-                facing, callingPid, clientUid, getpid());
-        break;
-      case CAMERA_DEVICE_API_VERSION_2_0:
-      case CAMERA_DEVICE_API_VERSION_2_1:
-      case CAMERA_DEVICE_API_VERSION_3_0:
-      case CAMERA_DEVICE_API_VERSION_3_1:
-      case CAMERA_DEVICE_API_VERSION_3_2:
-        client = new Camera2Client(this, cameraClient,
-                clientPackageName, cameraId,
-                facing, callingPid, clientUid, getpid(),
-                deviceVersion);
-        break;
-      case -1:
-        ALOGE("Invalid camera id %d", cameraId);
-        return BAD_VALUE;
-      default:
-        ALOGE("Unknown camera device HAL version: %d", deviceVersion);
-        return INVALID_OPERATION;
+    if (halVersion < 0 || halVersion == deviceVersion) {
+        // Default path: HAL version is unspecified by caller, create CameraClient
+        // based on device version reported by the HAL.
+        switch(deviceVersion) {
+          case CAMERA_DEVICE_API_VERSION_1_0:
+            client = new CameraClient(this, cameraClient,
+                    clientPackageName, cameraId,
+                    facing, callingPid, clientUid, getpid());
+            break;
+          case CAMERA_DEVICE_API_VERSION_2_0:
+          case CAMERA_DEVICE_API_VERSION_2_1:
+          case CAMERA_DEVICE_API_VERSION_3_0:
+          case CAMERA_DEVICE_API_VERSION_3_1:
+          case CAMERA_DEVICE_API_VERSION_3_2:
+            client = new Camera2Client(this, cameraClient,
+                    clientPackageName, cameraId,
+                    facing, callingPid, clientUid, getpid());
+            break;
+          case -1:
+            ALOGE("Invalid camera id %d", cameraId);
+            return BAD_VALUE;
+          default:
+            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+            return INVALID_OPERATION;
+        }
+    } else {
+        // A particular HAL version is requested by caller. Create CameraClient
+        // based on the requested HAL version.
+        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
+            halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
+            // Only support higher HAL version device opened as HAL1.0 device.
+            client = new CameraClient(this, cameraClient,
+                    clientPackageName, cameraId,
+                    facing, callingPid, clientUid, getpid());
+        } else {
+            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
+            ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
+                    " opened as HAL %x device", halVersion, deviceVersion,
+                    CAMERA_DEVICE_API_VERSION_1_0);
+            return INVALID_OPERATION;
+        }
     }
 
     status_t status = connectFinishUnsafe(client, client->getRemote());
@@ -762,6 +782,70 @@
     return OK;
 }
 
+status_t CameraService::connectLegacy(
+        const sp<ICameraClient>& cameraClient,
+        int cameraId, int halVersion,
+        const String16& clientPackageName,
+        int clientUid,
+        /*out*/
+        sp<ICamera>& device) {
+
+    if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
+            mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_3) {
+        /*
+         * Either the HAL version is unspecified in which case this just creates
+         * a camera client selected by the latest device version, or
+         * it's a particular version in which case the HAL must supported
+         * the open_legacy call
+         */
+        ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!",
+                __FUNCTION__, mModule->common.module_api_version);
+        return INVALID_OPERATION;
+    }
+
+    String8 clientName8(clientPackageName);
+    int callingPid = getCallingPid();
+
+    LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid,
+            clientName8.string(), cameraId);
+
+    status_t status = validateConnect(cameraId, /*inout*/clientUid);
+    if (status != OK) {
+        return status;
+    }
+
+    sp<Client> client;
+    {
+        Mutex::Autolock lock(mServiceLock);
+        sp<BasicClient> clientTmp;
+        if (!canConnectUnsafe(cameraId, clientPackageName,
+                              cameraClient->asBinder(),
+                              /*out*/clientTmp)) {
+            return -EBUSY;
+        } else if (client.get() != NULL) {
+            device = static_cast<Client*>(clientTmp.get());
+            return OK;
+        }
+
+        status = connectHelperLocked(cameraClient,
+                                     cameraId,
+                                     clientPackageName,
+                                     clientUid,
+                                     callingPid,
+                                     client,
+                                     halVersion);
+        if (status != OK) {
+            return status;
+        }
+
+    }
+    // important: release the mutex here so the client can call back
+    //    into the service from its destructor (can be at the end of the call)
+
+    device = client;
+    return OK;
+}
+
 status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
                                             const sp<IBinder>& remoteCallback) {
     status_t status = client->initialize(mModule);
@@ -1196,6 +1280,7 @@
         case BnCameraService::CONNECT:
         case BnCameraService::CONNECT_PRO:
         case BnCameraService::CONNECT_DEVICE:
+        case BnCameraService::CONNECT_LEGACY:
             const int pid = getCallingPid();
             const int self_pid = getpid();
             if (pid != self_pid) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index b2b65b8..28590eb 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -83,6 +83,11 @@
             /*out*/
             sp<ICamera>& device);
 
+    virtual status_t connectLegacy(const sp<ICameraClient>& cameraClient, int cameraId,
+            int halVersion, const String16& clientPackageName, int clientUid,
+            /*out*/
+            sp<ICamera>& device);
+
     virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb,
             int cameraId, const String16& clientPackageName, int clientUid,
             /*out*/
@@ -450,7 +455,8 @@
                                       int clientUid,
                                       int callingPid,
                                       /*out*/
-                                      sp<Client>& client);
+                                      sp<Client>& client,
+                                      int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 0447979..1642896 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -53,12 +53,10 @@
         int cameraFacing,
         int clientPid,
         uid_t clientUid,
-        int servicePid,
-        int deviceVersion):
+        int servicePid):
         Camera2ClientBase(cameraService, cameraClient, clientPackageName,
                 cameraId, cameraFacing, clientPid, clientUid, servicePid),
-        mParameters(cameraId, cameraFacing),
-        mDeviceVersion(deviceVersion)
+        mParameters(cameraId, cameraFacing)
 {
     ATRACE_CALL();
 
@@ -80,7 +78,7 @@
     {
         SharedParameters::Lock l(mParameters);
 
-        res = l.mParameters.initialize(&(mDevice->info()));
+        res = l.mParameters.initialize(&(mDevice->info()), mDeviceVersion);
         if (res != OK) {
             ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
                     __FUNCTION__, mCameraId, strerror(-res), res);
@@ -755,6 +753,7 @@
     // ever take a picture.
     // TODO: Find a better compromise, though this likely would involve HAL
     // changes.
+    int lastJpegStreamId = mJpegProcessor->getStreamId();
     res = updateProcessorStream(mJpegProcessor, params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Can't pre-configure still image "
@@ -762,6 +761,7 @@
                 __FUNCTION__, mCameraId, strerror(-res), res);
         return res;
     }
+    bool jpegStreamChanged = mJpegProcessor->getStreamId() != lastJpegStreamId;
 
     Vector<int32_t> outputStreams;
     bool callbacksEnabled = (params.previewCallbackFlags &
@@ -817,6 +817,12 @@
                     __FUNCTION__, mCameraId, strerror(-res), res);
             return res;
         }
+
+        if (jpegStreamChanged) {
+            ALOGV("%s: Camera %d: Clear ZSL buffer queue when Jpeg size is changed",
+                    __FUNCTION__, mCameraId);
+            mZslProcessor->clearZslQueue();
+        }
         outputStreams.push(getZslStreamId());
     } else {
         mZslProcessor->deleteStream();
@@ -1270,6 +1276,7 @@
 
         ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
 
+        int lastJpegStreamId = mJpegProcessor->getStreamId();
         res = updateProcessorStream(mJpegProcessor, l.mParameters);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
@@ -1277,6 +1284,14 @@
             return res;
         }
         takePictureCounter = ++l.mParameters.takePictureCounter;
+
+        // Clear ZSL buffer queue when Jpeg size is changed.
+        bool jpegStreamChanged = mJpegProcessor->getStreamId() != lastJpegStreamId;
+        if (l.mParameters.zslMode && jpegStreamChanged) {
+            ALOGV("%s: Camera %d: Clear ZSL buffer queue when Jpeg size is changed",
+                    __FUNCTION__, mCameraId);
+            mZslProcessor->clearZslQueue();
+        }
     }
 
     ATRACE_ASYNC_BEGIN(kTakepictureLabel, takePictureCounter);
@@ -1659,8 +1674,8 @@
 }
 
 status_t Camera2Client::registerFrameListener(int32_t minId, int32_t maxId,
-        wp<camera2::FrameProcessor::FilteredListener> listener) {
-    return mFrameProcessor->registerListener(minId, maxId, listener);
+        wp<camera2::FrameProcessor::FilteredListener> listener, bool sendPartials) {
+    return mFrameProcessor->registerListener(minId, maxId, listener, sendPartials);
 }
 
 status_t Camera2Client::removeFrameListener(int32_t minId, int32_t maxId,
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index fe0bf74..5ce757a 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -89,8 +89,7 @@
             int cameraFacing,
             int clientPid,
             uid_t clientUid,
-            int servicePid,
-            int deviceVersion);
+            int servicePid);
 
     virtual ~Camera2Client();
 
@@ -118,7 +117,8 @@
     int getZslStreamId() const;
 
     status_t registerFrameListener(int32_t minId, int32_t maxId,
-            wp<camera2::FrameProcessor::FilteredListener> listener);
+            wp<camera2::FrameProcessor::FilteredListener> listener,
+            bool sendPartials = true);
     status_t removeFrameListener(int32_t minId, int32_t maxId,
             wp<camera2::FrameProcessor::FilteredListener> listener);
 
@@ -170,7 +170,6 @@
 
     void     setPreviewCallbackFlagL(Parameters &params, int flag);
     status_t updateRequests(Parameters &params);
-    int mDeviceVersion;
 
     // Used with stream IDs
     static const int NO_STREAM = -1;
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 30b7bb8..517226d 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -79,7 +79,7 @@
         ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
         mHardware.clear();
-        return NO_INIT;
+        return res;
     }
 
     mHardware->setCallbacks(notifyCallback,
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index 8268f65..cb9aca6 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -350,8 +350,10 @@
         return DONE;
     }
 
+    // We don't want to get partial results for ZSL capture.
     client->registerFrameListener(mCaptureId, mCaptureId + 1,
-            this);
+            this,
+            /*sendPartials*/false);
 
     // TODO: Actually select the right thing here.
     res = processor->pushToReprocess(mCaptureId);
@@ -393,8 +395,14 @@
 
     bool isAeConverged = false;
     // Get the onFrameAvailable callback when the requestID == mCaptureId
+    // We don't want to get partial results for normal capture, as we need
+    // Get ANDROID_SENSOR_TIMESTAMP from the capture result, but partial
+    // result doesn't have to have this metadata available.
+    // TODO: Update to use the HALv3 shutter notification for remove the
+    // need for this listener and make it faster. see bug 12530628.
     client->registerFrameListener(mCaptureId, mCaptureId + 1,
-            this);
+            this,
+            /*sendPartials*/false);
 
     {
         Mutex::Autolock l(mInputMutex);
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index 69bea24..3de5d90 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -78,7 +78,7 @@
     }
 
     if (mSynthesize3ANotify) {
-        process3aState(frame.mMetadata, client);
+        process3aState(frame, client);
     }
 
     return FrameProcessorBase::processSingleFrame(frame, device);
@@ -212,14 +212,15 @@
     return OK;
 }
 
-status_t FrameProcessor::process3aState(const CameraMetadata &frame,
+status_t FrameProcessor::process3aState(const CaptureResult &frame,
         const sp<Camera2Client> &client) {
 
     ATRACE_CALL();
+    const CameraMetadata &metadata = frame.mMetadata;
     camera_metadata_ro_entry_t entry;
     int cameraId = client->getCameraId();
 
-    entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
+    entry = metadata.find(ANDROID_REQUEST_FRAME_COUNT);
     int32_t frameNumber = entry.data.i32[0];
 
     // Don't send 3A notifications for the same frame number twice
@@ -238,26 +239,31 @@
 
     // TODO: Also use AE mode, AE trigger ID
 
-    gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_MODE,
+    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AF_MODE,
             &new3aState.afMode, frameNumber, cameraId);
 
-    gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_MODE,
+    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AWB_MODE,
             &new3aState.awbMode, frameNumber, cameraId);
 
-    gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AE_STATE,
+    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AE_STATE,
             &new3aState.aeState, frameNumber, cameraId);
 
-    gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_STATE,
+    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AF_STATE,
             &new3aState.afState, frameNumber, cameraId);
 
-    gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_STATE,
+    gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AWB_STATE,
             &new3aState.awbState, frameNumber, cameraId);
 
-    gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AF_TRIGGER_ID,
-            &new3aState.afTriggerId, frameNumber, cameraId);
+    if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
+        new3aState.afTriggerId = frame.mResultExtras.afTriggerId;
+        new3aState.aeTriggerId = frame.mResultExtras.precaptureTriggerId;
+    } else {
+        gotAllStates &= get3aResult<int32_t>(metadata, ANDROID_CONTROL_AF_TRIGGER_ID,
+                 &new3aState.afTriggerId, frameNumber, cameraId);
 
-    gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AE_PRECAPTURE_ID,
-            &new3aState.aeTriggerId, frameNumber, cameraId);
+        gotAllStates &= get3aResult<int32_t>(metadata, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+                 &new3aState.aeTriggerId, frameNumber, cameraId);
+    }
 
     if (!gotAllStates) return BAD_VALUE;
 
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
index 514bd1a..4afca50 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
@@ -58,7 +58,7 @@
             const sp<Camera2Client> &client);
 
     // Send 3A state change notifications to client based on frame metadata
-    status_t process3aState(const CameraMetadata &frame,
+    status_t process3aState(const CaptureResult &frame,
             const sp<Camera2Client> &client);
 
     // Helper for process3aState
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 51b1980..6459300 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -29,6 +29,7 @@
 
 #include "Parameters.h"
 #include "system/camera.h"
+#include "hardware/camera_common.h"
 #include <media/MediaProfiles.h>
 #include <media/mediarecorder.h>
 
@@ -45,7 +46,7 @@
 Parameters::~Parameters() {
 }
 
-status_t Parameters::initialize(const CameraMetadata *info) {
+status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) {
     status_t res;
 
     if (info->entryCount() == 0) {
@@ -53,6 +54,7 @@
         return BAD_VALUE;
     }
     Parameters::info = info;
+    mDeviceVersion = deviceVersion;
 
     res = buildFastInfo();
     if (res != OK) return res;
@@ -140,16 +142,14 @@
     previewTransform = degToTransform(0,
             cameraFacing == CAMERA_FACING_FRONT);
 
-    camera_metadata_ro_entry_t availableFormats =
-        staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
-
     {
         String8 supportedPreviewFormats;
+        SortedVector<int32_t> outputFormats = getAvailableOutputFormats();
         bool addComma = false;
-        for (size_t i=0; i < availableFormats.count; i++) {
+        for (size_t i=0; i < outputFormats.size(); i++) {
             if (addComma) supportedPreviewFormats += ",";
             addComma = true;
-            switch (availableFormats.data.i32[i]) {
+            switch (outputFormats[i]) {
             case HAL_PIXEL_FORMAT_YCbCr_422_SP:
                 supportedPreviewFormats +=
                     CameraParameters::PIXEL_FORMAT_YUV422SP;
@@ -191,7 +191,7 @@
 
             default:
                 ALOGW("%s: Camera %d: Unknown preview format: %x",
-                        __FUNCTION__, cameraId, availableFormats.data.i32[i]);
+                        __FUNCTION__, cameraId, outputFormats[i]);
                 addComma = false;
                 break;
             }
@@ -239,24 +239,23 @@
                 supportedPreviewFrameRates);
     }
 
-    camera_metadata_ro_entry_t availableJpegSizes =
-        staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, 2);
-    if (!availableJpegSizes.count) return NO_INIT;
+    Vector<Size> availableJpegSizes = getAvailableJpegSizes();
+    if (!availableJpegSizes.size()) return NO_INIT;
 
     // TODO: Pick maximum
-    pictureWidth = availableJpegSizes.data.i32[0];
-    pictureHeight = availableJpegSizes.data.i32[1];
+    pictureWidth = availableJpegSizes[0].width;
+    pictureHeight = availableJpegSizes[0].height;
 
     params.setPictureSize(pictureWidth,
             pictureHeight);
 
     {
         String8 supportedPictureSizes;
-        for (size_t i=0; i < availableJpegSizes.count; i += 2) {
+        for (size_t i=0; i < availableJpegSizes.size(); i++) {
             if (i != 0) supportedPictureSizes += ",";
             supportedPictureSizes += String8::format("%dx%d",
-                    availableJpegSizes.data.i32[i],
-                    availableJpegSizes.data.i32[i+1]);
+                    availableJpegSizes[i].width,
+                    availableJpegSizes[i].height);
         }
         params.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES,
                 supportedPictureSizes);
@@ -952,9 +951,8 @@
         staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
     if (!availableFocalLengths.count) return NO_INIT;
 
-    camera_metadata_ro_entry_t availableFormats =
-        staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
-    if (!availableFormats.count) return NO_INIT;
+    SortedVector<int32_t> availableFormats = getAvailableOutputFormats();
+    if (!availableFormats.size()) return NO_INIT;
 
 
     if (sceneModeOverrides.count > 0) {
@@ -1038,8 +1036,8 @@
 
     // Check if the HAL supports HAL_PIXEL_FORMAT_YCbCr_420_888
     fastInfo.useFlexibleYuv = false;
-    for (size_t i = 0; i < availableFormats.count; i++) {
-        if (availableFormats.data.i32[i] == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+    for (size_t i = 0; i < availableFormats.size(); i++) {
+        if (availableFormats[i] == HAL_PIXEL_FORMAT_YCbCr_420_888) {
             fastInfo.useFlexibleYuv = true;
             break;
         }
@@ -1198,8 +1196,7 @@
                     "is active!", __FUNCTION__);
             return BAD_VALUE;
         }
-        camera_metadata_ro_entry_t availableFormats =
-            staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
+        SortedVector<int32_t> availableFormats = getAvailableOutputFormats();
         // If using flexible YUV, always support NV21/YV12. Otherwise, check
         // HAL's list.
         if (! (fastInfo.useFlexibleYuv &&
@@ -1208,11 +1205,10 @@
                  validatedParams.previewFormat ==
                         HAL_PIXEL_FORMAT_YV12) ) ) {
             // Not using flexible YUV format, so check explicitly
-            for (i = 0; i < availableFormats.count; i++) {
-                if (availableFormats.data.i32[i] ==
-                        validatedParams.previewFormat) break;
+            for (i = 0; i < availableFormats.size(); i++) {
+                if (availableFormats[i] == validatedParams.previewFormat) break;
             }
-            if (i == availableFormats.count) {
+            if (i == availableFormats.size()) {
                 ALOGE("%s: Requested preview format %s (0x%x) is not supported",
                         __FUNCTION__, newParams.getPreviewFormat(),
                         validatedParams.previewFormat);
@@ -1302,15 +1298,14 @@
             &validatedParams.pictureHeight);
     if (validatedParams.pictureWidth == pictureWidth ||
             validatedParams.pictureHeight == pictureHeight) {
-        camera_metadata_ro_entry_t availablePictureSizes =
-            staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
-        for (i = 0; i < availablePictureSizes.count; i+=2) {
-            if ((availablePictureSizes.data.i32[i] ==
+        Vector<Size> availablePictureSizes = getAvailableJpegSizes();
+        for (i = 0; i < availablePictureSizes.size(); i++) {
+            if ((availablePictureSizes[i].width ==
                     validatedParams.pictureWidth) &&
-                (availablePictureSizes.data.i32[i+1] ==
+                (availablePictureSizes[i].height ==
                     validatedParams.pictureHeight)) break;
         }
-        if (i == availablePictureSizes.count) {
+        if (i == availablePictureSizes.size()) {
             ALOGE("%s: Requested picture size %d x %d is not supported",
                     __FUNCTION__, validatedParams.pictureWidth,
                     validatedParams.pictureHeight);
@@ -2527,22 +2522,37 @@
         ALOGE("%s: Input size is null", __FUNCTION__);
         return BAD_VALUE;
     }
+    sizes->clear();
 
-    const size_t SIZE_COUNT = sizeof(Size) / sizeof(int);
-    camera_metadata_ro_entry_t availableProcessedSizes =
-        staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT);
-    if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE;
-
-    Size filteredSize;
-    for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) {
-        filteredSize.width = availableProcessedSizes.data.i32[i];
-        filteredSize.height = availableProcessedSizes.data.i32[i+1];
-            // Need skip the preview sizes that are too large.
-            if (filteredSize.width <= limit.width &&
-                    filteredSize.height <= limit.height) {
-                sizes->push(filteredSize);
+    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+        Vector<StreamConfiguration> scs = getStreamConfigurations();
+        for (size_t i=0; i < scs.size(); i++) {
+            const StreamConfiguration &sc = scs[i];
+            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+                    sc.width <= limit.width && sc.height <= limit.height) {
+                Size sz = {sc.width, sc.height};
+                sizes->push(sz);
             }
+        }
+    } else {
+        const size_t SIZE_COUNT = sizeof(Size) / sizeof(int);
+        camera_metadata_ro_entry_t availableProcessedSizes =
+            staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT);
+        if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE;
+
+        Size filteredSize;
+        for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) {
+            filteredSize.width = availableProcessedSizes.data.i32[i];
+            filteredSize.height = availableProcessedSizes.data.i32[i+1];
+                // Need skip the preview sizes that are too large.
+                if (filteredSize.width <= limit.width &&
+                        filteredSize.height <= limit.height) {
+                    sizes->push(filteredSize);
+                }
+        }
     }
+
     if (sizes->isEmpty()) {
         ALOGE("generated preview size list is empty!!");
         return BAD_VALUE;
@@ -2576,6 +2586,78 @@
     return maxSize;
 }
 
+Vector<Parameters::StreamConfiguration> Parameters::getStreamConfigurations() {
+    const int STREAM_CONFIGURATION_SIZE = 4;
+    const int STREAM_FORMAT_OFFSET = 0;
+    const int STREAM_WIDTH_OFFSET = 1;
+    const int STREAM_HEIGHT_OFFSET = 2;
+    const int STREAM_IS_INPUT_OFFSET = 3;
+    Vector<StreamConfiguration> scs;
+    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+        ALOGE("StreamConfiguration is only valid after device HAL 3.2!");
+        return scs;
+    }
+
+    camera_metadata_ro_entry_t availableStreamConfigs =
+                staticInfo(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+    for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
+        int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+        int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+        int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+        int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+        StreamConfiguration sc = {format, width, height, isInput};
+        scs.add(sc);
+    }
+    return scs;
+}
+
+SortedVector<int32_t> Parameters::getAvailableOutputFormats() {
+    SortedVector<int32_t> outputFormats; // Non-duplicated output formats
+    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+        Vector<StreamConfiguration> scs = getStreamConfigurations();
+        for (size_t i=0; i < scs.size(); i++) {
+            const StreamConfiguration &sc = scs[i];
+            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
+                outputFormats.add(sc.format);
+            }
+        }
+    } else {
+        camera_metadata_ro_entry_t availableFormats = staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
+        for (size_t i=0; i < availableFormats.count; i++) {
+            outputFormats.add(availableFormats.data.i32[i]);
+        }
+    }
+    return outputFormats;
+}
+
+Vector<Parameters::Size> Parameters::getAvailableJpegSizes() {
+    Vector<Parameters::Size> jpegSizes;
+    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+        Vector<StreamConfiguration> scs = getStreamConfigurations();
+        for (size_t i=0; i < scs.size(); i++) {
+            const StreamConfiguration &sc = scs[i];
+            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    sc.format == HAL_PIXEL_FORMAT_BLOB) {
+                Size sz = {sc.width, sc.height};
+                jpegSizes.add(sz);
+            }
+        }
+    } else {
+        const int JPEG_SIZE_ENTRY_COUNT = 2;
+        const int WIDTH_OFFSET = 0;
+        const int HEIGHT_OFFSET = 1;
+        camera_metadata_ro_entry_t availableJpegSizes =
+            staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
+        for (size_t i=0; i < availableJpegSizes.count; i+= JPEG_SIZE_ENTRY_COUNT) {
+            int width = availableJpegSizes.data.i32[i + WIDTH_OFFSET];
+            int height = availableJpegSizes.data.i32[i + HEIGHT_OFFSET];
+            Size sz = {width, height};
+            jpegSizes.add(sz);
+        }
+    }
+    return jpegSizes;
+}
+
 Parameters::CropRegion Parameters::calculateCropRegion(
                             Parameters::CropRegion::Outputs outputs) const {
 
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 28dd788..f95c69a 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -226,7 +226,7 @@
     ~Parameters();
 
     // Sets up default parameters
-    status_t initialize(const CameraMetadata *info);
+    status_t initialize(const CameraMetadata *info, int deviceVersion);
 
     // Build fast-access device static info from static info
     status_t buildFastInfo();
@@ -346,6 +346,24 @@
     status_t getFilteredSizes(Size limit, Vector<Size> *sizes);
     // Get max size (from the size array) that matches the given aspect ratio.
     Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count);
+
+    struct StreamConfiguration {
+        int32_t format;
+        int32_t width;
+        int32_t height;
+        int32_t isInput;
+    };
+    // Helper function extract available stream configuration
+    // Only valid since device HAL version 3.2
+    // returns an empty Vector if device HAL version does support it
+    Vector<StreamConfiguration> getStreamConfigurations();
+
+    // Helper function to get non-duplicated available output formats
+    SortedVector<int32_t> getAvailableOutputFormats();
+    // Helper function to get available output jpeg sizes
+    Vector<Size> getAvailableJpegSizes();
+
+    int mDeviceVersion;
 };
 
 // This class encapsulates the Parameters class so that it can only be accessed
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index 2064e2c..911f55a 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -89,8 +89,26 @@
 
     Mutex::Autolock m(mMutex);
     if (mPreviewRequest.entryCount() == 0) {
-        res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
-                &mPreviewRequest);
+        sp<Camera2Client> client = mClient.promote();
+        if (client == 0) {
+            ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId);
+            return INVALID_OPERATION;
+        }
+
+        // Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.
+        if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) {
+            if (params.zslMode && !params.recordingHint) {
+                res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
+                        &mPreviewRequest);
+            } else {
+                res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,
+                        &mPreviewRequest);
+            }
+        } else {
+            res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                    &mPreviewRequest);
+        }
+
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to create default preview request: "
                     "%s (%d)", __FUNCTION__, mId, strerror(-res), res);
@@ -430,10 +448,13 @@
 
     Mutex::Autolock m(mMutex);
 
-    // If a recording stream is being started up, free up any
-    // outstanding buffers left from the previous recording session.
-    // There should never be any, so if there are, warn about it.
-    if (isStreamActive(outputStreams, mRecordingStreamId)) {
+    // If a recording stream is being started up and no recording
+    // stream is active yet, free up any outstanding buffers left
+    // from the previous recording session. There should never be
+    // any, so if there are, warn about it.
+    bool isRecordingStreamIdle = !isStreamActive(mActiveStreamIds, mRecordingStreamId);
+    bool startRecordingStream = isStreamActive(outputStreams, mRecordingStreamId);
+    if (startRecordingStream && isRecordingStreamIdle) {
         releaseAllRecordingFramesLocked();
     }
 
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 2a2a5af..10463c1 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -202,7 +202,8 @@
     }
     client->registerFrameListener(Camera2Client::kPreviewRequestIdStart,
             Camera2Client::kPreviewRequestIdEnd,
-            this);
+            this,
+            /*sendPartials*/false);
 
     return OK;
 }
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 1dcb718..79f75a5 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -52,8 +52,31 @@
         mFrameListHead(0),
         mZslQueueHead(0),
         mZslQueueTail(0) {
-    mZslQueue.insertAt(0, kZslBufferDepth);
-    mFrameList.insertAt(0, kFrameListDepth);
+    // Initialize buffer queue and frame list based on pipeline max depth.
+    size_t pipelineMaxDepth = kDefaultMaxPipelineDepth;
+    if (client != 0) {
+        sp<Camera3Device> device =
+        static_cast<Camera3Device*>(client->getCameraDevice().get());
+        if (device != 0) {
+            camera_metadata_ro_entry_t entry =
+                device->info().find(ANDROID_REQUEST_PIPELINE_MAX_DEPTH);
+            if (entry.count == 1) {
+                pipelineMaxDepth = entry.data.u8[0];
+            } else {
+                ALOGW("%s: Unable to find the android.request.pipelineMaxDepth,"
+                        " use default pipeline max depth %zu", __FUNCTION__,
+                        kDefaultMaxPipelineDepth);
+            }
+        }
+    }
+
+    ALOGV("%s: Initialize buffer queue and frame list depth based on max pipeline depth (%d)",
+          __FUNCTION__, pipelineMaxDepth);
+    mBufferQueueDepth = pipelineMaxDepth + 1;
+    mFrameListDepth = pipelineMaxDepth + 1;
+
+    mZslQueue.insertAt(0, mBufferQueueDepth);
+    mFrameList.insertAt(0, mFrameListDepth);
     sp<CaptureSequencer> captureSequencer = mSequencer.promote();
     if (captureSequencer != 0) captureSequencer->setZslProcessor(this);
 }
@@ -70,13 +93,25 @@
     camera_metadata_ro_entry_t entry;
     entry = result.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
     nsecs_t timestamp = entry.data.i64[0];
+    if (entry.count == 0) {
+        ALOGE("%s: metadata doesn't have timestamp, skip this result");
+        return;
+    }
     (void)timestamp;
-    ALOGVV("Got preview metadata for timestamp %" PRId64, timestamp);
+
+    entry = result.mMetadata.find(ANDROID_REQUEST_FRAME_COUNT);
+    if (entry.count == 0) {
+        ALOGE("%s: metadata doesn't have frame number, skip this result");
+        return;
+    }
+    int32_t frameNumber = entry.data.i32[0];
+
+    ALOGVV("Got preview metadata for frame %d with timestamp %" PRId64, frameNumber, timestamp);
 
     if (mState != RUNNING) return;
 
     mFrameList.editItemAt(mFrameListHead) = result.mMetadata;
-    mFrameListHead = (mFrameListHead + 1) % kFrameListDepth;
+    mFrameListHead = (mFrameListHead + 1) % mFrameListDepth;
 }
 
 status_t ZslProcessor3::updateStream(const Parameters &params) {
@@ -136,7 +171,7 @@
         // Note that format specified internally in Camera3ZslStream
         res = device->createZslStream(
                 params.fastInfo.arrayWidth, params.fastInfo.arrayHeight,
-                kZslBufferDepth,
+                mBufferQueueDepth,
                 &mZslStreamId,
                 &mZslStream);
         if (res != OK) {
@@ -145,10 +180,15 @@
                     strerror(-res), res);
             return res;
         }
+
+        // Only add the camera3 buffer listener when the stream is created.
+        mZslStream->addBufferListener(this);
     }
+
     client->registerFrameListener(Camera2Client::kPreviewRequestIdStart,
             Camera2Client::kPreviewRequestIdEnd,
-            this);
+            this,
+            /*sendPartials*/false);
 
     return OK;
 }
@@ -250,18 +290,45 @@
         uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
         res = request.update(ANDROID_REQUEST_TYPE,
                 &requestType, 1);
+        if (res != OK) {
+            ALOGE("%s: Unable to update request type",
+                  __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+
         int32_t inputStreams[1] =
                 { mZslStreamId };
-        if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
+        res = request.update(ANDROID_REQUEST_INPUT_STREAMS,
                 inputStreams, 1);
+        if (res != OK) {
+            ALOGE("%s: Unable to update request input streams",
+                  __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+
+        uint8_t captureIntent =
+                static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
+        res = request.update(ANDROID_CONTROL_CAPTURE_INTENT,
+                &captureIntent, 1);
+        if (res != OK ) {
+            ALOGE("%s: Unable to update request capture intent",
+                  __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+
         // TODO: Shouldn't we also update the latest preview frame?
         int32_t outputStreams[1] =
                 { client->getCaptureStreamId() };
-        if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
+        res = request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
                 outputStreams, 1);
+        if (res != OK) {
+            ALOGE("%s: Unable to update request output streams",
+                  __FUNCTION__);
+            return INVALID_OPERATION;
+        }
+
         res = request.update(ANDROID_REQUEST_ID,
                 &requestId, 1);
-
         if (res != OK ) {
             ALOGE("%s: Unable to update frame to a reprocess request",
                   __FUNCTION__);
@@ -276,15 +343,6 @@
             return INVALID_OPERATION;
         }
 
-        // Flush device to clear out all in-flight requests pending in HAL.
-        res = client->getCameraDevice()->flush();
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Failed to flush device: "
-                "%s (%d)",
-                __FUNCTION__, client->getCameraId(), strerror(-res), res);
-            return res;
-        }
-
         // Update JPEG settings
         {
             SharedParameters::Lock l(client->getParameters());
@@ -322,11 +380,19 @@
 
 status_t ZslProcessor3::clearZslQueueLocked() {
     if (mZslStream != 0) {
+        // clear result metadata list first.
+        clearZslResultQueueLocked();
         return mZslStream->clearInputRingBuffer();
     }
     return OK;
 }
 
+void ZslProcessor3::clearZslResultQueueLocked() {
+    mFrameList.clear();
+    mFrameListHead = 0;
+    mFrameList.insertAt(0, mFrameListDepth);
+}
+
 void ZslProcessor3::dump(int fd, const Vector<String16>& /*args*/) const {
     Mutex::Autolock l(mInputMutex);
     if (!mLatestCapturedRequest.isEmpty()) {
@@ -480,11 +546,17 @@
     // We need to guarantee that if we do two back-to-back captures,
     // the second won't use a buffer that's older/the same as the first, which
     // is theoretically possible if we don't clear out the queue and the
-    // selection criteria is something like 'newest'. Clearing out the queue
-    // on a completed capture ensures we'll only use new data.
+    // selection criteria is something like 'newest'. Clearing out the result
+    // metadata queue on a completed capture ensures we'll only use new timestamp.
+    // Calling clearZslQueueLocked is a guaranteed deadlock because this callback
+    // holds the Camera3Stream internal lock (mLock), and clearZslQueueLocked requires
+    // to hold the same lock.
+    // TODO: need figure out a way to clear the Zsl buffer queue properly. Right now
+    // it is safe not to do so, as back to back ZSL capture requires stop and start
+    // preview, which will flush ZSL queue automatically.
     ALOGV("%s: Memory optimization, clearing ZSL queue",
           __FUNCTION__);
-    clearZslQueueLocked();
+    clearZslResultQueueLocked();
 
     // Required so we accept more ZSL requests
     mState = RUNNING;
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index 4c52a64..dfb1457 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -107,8 +107,9 @@
         CameraMetadata frame;
     };
 
-    static const size_t kZslBufferDepth = 4;
-    static const size_t kFrameListDepth = kZslBufferDepth * 2;
+    static const int32_t kDefaultMaxPipelineDepth = 4;
+    size_t mBufferQueueDepth;
+    size_t mFrameListDepth;
     Vector<CameraMetadata> mFrameList;
     size_t mFrameListHead;
 
@@ -124,6 +125,8 @@
 
     status_t clearZslQueueLocked();
 
+    void clearZslResultQueueLocked();
+
     void dumpZslQueue(int id) const;
 
     nsecs_t getCandidateTimestampLocked(size_t* metadataIdx) const;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 8154e59..de42cee 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -82,7 +82,7 @@
     mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
                                       FRAME_PROCESSOR_LISTENER_MAX_ID,
                                       /*listener*/this,
-                                      /*quirkSendPartials*/true);
+                                      /*sendPartials*/true);
 
     return OK;
 }
@@ -102,7 +102,7 @@
 status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > requests,
                                                bool streaming, int64_t* lastFrameNumber) {
     ATRACE_CALL();
-    ALOGV("%s-start of function. Request list size %d", __FUNCTION__, requests.size());
+    ALOGV("%s-start of function. Request list size %zu", __FUNCTION__, requests.size());
 
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
@@ -177,7 +177,7 @@
 
         metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
         loopCounter++; // loopCounter starts from 1
-        ALOGV("%s: Camera %d: Creating request with ID %d (%d of %d)",
+        ALOGV("%s: Camera %d: Creating request with ID %d (%d of %zu)",
               __FUNCTION__, mCameraId, requestId, loopCounter, requests.size());
 
         metadataRequestList.push_back(metadata);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 19efd30..13c9f48 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -54,7 +54,8 @@
         int servicePid):
         TClientBase(cameraService, remoteCallback, clientPackageName,
                 cameraId, cameraFacing, clientPid, clientUid, servicePid),
-        mSharedCameraCallbacks(remoteCallback)
+        mSharedCameraCallbacks(remoteCallback),
+        mDeviceVersion(cameraService->getDeviceVersion(cameraId))
 {
     ALOGI("Camera %d: Opened", cameraId);
 
@@ -280,6 +281,11 @@
 }
 
 template <typename TClientBase>
+int Camera2ClientBase<TClientBase>::getCameraDeviceVersion() const {
+    return mDeviceVersion;
+}
+
+template <typename TClientBase>
 const sp<CameraDeviceBase>& Camera2ClientBase<TClientBase>::getCameraDevice() {
     return mDevice;
 }
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 9feca93..f57d204 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -76,6 +76,7 @@
     int                   getCameraId() const;
     const sp<CameraDeviceBase>&
                           getCameraDevice();
+    int                   getCameraDeviceVersion() const;
     const sp<CameraService>&
                           getCameraService();
 
@@ -122,6 +123,7 @@
 
     /** CameraDeviceBase instance wrapping HAL2+ entry */
 
+    const int mDeviceVersion;
     sp<CameraDeviceBase>  mDevice;
 
     /** Utility members */
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 7597b10..c7bd886 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -26,6 +26,7 @@
 
 #include <camera/camera2/ICameraDeviceCallbacks.h>
 #include "hardware/camera2.h"
+#include "hardware/camera3.h"
 #include "camera/CameraMetadata.h"
 #include "camera/CaptureResult.h"
 
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index f6a971a..482f687 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -37,11 +37,23 @@
 }
 
 status_t FrameProcessorBase::registerListener(int32_t minId,
-        int32_t maxId, wp<FilteredListener> listener, bool quirkSendPartials) {
+        int32_t maxId, wp<FilteredListener> listener, bool sendPartials) {
     Mutex::Autolock l(mInputMutex);
+    List<RangeListener>::iterator item = mRangeListeners.begin();
+    while (item != mRangeListeners.end()) {
+        if (item->minId == minId &&
+                item->maxId == maxId &&
+                item->listener == listener) {
+            // already registered, just return
+            ALOGV("%s: Attempt to register the same client twice, ignoring",
+                    __FUNCTION__);
+            return OK;
+        }
+        item++;
+    }
     ALOGV("%s: Registering listener for frame id range %d - %d",
             __FUNCTION__, minId, maxId);
-    RangeListener rListener = { minId, maxId, listener, quirkSendPartials };
+    RangeListener rListener = { minId, maxId, listener, sendPartials };
     mRangeListeners.push_back(rListener);
     return OK;
 }
@@ -176,7 +188,7 @@
         List<RangeListener>::iterator item = mRangeListeners.begin();
         while (item != mRangeListeners.end()) {
             if (requestId >= item->minId && requestId < item->maxId &&
-                    (!quirkIsPartial || item->quirkSendPartials)) {
+                    (!quirkIsPartial || item->sendPartials)) {
                 sp<FilteredListener> listener = item->listener.promote();
                 if (listener == 0) {
                     item = mRangeListeners.erase(item);
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h
index 15a014e..3649c45 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.h
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.h
@@ -44,11 +44,12 @@
     };
 
     // Register a listener for a range of IDs [minId, maxId). Multiple listeners
-    // can be listening to the same range.
-    // QUIRK: sendPartials controls whether partial results will be sent.
+    // can be listening to the same range. Registering the same listener with
+    // the same range of IDs has no effect.
+    // sendPartials controls whether partial results will be sent.
     status_t registerListener(int32_t minId, int32_t maxId,
                               wp<FilteredListener> listener,
-                              bool quirkSendPartials = true);
+                              bool sendPartials = true);
     status_t removeListener(int32_t minId, int32_t maxId,
                             wp<FilteredListener> listener);
 
@@ -66,7 +67,7 @@
         int32_t minId;
         int32_t maxId;
         wp<FilteredListener> listener;
-        bool quirkSendPartials;
+        bool sendPartials;
     };
     List<RangeListener> mRangeListeners;
 
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 87b2807..2746f6f 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -92,8 +92,22 @@
     status_t initialize(hw_module_t *module)
     {
         ALOGI("Opening camera %s", mName.string());
-        int rc = module->methods->open(module, mName.string(),
-                                       (hw_device_t **)&mDevice);
+        camera_module_t *cameraModule = reinterpret_cast<camera_module_t *>(module);
+        camera_info info;
+        status_t res = cameraModule->get_camera_info(atoi(mName.string()), &info);
+        if (res != OK) return res;
+
+        int rc = OK;
+        if (module->module_api_version >= CAMERA_MODULE_API_VERSION_2_3 &&
+            info.device_version > CAMERA_DEVICE_API_VERSION_1_0) {
+            // Open higher version camera device as HAL1.0 device.
+            rc = cameraModule->open_legacy(module, mName.string(),
+                                               CAMERA_DEVICE_API_VERSION_1_0,
+                                               (hw_device_t **)&mDevice);
+        } else {
+            rc = module->methods->open(module, mName.string(),
+                                           (hw_device_t **)&mDevice);
+        }
         if (rc != OK) {
             ALOGE("Could not open camera %s: %d", mName.string(), rc);
             return rc;
@@ -611,9 +625,14 @@
     static int __set_buffers_geometry(struct preview_stream_ops* w,
                       int width, int height, int format)
     {
+        int rc;
         ANativeWindow *a = anw(w);
-        return native_window_set_buffers_geometry(a,
-                          width, height, format);
+
+        rc = native_window_set_buffers_dimensions(a, width, height);
+        if (!rc) {
+            rc = native_window_set_buffers_format(a, format);
+        }
+        return rc;
     }
 
     static int __set_crop(struct preview_stream_ops *w,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 16d6f42..6ceb9d4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -113,7 +113,6 @@
     }
 
     /** Cross-check device version */
-
     if (device->common.version < CAMERA_DEVICE_API_VERSION_3_0) {
         SET_ERR_L("Could not open camera: "
                 "Camera device should be at least %x, reports %x instead",
@@ -173,6 +172,7 @@
 
     /** Everything is good to go */
 
+    mDeviceVersion = device->common.version;
     mDeviceInfo = info.static_camera_characteristics;
     mHal3Device = device;
     mStatus = STATUS_UNCONFIGURED;
@@ -284,42 +284,74 @@
     return gotLock;
 }
 
+Camera3Device::Size Camera3Device::getMaxJpegResolution() const {
+    int32_t maxJpegWidth = 0, maxJpegHeight = 0;
+    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+        const int STREAM_CONFIGURATION_SIZE = 4;
+        const int STREAM_FORMAT_OFFSET = 0;
+        const int STREAM_WIDTH_OFFSET = 1;
+        const int STREAM_HEIGHT_OFFSET = 2;
+        const int STREAM_IS_INPUT_OFFSET = 3;
+        camera_metadata_ro_entry_t availableStreamConfigs =
+                mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+        if (availableStreamConfigs.count == 0 ||
+                availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
+            return Size(0, 0);
+        }
+
+        // Get max jpeg size (area-wise).
+        for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
+            int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+            int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+            int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+            int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+            if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT
+                    && format == HAL_PIXEL_FORMAT_BLOB &&
+                    (width * height > maxJpegWidth * maxJpegHeight)) {
+                maxJpegWidth = width;
+                maxJpegHeight = height;
+            }
+        }
+    } else {
+        camera_metadata_ro_entry availableJpegSizes =
+                mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
+        if (availableJpegSizes.count == 0 || availableJpegSizes.count % 2 != 0) {
+            return Size(0, 0);
+        }
+
+        // Get max jpeg size (area-wise).
+        for (size_t i = 0; i < availableJpegSizes.count; i += 2) {
+            if ((availableJpegSizes.data.i32[i] * availableJpegSizes.data.i32[i + 1])
+                    > (maxJpegWidth * maxJpegHeight)) {
+                maxJpegWidth = availableJpegSizes.data.i32[i];
+                maxJpegHeight = availableJpegSizes.data.i32[i + 1];
+            }
+        }
+    }
+    return Size(maxJpegWidth, maxJpegHeight);
+}
+
 ssize_t Camera3Device::getJpegBufferSize(uint32_t width, uint32_t height) const {
-    // TODO: replace below with availableStreamConfiguration for HAL3.2+.
-    camera_metadata_ro_entry availableJpegSizes =
-            mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
-    if (availableJpegSizes.count == 0 || availableJpegSizes.count % 2 != 0) {
+    // Get max jpeg size (area-wise).
+    Size maxJpegResolution = getMaxJpegResolution();
+    if (maxJpegResolution.width == 0) {
         ALOGE("%s: Camera %d: Can't find find valid available jpeg sizes in static metadata!",
                 __FUNCTION__, mId);
         return BAD_VALUE;
     }
 
-    // Get max jpeg size (area-wise).
-    int32_t maxJpegWidth = 0, maxJpegHeight = 0;
-    bool foundMax = false;
-    for (size_t i = 0; i < availableJpegSizes.count; i += 2) {
-        if ((availableJpegSizes.data.i32[i] * availableJpegSizes.data.i32[i + 1])
-                > (maxJpegWidth * maxJpegHeight)) {
-            maxJpegWidth = availableJpegSizes.data.i32[i];
-            maxJpegHeight = availableJpegSizes.data.i32[i + 1];
-            foundMax = true;
-        }
-    }
-    if (!foundMax) {
-        return BAD_VALUE;
-    }
-
     // Get max jpeg buffer size
     ssize_t maxJpegBufferSize = 0;
-    camera_metadata_ro_entry jpegMaxSize = mDeviceInfo.find(ANDROID_JPEG_MAX_SIZE);
-    if (jpegMaxSize.count == 0) {
+    camera_metadata_ro_entry jpegBufMaxSize = mDeviceInfo.find(ANDROID_JPEG_MAX_SIZE);
+    if (jpegBufMaxSize.count == 0) {
         ALOGE("%s: Camera %d: Can't find maximum JPEG size in static metadata!", __FUNCTION__, mId);
         return BAD_VALUE;
     }
-    maxJpegBufferSize = jpegMaxSize.data.i32[0];
+    maxJpegBufferSize = jpegBufMaxSize.data.i32[0];
 
     // Calculate final jpeg buffer size for the given resolution.
-    float scaleFactor = ((float) (width * height)) / (maxJpegWidth * maxJpegHeight);
+    float scaleFactor = ((float) (width * height)) /
+            (maxJpegResolution.width * maxJpegResolution.height);
     ssize_t jpegBufferSize = scaleFactor * maxJpegBufferSize;
     // Bound the buffer size to [MIN_JPEG_BUFFER_SIZE, maxJpegBufferSize].
     if (jpegBufferSize > maxJpegBufferSize) {
@@ -1156,7 +1188,7 @@
         {
             ANDROID_CONTROL_AF_TRIGGER_ID,
             static_cast<int32_t>(id)
-        },
+        }
     };
 
     return mRequestThread->queueTrigger(trigger,
@@ -1177,7 +1209,7 @@
         {
             ANDROID_CONTROL_AF_TRIGGER_ID,
             static_cast<int32_t>(id)
-        },
+        }
     };
 
     return mRequestThread->queueTrigger(trigger,
@@ -1198,7 +1230,7 @@
         {
             ANDROID_CONTROL_AE_PRECAPTURE_ID,
             static_cast<int32_t>(id)
-        },
+        }
     };
 
     return mRequestThread->queueTrigger(trigger,
@@ -1501,12 +1533,12 @@
  */
 
 status_t Camera3Device::registerInFlight(uint32_t frameNumber,
-        int32_t numBuffers, CaptureResultExtras resultExtras) {
+        int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
-    res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras));
+    res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput));
     if (res < 0) return res;
 
     return OK;
@@ -1539,8 +1571,6 @@
     uint8_t aeState;
     uint8_t afState;
     uint8_t awbState;
-    int32_t afTriggerId;
-    int32_t aeTriggerId;
 
     gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_MODE,
         &afMode, frameNumber);
@@ -1557,12 +1587,6 @@
     gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_STATE,
         &awbState, frameNumber);
 
-    gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_TRIGGER_ID,
-        &afTriggerId, frameNumber);
-
-    gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_PRECAPTURE_ID,
-        &aeTriggerId, frameNumber);
-
     if (!gotAllStates) return false;
 
     ALOGVV("%s: Camera %d: Frame %d, Request ID %d: AF mode %d, AWB mode %d, "
@@ -1571,7 +1595,7 @@
         __FUNCTION__, mId, frameNumber, resultExtras.requestId,
         afMode, awbMode,
         afState, aeState, awbState,
-        afTriggerId, aeTriggerId);
+        resultExtras.afTriggerId, resultExtras.precaptureTriggerId);
 
     // Got all states, so construct a minimal result to send
     // In addition to the above fields, this means adding in
@@ -1635,12 +1659,12 @@
     }
 
     if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AF_TRIGGER_ID,
-            &afTriggerId, frameNumber)) {
+            &resultExtras.afTriggerId, frameNumber)) {
         return false;
     }
 
     if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AE_PRECAPTURE_ID,
-            &aeTriggerId, frameNumber)) {
+            &resultExtras.precaptureTriggerId, frameNumber)) {
         return false;
     }
 
@@ -1696,7 +1720,8 @@
     status_t res;
 
     uint32_t frameNumber = result->frame_number;
-    if (result->result == NULL && result->num_output_buffers == 0) {
+    if (result->result == NULL && result->num_output_buffers == 0 &&
+            result->input_buffer == NULL) {
         SET_ERR("No result data provided by HAL for frame %d",
                 frameNumber);
         return;
@@ -1704,6 +1729,7 @@
     bool partialResultQuirk = false;
     CameraMetadata collectedQuirkResult;
     CaptureResultExtras resultExtras;
+    bool hasInputBufferInRequest = false;
 
     // Get capture timestamp and resultExtras from list of in-flight requests,
     // where it was added by the shutter notification for this frame.
@@ -1752,6 +1778,7 @@
 
         timestamp = request.captureTimestamp;
         resultExtras = request.resultExtras;
+        hasInputBufferInRequest = request.hasInputBuffer;
 
         /**
          * One of the following must happen before it's legal to call process_capture_result,
@@ -1780,8 +1807,17 @@
             request.haveResultMetadata = true;
         }
 
-        request.numBuffersLeft -= result->num_output_buffers;
-
+        uint32_t numBuffersReturned = result->num_output_buffers;
+        if (result->input_buffer != NULL) {
+            if (hasInputBufferInRequest) {
+                numBuffersReturned += 1;
+            } else {
+                ALOGW("%s: Input buffer should be NULL if there is no input"
+                        " buffer sent in the request",
+                        __FUNCTION__);
+            }
+        }
+        request.numBuffersLeft -= numBuffersReturned;
         if (request.numBuffersLeft < 0) {
             SET_ERR("Too many buffers returned for frame %d",
                     frameNumber);
@@ -1882,6 +1918,25 @@
         }
     }
 
+    if (result->input_buffer != NULL) {
+        if (hasInputBufferInRequest) {
+            Camera3Stream *stream =
+                Camera3Stream::cast(result->input_buffer->stream);
+            res = stream->returnInputBuffer(*(result->input_buffer));
+            // Note: stream may be deallocated at this point, if this buffer was the
+            // last reference to it.
+            if (res != OK) {
+                ALOGE("%s: RequestThread: Can't return input buffer for frame %d to"
+                      "  its stream:%s (%d)",  __FUNCTION__,
+                      frameNumber, strerror(-res), res);
+            }
+        } else {
+            ALOGW("%s: Input buffer should be NULL if there is no input"
+                    " buffer sent in the request, skipping input buffer return.",
+                    __FUNCTION__);
+        }
+    }
+
     // Finally, signal any waiters for new frames
 
     if (gotResult) {
@@ -2126,6 +2181,17 @@
     return OK;
 }
 
+bool Camera3Device::RequestThread::isRepeatingRequestLocked(const sp<CaptureRequest> requestIn) {
+    if (mRepeatingRequests.empty()) {
+        return false;
+    }
+    int32_t requestId = requestIn->mResultExtras.requestId;
+    const RequestList &repeatRequests = mRepeatingRequests;
+    // All repeating requests are guaranteed to have same id so only check first quest
+    const sp<CaptureRequest> firstRequest = *repeatRequests.begin();
+    return (firstRequest->mResultExtras.requestId == requestId);
+}
+
 status_t Camera3Device::RequestThread::clearRepeatingRequests(/*out*/int64_t *lastFrameNumber) {
     Mutex::Autolock l(mRequestLock);
     mRepeatingRequests.clear();
@@ -2140,6 +2206,18 @@
     Mutex::Autolock l(mRequestLock);
     ALOGV("RequestThread::%s:", __FUNCTION__);
     mRepeatingRequests.clear();
+
+    // Decrement repeating frame count for those requests never sent to device
+    // TODO: Remove this after we have proper error handling so these requests
+    // will generate an error callback. This might be the only place calling
+    // isRepeatingRequestLocked. If so, isRepeatingRequestLocked should also be removed.
+    const RequestList &requests = mRequestQueue;
+    for (RequestList::const_iterator it = requests.begin();
+            it != requests.end(); ++it) {
+        if (isRepeatingRequestLocked(*it)) {
+            mRepeatingLastFrameNumber--;
+        }
+    }
     mRequestQueue.clear();
     mTriggerMap.clear();
     if (lastFrameNumber != NULL) {
@@ -2271,6 +2349,7 @@
     }
 
     camera3_stream_buffer_t inputBuffer;
+    uint32_t totalNumBuffers = 0;
 
     // Fill in buffers
 
@@ -2283,6 +2362,7 @@
             cleanUpFailedRequest(request, nextRequest, outputBuffers);
             return true;
         }
+        totalNumBuffers += 1;
     } else {
         request.input_buffer = NULL;
     }
@@ -2301,6 +2381,7 @@
         }
         request.num_output_buffers++;
     }
+    totalNumBuffers += request.num_output_buffers;
 
     // Log request in the in-flight queue
     sp<Camera3Device> parent = mParent.promote();
@@ -2311,7 +2392,8 @@
     }
 
     res = parent->registerInFlight(request.frame_number,
-            request.num_output_buffers, nextRequest->mResultExtras);
+            totalNumBuffers, nextRequest->mResultExtras,
+            /*hasInput*/request.input_buffer != NULL);
     ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
            ", burstId = %" PRId32 ".",
             __FUNCTION__,
@@ -2367,21 +2449,6 @@
     }
     mPrevTriggers = triggerCount;
 
-    // Return input buffer back to framework
-    if (request.input_buffer != NULL) {
-        Camera3Stream *stream =
-            Camera3Stream::cast(request.input_buffer->stream);
-        res = stream->returnInputBuffer(*(request.input_buffer));
-        // Note: stream may be deallocated at this point, if this buffer was the
-        // last reference to it.
-        if (res != OK) {
-            ALOGE("%s: RequestThread: Can't return input buffer for frame %d to"
-                    "  its stream:%s (%d)",  __FUNCTION__,
-                    request.frame_number, strerror(-res), res);
-            // TODO: Report error upstream
-        }
-    }
-
     return true;
 }
 
@@ -2554,13 +2621,29 @@
 
     Mutex::Autolock al(mTriggerMutex);
 
+    sp<Camera3Device> parent = mParent.promote();
+    if (parent == NULL) {
+        CLOGE("RequestThread: Parent is gone");
+        return DEAD_OBJECT;
+    }
+
     CameraMetadata &metadata = request->mSettings;
     size_t count = mTriggerMap.size();
 
     for (size_t i = 0; i < count; ++i) {
         RequestTrigger trigger = mTriggerMap.valueAt(i);
-
         uint32_t tag = trigger.metadataTag;
+
+        if (tag == ANDROID_CONTROL_AF_TRIGGER_ID || tag == ANDROID_CONTROL_AE_PRECAPTURE_ID) {
+            bool isAeTrigger = (trigger.metadataTag == ANDROID_CONTROL_AE_PRECAPTURE_ID);
+            uint32_t triggerId = static_cast<uint32_t>(trigger.entryValue);
+            isAeTrigger ? request->mResultExtras.precaptureTriggerId = triggerId :
+                          request->mResultExtras.afTriggerId = triggerId;
+            if (parent->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+                continue; // Trigger ID tag is deprecated since device HAL 3.2
+            }
+        }
+
         camera_metadata_entry entry = metadata.find(tag);
 
         if (entry.count > 0) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 00ae771..ea958b7 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -168,6 +168,8 @@
 
     CameraMetadata             mDeviceInfo;
 
+    int                        mDeviceVersion;
+
     enum Status {
         STATUS_ERROR,
         STATUS_UNINITIALIZED,
@@ -297,6 +299,18 @@
      */
     bool               tryLockSpinRightRound(Mutex& lock);
 
+    struct Size {
+        int width;
+        int height;
+        Size(int w, int h) : width(w), height(h){}
+    };
+
+    /**
+     * Helper function to get the largest Jpeg resolution (in area)
+     * Return Size(0, 0) if static metatdata is invalid
+     */
+    Size getMaxJpegResolution() const;
+
     /**
      * Get Jpeg buffer size for a given jpeg resolution.
      * Negative values are error codes.
@@ -430,6 +444,9 @@
         // Relay error to parent device object setErrorState
         void               setErrorState(const char *fmt, ...);
 
+        // If the input request is in mRepeatingRequests. Must be called with mRequestLock hold
+        bool isRepeatingRequestLocked(const sp<CaptureRequest>);
+
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
         camera3_device_t  *mHal3Device;
@@ -484,9 +501,11 @@
         // Set by process_capture_result call with valid metadata
         bool    haveResultMetadata;
         // Decremented by calls to process_capture_result with valid output
-        // buffers
+        // and input buffers
         int     numBuffersLeft;
         CaptureResultExtras resultExtras;
+        // If this request has any input buffer
+        bool hasInputBuffer;
 
         // Fields used by the partial result quirk only
         struct PartialResultQuirkInFlight {
@@ -505,14 +524,16 @@
                 captureTimestamp(0),
                 requestStatus(OK),
                 haveResultMetadata(false),
-                numBuffersLeft(0) {
+                numBuffersLeft(0),
+                hasInputBuffer(false){
         }
 
         InFlightRequest(int numBuffers) :
                 captureTimestamp(0),
                 requestStatus(OK),
                 haveResultMetadata(false),
-                numBuffersLeft(numBuffers) {
+                numBuffersLeft(numBuffers),
+                hasInputBuffer(false){
         }
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras) :
@@ -520,9 +541,19 @@
                 requestStatus(OK),
                 haveResultMetadata(false),
                 numBuffersLeft(numBuffers),
-                resultExtras(extras) {
+                resultExtras(extras),
+                hasInputBuffer(false){
         }
-    };
+
+        InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput) :
+                captureTimestamp(0),
+                requestStatus(OK),
+                haveResultMetadata(false),
+                numBuffersLeft(numBuffers),
+                resultExtras(extras),
+                hasInputBuffer(hasInput){
+        }
+};
     // Map from frame number to the in-flight request state
     typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
 
@@ -530,7 +561,7 @@
     InFlightMap            mInFlightMap;
 
     status_t registerInFlight(uint32_t frameNumber,
-            int32_t numBuffers, CaptureResultExtras resultExtras);
+            int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput);
 
     /**
      * For the partial result quirk, check if all 3A state fields are available
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 7645a2a..d7b1871 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -485,6 +485,18 @@
 void Camera3Stream::addBufferListener(
         wp<Camera3StreamBufferListener> listener) {
     Mutex::Autolock l(mLock);
+
+    List<wp<Camera3StreamBufferListener> >::iterator it, end;
+    for (it = mBufferListenerList.begin(), end = mBufferListenerList.end();
+         it != end;
+         ) {
+        if (*it == listener) {
+            ALOGE("%s: Try to add the same listener twice, ignoring...", __FUNCTION__);
+            return;
+        }
+        it++;
+    }
+
     mBufferListenerList.push_back(listener);
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 14f5387..a77f27c 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -226,8 +226,17 @@
      */
     virtual void     dump(int fd, const Vector<String16> &args) const = 0;
 
+    /**
+     * Add a camera3 buffer listener. Adding the same listener twice has
+     * no effect.
+     */
     void             addBufferListener(
             wp<Camera3StreamBufferListener> listener);
+
+    /**
+     * Remove a camera3 buffer listener. Removing the same listener twice
+     * or the listener that was never added has no effect.
+     */
     void             removeBufferListener(
             const sp<Camera3StreamBufferListener>& listener);
 
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 05b3d1f..6c298f9 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -300,6 +300,7 @@
     nsecs_t actual = pinnedBuffer->getBufferItem().mTimestamp;
 
     if (actual != timestamp) {
+        // TODO: this is problematic, we'll end up with using wrong result for this pinned buffer.
         ALOGW("%s: ZSL buffer candidate search didn't find an exact match --"
               " requested timestamp = %" PRId64 ", actual timestamp = %" PRId64,
               __FUNCTION__, timestamp, actual);
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index fa59388..747af79 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -377,7 +377,7 @@
 }
 
 status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle,
-                                  const sp<IMemory>& dataMemory)
+                                 const sp<IMemory>& dataMemory)
 {
     ALOGV("startRecognition() model handle %d", handle);
 
@@ -391,28 +391,25 @@
     if (model == 0) {
         return BAD_VALUE;
     }
+    if ((dataMemory == 0) ||
+            (dataMemory->size() < sizeof(struct sound_trigger_recognition_config))) {
+        return BAD_VALUE;
+    }
 
     if (model->mState == Model::STATE_ACTIVE) {
         return INVALID_OPERATION;
     }
     model->mState = Model::STATE_ACTIVE;
 
-    char *data = NULL;
-    unsigned int data_size = 0;
-    if (dataMemory != 0 && dataMemory->size() != 0) {
-        data_size = (unsigned int)dataMemory->size();
-        data = (char *)dataMemory->pointer();
-        ALOGV("startRecognition() data size %d data %d - %d",
-                      data_size, data[0], data[data_size - 1]);
-    }
+    struct sound_trigger_recognition_config *config =
+            (struct sound_trigger_recognition_config *)dataMemory->pointer();
 
     //TODO: get capture handle and device from audio policy service
-    audio_io_handle_t capture_handle = 0;
-    return mHwDevice->start_recognition(mHwDevice, handle, capture_handle, AUDIO_DEVICE_NONE,
+    config->capture_handle = AUDIO_IO_HANDLE_NONE;
+    config->capture_device = AUDIO_DEVICE_NONE;
+    return mHwDevice->start_recognition(mHwDevice, handle, config,
                                         SoundTriggerHwService::recognitionCallback,
-                                        this,
-                                        data_size,
-                                        data);
+                                        this);
 }
 
 status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle)