Merge "MediaMuxer prefer not to use the MPEG4Writer in real time recording mode." into jb-mr2-dev
diff --git a/camera/Android.mk b/camera/Android.mk
index e33fb50..fa518ff 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -22,6 +22,7 @@
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
 	libutils \
+	liblog \
 	libbinder \
 	libhardware \
 	libui \
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
index e455943..ec13911 100644
--- a/camera/tests/Android.mk
+++ b/camera/tests/Android.mk
@@ -35,4 +35,4 @@
 LOCAL_MODULE:= camera_client_test
 LOCAL_MODULE_TAGS := tests
 
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index d583e65..3844487 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -9,7 +9,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libstagefright libmedia libutils libbinder libstagefright_foundation \
-        libjpeg libgui libcutils
+        libjpeg libgui libcutils liblog
 
 LOCAL_C_INCLUDES:= \
 	frameworks/av/media/libstagefright \
@@ -187,4 +187,3 @@
 LOCAL_MODULE:= muxer
 
 include $(BUILD_EXECUTABLE)
-
diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk
index 96205a1..dc973da 100644
--- a/drm/drmserver/Android.mk
+++ b/drm/drmserver/Android.mk
@@ -24,6 +24,7 @@
 LOCAL_SHARED_LIBRARIES := \
     libmedia \
     libutils \
+    liblog \
     libbinder \
     libdl
 
diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk
index 9e07fe3..49c4f9b 100644
--- a/drm/libdrmframework/Android.mk
+++ b/drm/libdrmframework/Android.mk
@@ -25,6 +25,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libutils \
+    liblog \
     libbinder \
     libdl
 
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
index 205b9a5..e251f82 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -36,6 +36,7 @@
     libicui18n \
     libicuuc \
     libutils \
+    liblog \
     libdl \
     libcrypto \
     libssl \
diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk
index d170d49..cb3a2e2 100644
--- a/drm/libdrmframework/plugins/passthru/Android.mk
+++ b/drm/libdrmframework/plugins/passthru/Android.mk
@@ -25,6 +25,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libutils \
+    liblog \
     libdl
 
 
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
index a056cd8..ada23a2 100644
--- a/drm/mediadrm/plugins/mock/Android.mk
+++ b/drm/mediadrm/plugins/mock/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
 
 LOCAL_SHARED_LIBRARIES := \
-    libutils
+    libutils liblog
 
 LOCAL_C_INCLUDES += \
     $(TOP)/frameworks/av/include \
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 91f5c9c..00f6de3 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -21,6 +21,7 @@
 
 #include "drm/DrmAPI.h"
 #include "MockDrmCryptoPlugin.h"
+#include "media/stagefright/MediaErrors.h"
 
 using namespace android;
 
@@ -98,17 +99,17 @@
     }
 
 
-    status_t MockDrmPlugin::getLicenseRequest(Vector<uint8_t> const &sessionId,
-                                              Vector<uint8_t> const &initData,
-                                              String8 const &mimeType, LicenseType licenseType,
-                                              KeyedVector<String8, String8> const &optionalParameters,
-                                              Vector<uint8_t> &request, String8 &defaultUrl)
+    status_t MockDrmPlugin::getKeyRequest(Vector<uint8_t> const &sessionId,
+                                          Vector<uint8_t> const &initData,
+                                          String8 const &mimeType, KeyType keyType,
+                                          KeyedVector<String8, String8> const &optionalParameters,
+                                          Vector<uint8_t> &request, String8 &defaultUrl)
     {
         Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::getLicenseRequest(sessionId=%s, initData=%s, mimeType=%s"
-              ", licenseType=%d, optionalParameters=%s))",
+        ALOGD("MockDrmPlugin::getKeyRequest(sessionId=%s, initData=%s, mimeType=%s"
+              ", keyType=%d, optionalParameters=%s))",
               vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(),
-              licenseType, stringMapToString(optionalParameters).string());
+              keyType, stringMapToString(optionalParameters).string());
 
         ssize_t index = findSession(sessionId);
         if (index == kNotFound) {
@@ -119,15 +120,15 @@
         // Properties used in mock test, set by mock plugin and verifed cts test app
         //   byte[] initData           -> mock-initdata
         //   string mimeType           -> mock-mimetype
-        //   string licenseType        -> mock-licensetype
+        //   string keyType            -> mock-keytype
         //   string optionalParameters -> mock-optparams formatted as {key1,value1},{key2,value2}
 
         mByteArrayProperties.add(String8("mock-initdata"), initData);
         mStringProperties.add(String8("mock-mimetype"), mimeType);
 
-        String8 licenseTypeStr;
-        licenseTypeStr.appendFormat("%d", (int)licenseType);
-        mStringProperties.add(String8("mock-licensetype"), licenseTypeStr);
+        String8 keyTypeStr;
+        keyTypeStr.appendFormat("%d", (int)keyType);
+        mStringProperties.add(String8("mock-keytype"), keyTypeStr);
 
         String8 params;
         for (size_t i = 0; i < optionalParameters.size(); i++) {
@@ -159,11 +160,12 @@
         return OK;
     }
 
-    status_t MockDrmPlugin::provideLicenseResponse(Vector<uint8_t> const &sessionId,
-                                                   Vector<uint8_t> const &response)
+    status_t MockDrmPlugin::provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                               Vector<uint8_t> const &response,
+                                               Vector<uint8_t> &keySetId)
     {
         Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::provideLicenseResponse(sessionId=%s, response=%s)",
+        ALOGD("MockDrmPlugin::provideKeyResponse(sessionId=%s, response=%s)",
               vectorToString(sessionId).string(), vectorToString(response).string());
         ssize_t index = findSession(sessionId);
         if (index == kNotFound) {
@@ -176,30 +178,61 @@
 
         // Properties used in mock test, set by mock plugin and verifed cts test app
         //   byte[] response            -> mock-response
-
         mByteArrayProperties.add(String8("mock-response"), response);
 
+        const size_t kKeySetIdSize = 8;
+
+        for (size_t i = 0; i < kKeySetIdSize / sizeof(long); i++) {
+            long r = random();
+            keySetId.appendArray((uint8_t *)&r, sizeof(long));
+        }
+        mKeySets.add(keySetId);
+
         return OK;
     }
 
-    status_t MockDrmPlugin::removeLicense(Vector<uint8_t> const &sessionId)
+    status_t MockDrmPlugin::removeKeys(Vector<uint8_t> const &keySetId)
     {
         Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::removeLicense(sessionId=%s)",
-              vectorToString(sessionId).string());
+        ALOGD("MockDrmPlugin::removeKeys(keySetId=%s)",
+              vectorToString(keySetId).string());
+
+        ssize_t index = findKeySet(keySetId);
+        if (index == kNotFound) {
+            ALOGD("Invalid keySetId");
+            return BAD_VALUE;
+        }
+        mKeySets.removeAt(index);
+
+        return OK;
+    }
+
+    status_t MockDrmPlugin::restoreKeys(Vector<uint8_t> const &sessionId,
+                                        Vector<uint8_t> const &keySetId)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::restoreKeys(sessionId=%s, keySetId=%s)",
+              vectorToString(sessionId).string(),
+              vectorToString(keySetId).string());
         ssize_t index = findSession(sessionId);
         if (index == kNotFound) {
             ALOGD("Invalid sessionId");
             return BAD_VALUE;
         }
 
+        index = findKeySet(keySetId);
+        if (index == kNotFound) {
+            ALOGD("Invalid keySetId");
+            return BAD_VALUE;
+        }
+
         return OK;
     }
 
-    status_t MockDrmPlugin::queryLicenseStatus(Vector<uint8_t> const &sessionId,
+    status_t MockDrmPlugin::queryKeyStatus(Vector<uint8_t> const &sessionId,
                                                KeyedVector<String8, String8> &infoMap) const
     {
-        ALOGD("MockDrmPlugin::queryLicenseStatus(sessionId=%s)",
+        ALOGD("MockDrmPlugin::queryKeyStatus(sessionId=%s)",
               vectorToString(sessionId).string());
 
         ssize_t index = findSession(sessionId);
@@ -310,7 +343,31 @@
         Mutex::Autolock lock(mLock);
         ALOGD("MockDrmPlugin::setPropertyString(name=%s, value=%s)",
               name.string(), value.string());
-        mStringProperties.add(name, value);
+
+        if (name == "mock-send-event") {
+            unsigned code, extra;
+            sscanf(value.string(), "%d %d", &code, &extra);
+            DrmPlugin::EventType eventType = (DrmPlugin::EventType)code;
+
+            Vector<uint8_t> const *pSessionId = NULL;
+            ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+            if (index >= 0) {
+                pSessionId = &mByteArrayProperties[index];
+            }
+
+            Vector<uint8_t> const *pData = NULL;
+            index = mByteArrayProperties.indexOfKey(String8("mock-event-data"));
+            if (index >= 0) {
+                pData = &mByteArrayProperties[index];
+            }
+            ALOGD("sending event from mock drm plugin: %d %d %s %s",
+                  (int)code, extra, pSessionId ? vectorToString(*pSessionId) : "{}",
+                  pData ? vectorToString(*pData) : "{}");
+
+            sendEvent(eventType, extra, pSessionId, pData);
+        } else {
+            mStringProperties.add(name, value);
+        }
         return OK;
     }
 
@@ -324,6 +381,198 @@
         return OK;
     }
 
+    status_t MockDrmPlugin::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+                                               String8 const &algorithm)
+    {
+        Mutex::Autolock lock(mLock);
+
+        ALOGD("MockDrmPlugin::setCipherAlgorithm(sessionId=%s, algorithm=%s)",
+              vectorToString(sessionId).string(), algorithm.string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        if (algorithm == "AES/CBC/NoPadding") {
+            return OK;
+        }
+        return BAD_VALUE;
+    }
+
+    status_t MockDrmPlugin::setMacAlgorithm(Vector<uint8_t> const &sessionId,
+                                            String8 const &algorithm)
+    {
+        Mutex::Autolock lock(mLock);
+
+        ALOGD("MockDrmPlugin::setMacAlgorithm(sessionId=%s, algorithm=%s)",
+              vectorToString(sessionId).string(), algorithm.string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        if (algorithm == "HmacSHA256") {
+            return OK;
+        }
+        return BAD_VALUE;
+    }
+
+    status_t MockDrmPlugin::encrypt(Vector<uint8_t> const &sessionId,
+                                    Vector<uint8_t> const &keyId,
+                                    Vector<uint8_t> const &input,
+                                    Vector<uint8_t> const &iv,
+                                    Vector<uint8_t> &output)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::encrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
+              vectorToString(sessionId).string(),
+              vectorToString(keyId).string(),
+              vectorToString(input).string(),
+              vectorToString(iv).string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] keyId              -> mock-keyid
+        //   byte[] input              -> mock-input
+        //   byte[] iv                 -> mock-iv
+        mByteArrayProperties.add(String8("mock-keyid"), keyId);
+        mByteArrayProperties.add(String8("mock-input"), input);
+        mByteArrayProperties.add(String8("mock-iv"), iv);
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   byte[] mock-output        -> output
+        index = mByteArrayProperties.indexOfKey(String8("mock-output"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-request' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            output = mByteArrayProperties.valueAt(index);
+        }
+        return OK;
+    }
+
+    status_t MockDrmPlugin::decrypt(Vector<uint8_t> const &sessionId,
+                                    Vector<uint8_t> const &keyId,
+                                    Vector<uint8_t> const &input,
+                                    Vector<uint8_t> const &iv,
+                                    Vector<uint8_t> &output)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::decrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
+              vectorToString(sessionId).string(),
+              vectorToString(keyId).string(),
+              vectorToString(input).string(),
+              vectorToString(iv).string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] keyId              -> mock-keyid
+        //   byte[] input              -> mock-input
+        //   byte[] iv                 -> mock-iv
+        mByteArrayProperties.add(String8("mock-keyid"), keyId);
+        mByteArrayProperties.add(String8("mock-input"), input);
+        mByteArrayProperties.add(String8("mock-iv"), iv);
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   byte[] mock-output        -> output
+        index = mByteArrayProperties.indexOfKey(String8("mock-output"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-request' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            output = mByteArrayProperties.valueAt(index);
+        }
+        return OK;
+    }
+
+    status_t MockDrmPlugin::sign(Vector<uint8_t> const &sessionId,
+                                 Vector<uint8_t> const &keyId,
+                                 Vector<uint8_t> const &message,
+                                 Vector<uint8_t> &signature)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::sign(sessionId=%s, keyId=%s, message=%s)",
+              vectorToString(sessionId).string(),
+              vectorToString(keyId).string(),
+              vectorToString(message).string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] keyId              -> mock-keyid
+        //   byte[] message            -> mock-message
+        mByteArrayProperties.add(String8("mock-keyid"), keyId);
+        mByteArrayProperties.add(String8("mock-message"), message);
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   byte[] mock-signature        -> signature
+        index = mByteArrayProperties.indexOfKey(String8("mock-signature"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-request' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            signature = mByteArrayProperties.valueAt(index);
+        }
+        return OK;
+    }
+
+    status_t MockDrmPlugin::verify(Vector<uint8_t> const &sessionId,
+                                   Vector<uint8_t> const &keyId,
+                                   Vector<uint8_t> const &message,
+                                   Vector<uint8_t> const &signature,
+                                   bool &match)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::verify(sessionId=%s, keyId=%s, message=%s, signature=%s)",
+              vectorToString(sessionId).string(),
+              vectorToString(keyId).string(),
+              vectorToString(message).string(),
+              vectorToString(signature).string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] keyId              -> mock-keyid
+        //   byte[] message            -> mock-message
+        //   byte[] signature          -> mock-signature
+        mByteArrayProperties.add(String8("mock-keyid"), keyId);
+        mByteArrayProperties.add(String8("mock-message"), message);
+        mByteArrayProperties.add(String8("mock-signature"), signature);
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   String mock-match "1" or "0"         -> match
+        index = mStringProperties.indexOfKey(String8("mock-match"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-request' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            match = atol(mStringProperties.valueAt(index).string());
+        }
+        return OK;
+    }
+
     ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
     {
         ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size());
@@ -335,6 +584,18 @@
         return kNotFound;
     }
 
+    ssize_t MockDrmPlugin::findKeySet(Vector<uint8_t> const &keySetId) const
+    {
+        ALOGD("findKeySet: nkeySets=%d, size=%d", mKeySets.size(), keySetId.size());
+        for (size_t i = 0; i < mKeySets.size(); ++i) {
+            if (memcmp(mKeySets[i].array(), keySetId.array(), keySetId.size()) == 0) {
+                return i;
+            }
+        }
+        return kNotFound;
+    }
+
+
     // Conversion utilities
     String8 MockDrmPlugin::vectorToString(Vector<uint8_t> const &vector) const
     {
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
index d46a127..ca9eac7 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -57,21 +57,23 @@
         status_t openSession(Vector<uint8_t> &sessionId);
         status_t closeSession(Vector<uint8_t> const &sessionId);
 
-        status_t
-            getLicenseRequest(Vector<uint8_t> const &sessionId,
-                              Vector<uint8_t> const &initData,
-                              String8 const &mimeType, LicenseType licenseType,
-                              KeyedVector<String8, String8> const &optionalParameters,
-                              Vector<uint8_t> &request, String8 &defaultUrl);
+        status_t getKeyRequest(Vector<uint8_t> const &sessionId,
+                               Vector<uint8_t> const &initData,
+                               String8 const &mimeType, KeyType keyType,
+                               KeyedVector<String8, String8> const &optionalParameters,
+                               Vector<uint8_t> &request, String8 &defaultUrl);
 
-        status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
-                                                Vector<uint8_t> const &response);
+        status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                    Vector<uint8_t> const &response,
+                                    Vector<uint8_t> &keySetId);
 
-        status_t removeLicense(Vector<uint8_t> const &sessionId);
+        status_t removeKeys(Vector<uint8_t> const &keySetId);
 
-        status_t
-            queryLicenseStatus(Vector<uint8_t> const &sessionId,
-                               KeyedVector<String8, String8> &infoMap) const;
+        status_t restoreKeys(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keySetId);
+
+        status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
+                                KeyedVector<String8, String8> &infoMap) const;
 
         status_t getProvisionRequest(Vector<uint8_t> &request,
                                              String8 &defaultUrl);
@@ -90,15 +92,46 @@
         status_t setPropertyByteArray(String8 const &name,
                                       Vector<uint8_t> const &value );
 
+        status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+                                    String8 const &algorithm);
+
+        status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+                                 String8 const &algorithm);
+
+        status_t encrypt(Vector<uint8_t> const &sessionId,
+                         Vector<uint8_t> const &keyId,
+                         Vector<uint8_t> const &input,
+                         Vector<uint8_t> const &iv,
+                         Vector<uint8_t> &output);
+
+        status_t decrypt(Vector<uint8_t> const &sessionId,
+                         Vector<uint8_t> const &keyId,
+                         Vector<uint8_t> const &input,
+                         Vector<uint8_t> const &iv,
+                         Vector<uint8_t> &output);
+
+        status_t sign(Vector<uint8_t> const &sessionId,
+                      Vector<uint8_t> const &keyId,
+                      Vector<uint8_t> const &message,
+                      Vector<uint8_t> &signature);
+
+        status_t verify(Vector<uint8_t> const &sessionId,
+                        Vector<uint8_t> const &keyId,
+                        Vector<uint8_t> const &message,
+                        Vector<uint8_t> const &signature,
+                        bool &match);
+
     private:
         String8 vectorToString(Vector<uint8_t> const &vector) const;
         String8 arrayToString(uint8_t const *array, size_t len) const;
         String8 stringMapToString(KeyedVector<String8, String8> map) const;
 
         SortedVector<Vector<uint8_t> > mSessions;
+        SortedVector<Vector<uint8_t> > mKeySets;
 
         static const ssize_t kNotFound = -1;
         ssize_t findSession(Vector<uint8_t> const &sessionId) const;
+        ssize_t findKeySet(Vector<uint8_t> const &keySetId) const;
 
         Mutex mLock;
         KeyedVector<String8, String8> mStringProperties;
diff --git a/include/media/IDrm.h b/include/media/IDrm.h
index 38e2378..d630c40 100644
--- a/include/media/IDrm.h
+++ b/include/media/IDrm.h
@@ -17,6 +17,7 @@
 #include <binder/IInterface.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/drm/DrmAPI.h>
+#include <media/IDrmClient.h>
 
 #ifndef ANDROID_IDRM_H_
 
@@ -42,19 +43,23 @@
     virtual status_t closeSession(Vector<uint8_t> const &sessionId) = 0;
 
     virtual status_t
-        getLicenseRequest(Vector<uint8_t> const &sessionId,
-                          Vector<uint8_t> const &initData,
-                          String8 const &mimeType, DrmPlugin::LicenseType licenseType,
-                          KeyedVector<String8, String8> const &optionalParameters,
-                          Vector<uint8_t> &request, String8 &defaultUrl) = 0;
+        getKeyRequest(Vector<uint8_t> const &sessionId,
+                      Vector<uint8_t> const &initData,
+                      String8 const &mimeType, DrmPlugin::KeyType keyType,
+                      KeyedVector<String8, String8> const &optionalParameters,
+                      Vector<uint8_t> &request, String8 &defaultUrl) = 0;
 
-    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
-                                            Vector<uint8_t> const &response) = 0;
+    virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                        Vector<uint8_t> const &response,
+                                        Vector<uint8_t> &keySetId) = 0;
 
-    virtual status_t removeLicense(Vector<uint8_t> const &sessionId) = 0;
+    virtual status_t removeKeys(Vector<uint8_t> const &keySetId) = 0;
 
-    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
-                                        KeyedVector<String8, String8> &infoMap) const = 0;
+    virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
+                                 Vector<uint8_t> const &keySetId) = 0;
+
+    virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
+                                    KeyedVector<String8, String8> &infoMap) const = 0;
 
     virtual status_t getProvisionRequest(Vector<uint8_t> &request,
                                          String8 &defaulUrl) = 0;
@@ -65,13 +70,44 @@
 
     virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
 
-    virtual status_t getPropertyString(String8 const &name, String8 &value ) const = 0;
+    virtual status_t getPropertyString(String8 const &name, String8 &value) const = 0;
     virtual status_t getPropertyByteArray(String8 const &name,
-                                          Vector<uint8_t> &value ) const = 0;
+                                          Vector<uint8_t> &value) const = 0;
     virtual status_t setPropertyString(String8 const &name,
                                        String8 const &value ) const = 0;
     virtual status_t setPropertyByteArray(String8 const &name,
-                                          Vector<uint8_t> const &value ) const = 0;
+                                          Vector<uint8_t> const &value) const = 0;
+
+    virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+                                        String8 const &algorithm) = 0;
+
+    virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+                                     String8 const &algorithm) = 0;
+
+    virtual status_t encrypt(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keyId,
+                             Vector<uint8_t> const &input,
+                             Vector<uint8_t> const &iv,
+                             Vector<uint8_t> &output) = 0;
+
+    virtual status_t decrypt(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keyId,
+                             Vector<uint8_t> const &input,
+                             Vector<uint8_t> const &iv,
+                             Vector<uint8_t> &output) = 0;
+
+    virtual status_t sign(Vector<uint8_t> const &sessionId,
+                          Vector<uint8_t> const &keyId,
+                          Vector<uint8_t> const &message,
+                          Vector<uint8_t> &signature) = 0;
+
+    virtual status_t verify(Vector<uint8_t> const &sessionId,
+                            Vector<uint8_t> const &keyId,
+                            Vector<uint8_t> const &message,
+                            Vector<uint8_t> const &signature,
+                            bool &match) = 0;
+
+    virtual status_t setListener(const sp<IDrmClient>& listener) = 0;
 
 private:
     DISALLOW_EVIL_CONSTRUCTORS(IDrm);
@@ -81,6 +117,9 @@
     virtual status_t onTransact(
             uint32_t code, const Parcel &data, Parcel *reply,
             uint32_t flags = 0);
+private:
+    void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
+    void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
 };
 
 }  // namespace android
diff --git a/include/media/IDrmClient.h b/include/media/IDrmClient.h
new file mode 100644
index 0000000..3b2fc7c
--- /dev/null
+++ b/include/media/IDrmClient.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 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_IDRMCLIENT_H
+#define ANDROID_IDRMCLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <media/drm/DrmAPI.h>
+
+namespace android {
+
+class IDrmClient: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(DrmClient);
+
+    virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDrmClient: public BnInterface<IDrmClient>
+{
+public:
+    virtual status_t onTransact(uint32_t code,
+                                const Parcel& data,
+                                Parcel* reply,
+                                uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IDRMCLIENT_H
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 96baf34..5cfe5bc 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -46,6 +46,7 @@
         kWhatInputSurfaceCreated = 'isfc',
         kWhatSignaledInputEOS    = 'seos',
         kWhatBuffersAllocated    = 'allc',
+        kWhatOMXDied             = 'OMXd',
     };
 
     ACodec();
@@ -97,6 +98,7 @@
     struct ExecutingToIdleState;
     struct IdleToLoadedState;
     struct FlushingState;
+    struct DeathNotifier;
 
     enum {
         kWhatSetup                   = 'setu',
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index b0c1b34..742bc0e 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -54,6 +54,7 @@
 
     // Convenience methods:
     bool getUInt16(off64_t offset, uint16_t *x);
+    bool getUInt24(off64_t offset, uint32_t *x); // 3 byte int, returned as a 32-bit int
     bool getUInt32(off64_t offset, uint32_t *x);
     bool getUInt64(off64_t offset, uint64_t *x);
 
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index df54923..de3fc36 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -157,6 +157,10 @@
     kKeyCryptoKey         = 'cryK',  // uint8_t[16]
     kKeyCryptoIV          = 'cryI',  // uint8_t[16]
     kKeyCryptoMode        = 'cryM',  // int32_t
+
+    kKeyCryptoDefaultIVSize = 'cryS',  // int32_t
+
+    kKeyPssh              = 'pssh',  // raw data
 };
 
 enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 0833110..5ae6f6b 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -55,6 +55,8 @@
     size_t countTracks() const;
     status_t getTrackFormat(size_t index, sp<AMessage> *format) const;
 
+    status_t getFileFormat(sp<AMessage> *format) const;
+
     status_t selectTrack(size_t index);
     status_t unselectTrack(size_t index);
 
diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk
index 778c5ac..2286827 100755
--- a/libvideoeditor/lvpp/Android.mk
+++ b/libvideoeditor/lvpp/Android.mk
@@ -50,6 +50,7 @@
     libaudioutils             \
     libbinder                 \
     libcutils                 \
+    liblog                    \
     libEGL                    \
     libGLESv2                 \
     libgui                    \
diff --git a/libvideoeditor/osal/src/Android.mk b/libvideoeditor/osal/src/Android.mk
index b73b9ae..4f38b0c 100755
--- a/libvideoeditor/osal/src/Android.mk
+++ b/libvideoeditor/osal/src/Android.mk
@@ -41,7 +41,7 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SHARED_LIBRARIES := libcutils libutils
+LOCAL_SHARED_LIBRARIES := libcutils libutils liblog
 
 LOCAL_C_INCLUDES += \
     $(TOP)/frameworks/av/libvideoeditor/osal/inc \
@@ -64,4 +64,3 @@
     -DUSE_STAGEFRIGHT_3GPP_READER
 
 include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libvideoeditor/vss/src/Android.mk b/libvideoeditor/vss/src/Android.mk
index cda7a83..0caa15b 100755
--- a/libvideoeditor/vss/src/Android.mk
+++ b/libvideoeditor/vss/src/Android.mk
@@ -57,6 +57,7 @@
     libaudioutils               \
     libbinder                   \
     libcutils                   \
+    liblog                      \
     libmedia                    \
     libstagefright              \
     libstagefright_foundation   \
@@ -96,4 +97,3 @@
     -DDECODE_GIF_ON_SAVING
 
 include $(BUILD_SHARED_LIBRARY)
-
diff --git a/media/common_time/Android.mk b/media/common_time/Android.mk
index 526f17b..632acbc 100644
--- a/media/common_time/Android.mk
+++ b/media/common_time/Android.mk
@@ -16,6 +16,7 @@
                    utils.cpp
 LOCAL_SHARED_LIBRARIES := libbinder \
                           libhardware \
-                          libutils
+                          libutils \
+                          liblog
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
index 3052ad9..5d0a87c 100644
--- a/media/libeffects/downmix/Android.mk
+++ b/media/libeffects/downmix/Android.mk
@@ -7,7 +7,7 @@
 	EffectDownmix.c
 
 LOCAL_SHARED_LIBRARIES := \
-	libcutils
+	libcutils liblog
 
 LOCAL_MODULE:= libdownmix
 
diff --git a/media/libeffects/factory/Android.mk b/media/libeffects/factory/Android.mk
index 6e69151..60a6ce5 100644
--- a/media/libeffects/factory/Android.mk
+++ b/media/libeffects/factory/Android.mk
@@ -7,7 +7,7 @@
 	EffectsFactory.c
 
 LOCAL_SHARED_LIBRARIES := \
-	libcutils
+	libcutils liblog
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
 LOCAL_MODULE:= libeffects
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
index dfa1711..c344352 100644
--- a/media/libeffects/preprocessing/Android.mk
+++ b/media/libeffects/preprocessing/Android.mk
@@ -21,7 +21,8 @@
 LOCAL_SHARED_LIBRARIES := \
     libwebrtc_audio_preprocessing \
     libspeexresampler \
-    libutils
+    libutils \
+    liblog
 
 ifeq ($(TARGET_SIMULATOR),true)
 LOCAL_LDLIBS += -ldl
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index 49cf4fa..e196eb2 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -10,6 +10,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
+	liblog \
 	libdl
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 1ada9c3..2c0c3a5 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -20,6 +20,7 @@
     IAudioRecord.cpp \
     ICrypto.cpp \
     IDrm.cpp \
+    IDrmClient.cpp \
     IHDCP.cpp \
     AudioRecord.cpp \
     AudioSystem.cpp \
@@ -62,7 +63,7 @@
 LOCAL_CFLAGS += -DSINGLE_STATE_QUEUE_INSTANTIATIONS='"SingleStateQueueInstantiations.cpp"'
 
 LOCAL_SHARED_LIBRARIES := \
-	libui libcutils libutils libbinder libsonivox libicuuc libexpat \
+	libui liblog libcutils libutils libbinder libsonivox libicuuc libexpat \
         libcamera_client libstagefright_foundation \
         libgui libdl libaudioutils
 
diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp
index 3b13ec6..1578846 100644
--- a/media/libmedia/IDrm.cpp
+++ b/media/libmedia/IDrm.cpp
@@ -33,10 +33,11 @@
     DESTROY_PLUGIN,
     OPEN_SESSION,
     CLOSE_SESSION,
-    GET_LICENSE_REQUEST,
-    PROVIDE_LICENSE_RESPONSE,
-    REMOVE_LICENSE,
-    QUERY_LICENSE_STATUS,
+    GET_KEY_REQUEST,
+    PROVIDE_KEY_RESPONSE,
+    REMOVE_KEYS,
+    RESTORE_KEYS,
+    QUERY_KEY_STATUS,
     GET_PROVISION_REQUEST,
     PROVIDE_PROVISION_RESPONSE,
     GET_SECURE_STOPS,
@@ -44,7 +45,14 @@
     GET_PROPERTY_STRING,
     GET_PROPERTY_BYTE_ARRAY,
     SET_PROPERTY_STRING,
-    SET_PROPERTY_BYTE_ARRAY
+    SET_PROPERTY_BYTE_ARRAY,
+    SET_CIPHER_ALGORITHM,
+    SET_MAC_ALGORITHM,
+    ENCRYPT,
+    DECRYPT,
+    SIGN,
+    VERIFY,
+    SET_LISTENER
 };
 
 struct BpDrm : public BpInterface<IDrm> {
@@ -92,9 +100,7 @@
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
         remote()->transact(OPEN_SESSION, data, &reply);
-        uint32_t size = reply.readInt32();
-        sessionId.insertAt((size_t)0, size);
-        reply.read(sessionId.editArray(), size);
+        readVector(reply, sessionId);
 
         return reply.readInt32();
     }
@@ -103,80 +109,81 @@
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        data.writeInt32(sessionId.size());
-        data.write(sessionId.array(), sessionId.size());
+        writeVector(data, sessionId);
         remote()->transact(CLOSE_SESSION, data, &reply);
 
         return reply.readInt32();
     }
 
     virtual status_t
-        getLicenseRequest(Vector<uint8_t> const &sessionId,
-                          Vector<uint8_t> const &initData,
-                          String8 const &mimeType, DrmPlugin::LicenseType licenseType,
-                          KeyedVector<String8, String8> const &optionalParameters,
-                          Vector<uint8_t> &request, String8 &defaultUrl) {
+        getKeyRequest(Vector<uint8_t> const &sessionId,
+                      Vector<uint8_t> const &initData,
+                      String8 const &mimeType, DrmPlugin::KeyType keyType,
+                      KeyedVector<String8, String8> const &optionalParameters,
+                      Vector<uint8_t> &request, String8 &defaultUrl) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        data.writeInt32(sessionId.size());
-        data.write(sessionId.array(), sessionId.size());
-
-        data.writeInt32(initData.size());
-        data.write(initData.array(), initData.size());
-
+        writeVector(data, sessionId);
+        writeVector(data, initData);
         data.writeString8(mimeType);
-        data.writeInt32((uint32_t)licenseType);
+        data.writeInt32((uint32_t)keyType);
 
         data.writeInt32(optionalParameters.size());
         for (size_t i = 0; i < optionalParameters.size(); ++i) {
             data.writeString8(optionalParameters.keyAt(i));
             data.writeString8(optionalParameters.valueAt(i));
         }
-        remote()->transact(GET_LICENSE_REQUEST, data, &reply);
+        remote()->transact(GET_KEY_REQUEST, data, &reply);
 
-        uint32_t len = reply.readInt32();
-        request.insertAt((size_t)0, len);
-        reply.read(request.editArray(), len);
+        readVector(reply, request);
         defaultUrl = reply.readString8();
 
         return reply.readInt32();
     }
 
-    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
-                                            Vector<uint8_t> const &response) {
+    virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                        Vector<uint8_t> const &response,
+                                        Vector<uint8_t> &keySetId) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
-        data.writeInt32(sessionId.size());
-        data.write(sessionId.array(), sessionId.size());
-        data.writeInt32(response.size());
-        data.write(response.array(), response.size());
-        remote()->transact(PROVIDE_LICENSE_RESPONSE, data, &reply);
+        writeVector(data, sessionId);
+        writeVector(data, response);
+        remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply);
+        readVector(reply, keySetId);
 
         return reply.readInt32();
     }
 
-    virtual status_t removeLicense(Vector<uint8_t> const &sessionId) {
+    virtual status_t removeKeys(Vector<uint8_t> const &keySetId) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        data.writeInt32(sessionId.size());
-        data.write(sessionId.array(), sessionId.size());
-        remote()->transact(REMOVE_LICENSE, data, &reply);
+        writeVector(data, keySetId);
+        remote()->transact(REMOVE_KEYS, data, &reply);
 
         return reply.readInt32();
     }
 
-    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
+    virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
+                                 Vector<uint8_t> const &keySetId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        writeVector(data, keySetId);
+        remote()->transact(RESTORE_KEYS, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
                                         KeyedVector<String8, String8> &infoMap) const {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        data.writeInt32(sessionId.size());
-        data.write(sessionId.array(), sessionId.size());
-
-        remote()->transact(QUERY_LICENSE_STATUS, data, &reply);
+        writeVector(data, sessionId);
+        remote()->transact(QUERY_KEY_STATUS, data, &reply);
 
         infoMap.clear();
         size_t count = reply.readInt32();
@@ -195,9 +202,7 @@
 
         remote()->transact(GET_PROVISION_REQUEST, data, &reply);
 
-        uint32_t len = reply.readInt32();
-        request.insertAt((size_t)0, len);
-        reply.read(request.editArray(), len);
+        readVector(reply, request);
         defaultUrl = reply.readString8();
 
         return reply.readInt32();
@@ -207,8 +212,7 @@
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        data.writeInt32(response.size());
-        data.write(response.array(), response.size());
+        writeVector(data, response);
         remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply);
 
         return reply.readInt32();
@@ -224,9 +228,7 @@
         uint32_t count = reply.readInt32();
         for (size_t i = 0; i < count; i++) {
             Vector<uint8_t> secureStop;
-            uint32_t len = reply.readInt32();
-            secureStop.insertAt((size_t)0, len);
-            reply.read(secureStop.editArray(), len);
+            readVector(reply, secureStop);
             secureStops.push_back(secureStop);
         }
         return reply.readInt32();
@@ -236,8 +238,7 @@
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        data.writeInt32(ssRelease.size());
-        data.write(ssRelease.array(), ssRelease.size());
+        writeVector(data, ssRelease);
         remote()->transact(RELEASE_SECURE_STOPS, data, &reply);
 
         return reply.readInt32();
@@ -261,10 +262,7 @@
         data.writeString8(name);
         remote()->transact(GET_PROPERTY_BYTE_ARRAY, data, &reply);
 
-        uint32_t len = reply.readInt32();
-        value.insertAt((size_t)0, len);
-        reply.read(value.editArray(), len);
-
+        readVector(reply, value);
         return reply.readInt32();
     }
 
@@ -285,15 +283,128 @@
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
         data.writeString8(name);
-        data.writeInt32(value.size());
-        data.write(value.array(), value.size());
+        writeVector(data, value);
         remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply);
 
         return reply.readInt32();
     }
 
 
+    virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+                                        String8 const &algorithm) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        data.writeString8(algorithm);
+        remote()->transact(SET_CIPHER_ALGORITHM, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+                                     String8 const &algorithm) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        data.writeString8(algorithm);
+        remote()->transact(SET_MAC_ALGORITHM, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t encrypt(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keyId,
+                             Vector<uint8_t> const &input,
+                             Vector<uint8_t> const &iv,
+                             Vector<uint8_t> &output) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        writeVector(data, keyId);
+        writeVector(data, input);
+        writeVector(data, iv);
+
+        remote()->transact(ENCRYPT, data, &reply);
+        readVector(reply, output);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t decrypt(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keyId,
+                             Vector<uint8_t> const &input,
+                             Vector<uint8_t> const &iv,
+                             Vector<uint8_t> &output) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        writeVector(data, keyId);
+        writeVector(data, input);
+        writeVector(data, iv);
+
+        remote()->transact(DECRYPT, data, &reply);
+        readVector(reply, output);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t sign(Vector<uint8_t> const &sessionId,
+                          Vector<uint8_t> const &keyId,
+                          Vector<uint8_t> const &message,
+                          Vector<uint8_t> &signature) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        writeVector(data, keyId);
+        writeVector(data, message);
+
+        remote()->transact(SIGN, data, &reply);
+        readVector(reply, signature);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t verify(Vector<uint8_t> const &sessionId,
+                            Vector<uint8_t> const &keyId,
+                            Vector<uint8_t> const &message,
+                            Vector<uint8_t> const &signature,
+                            bool &match) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        writeVector(data, keyId);
+        writeVector(data, message);
+        writeVector(data, signature);
+
+        remote()->transact(VERIFY, data, &reply);
+        match = (bool)reply.readInt32();
+        return reply.readInt32();
+    }
+
+    virtual status_t setListener(const sp<IDrmClient>& listener) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        remote()->transact(SET_LISTENER, data, &reply);
+        return reply.readInt32();
+    }
+
 private:
+    void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
+        uint32_t size = reply.readInt32();
+        vector.insertAt((size_t)0, size);
+        reply.read(vector.editArray(), size);
+    }
+
+    void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
+        data.writeInt32(vector.size());
+        data.write(vector.array(), vector.size());
+    }
+
     DISALLOW_EVIL_CONSTRUCTORS(BpDrm);
 };
 
@@ -301,6 +412,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+void BnDrm::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
+    uint32_t size = data.readInt32();
+    vector.insertAt((size_t)0, size);
+    data.read(vector.editArray(), size);
+}
+
+void BnDrm::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
+    reply->writeInt32(vector.size());
+    reply->write(vector.array(), vector.size());
+}
+
 status_t BnDrm::onTransact(
     uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
     switch (code) {
@@ -341,8 +463,7 @@
             CHECK_INTERFACE(IDrm, data, reply);
             Vector<uint8_t> sessionId;
             status_t result = openSession(sessionId);
-            reply->writeInt32(sessionId.size());
-            reply->write(sessionId.array(), sessionId.size());
+            writeVector(reply, sessionId);
             reply->writeInt32(result);
             return OK;
         }
@@ -351,28 +472,20 @@
         {
             CHECK_INTERFACE(IDrm, data, reply);
             Vector<uint8_t> sessionId;
-            uint32_t size = data.readInt32();
-            sessionId.insertAt((size_t)0, size);
-            data.read(sessionId.editArray(), size);
+            readVector(data, sessionId);
             reply->writeInt32(closeSession(sessionId));
             return OK;
         }
 
-        case GET_LICENSE_REQUEST:
+        case GET_KEY_REQUEST:
         {
             CHECK_INTERFACE(IDrm, data, reply);
-            Vector<uint8_t> sessionId;
-            uint32_t size = data.readInt32();
-            sessionId.insertAt((size_t)0, size);
-            data.read(sessionId.editArray(), size);
+            Vector<uint8_t> sessionId, initData;
 
-            Vector<uint8_t> initData;
-            size = data.readInt32();
-            initData.insertAt((size_t)0, size);
-            data.read(initData.editArray(), size);
-
+            readVector(data, sessionId);
+            readVector(data, initData);
             String8 mimeType = data.readString8();
-            DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)data.readInt32();
+            DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)data.readInt32();
 
             KeyedVector<String8, String8> optionalParameters;
             uint32_t count = data.readInt32();
@@ -386,55 +499,54 @@
             Vector<uint8_t> request;
             String8 defaultUrl;
 
-            status_t result = getLicenseRequest(sessionId, initData,
-                                                mimeType, licenseType,
-                                                optionalParameters,
-                                                request, defaultUrl);
-            reply->writeInt32(request.size());
-            reply->write(request.array(), request.size());
+            status_t result = getKeyRequest(sessionId, initData,
+                                            mimeType, keyType,
+                                            optionalParameters,
+                                            request, defaultUrl);
+            writeVector(reply, request);
             reply->writeString8(defaultUrl);
             reply->writeInt32(result);
             return OK;
         }
 
-        case PROVIDE_LICENSE_RESPONSE:
+        case PROVIDE_KEY_RESPONSE:
         {
             CHECK_INTERFACE(IDrm, data, reply);
-            Vector<uint8_t> sessionId;
-            uint32_t size = data.readInt32();
-            sessionId.insertAt((size_t)0, size);
-            data.read(sessionId.editArray(), size);
-            Vector<uint8_t> response;
-            size = data.readInt32();
-            response.insertAt((size_t)0, size);
-            data.read(response.editArray(), size);
-
-            reply->writeInt32(provideLicenseResponse(sessionId, response));
+            Vector<uint8_t> sessionId, response, keySetId;
+            readVector(data, sessionId);
+            readVector(data, response);
+            uint32_t result = provideKeyResponse(sessionId, response, keySetId);
+            writeVector(reply, keySetId);
+            reply->writeInt32(result);
             return OK;
         }
 
-        case REMOVE_LICENSE:
+        case REMOVE_KEYS:
         {
             CHECK_INTERFACE(IDrm, data, reply);
-            Vector<uint8_t> sessionId;
-            uint32_t size = data.readInt32();
-            sessionId.insertAt((size_t)0, size);
-            data.read(sessionId.editArray(), size);
-            reply->writeInt32(removeLicense(sessionId));
+            Vector<uint8_t> keySetId;
+            readVector(data, keySetId);
+            reply->writeInt32(removeKeys(keySetId));
             return OK;
         }
 
-        case QUERY_LICENSE_STATUS:
+        case RESTORE_KEYS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId, keySetId;
+            readVector(data, sessionId);
+            readVector(data, keySetId);
+            reply->writeInt32(restoreKeys(sessionId, keySetId));
+            return OK;
+        }
+
+        case QUERY_KEY_STATUS:
         {
             CHECK_INTERFACE(IDrm, data, reply);
             Vector<uint8_t> sessionId;
-            uint32_t size = data.readInt32();
-            sessionId.insertAt((size_t)0, size);
-            data.read(sessionId.editArray(), size);
+            readVector(data, sessionId);
             KeyedVector<String8, String8> infoMap;
-
-            status_t result = queryLicenseStatus(sessionId, infoMap);
-
+            status_t result = queryKeyStatus(sessionId, infoMap);
             size_t count = infoMap.size();
             reply->writeInt32(count);
             for (size_t i = 0; i < count; ++i) {
@@ -451,8 +563,7 @@
             Vector<uint8_t> request;
             String8 defaultUrl;
             status_t result = getProvisionRequest(request, defaultUrl);
-            reply->writeInt32(request.size());
-            reply->write(request.array(), request.size());
+            writeVector(reply, request);
             reply->writeString8(defaultUrl);
             reply->writeInt32(result);
             return OK;
@@ -462,11 +573,8 @@
         {
             CHECK_INTERFACE(IDrm, data, reply);
             Vector<uint8_t> response;
-            uint32_t size = data.readInt32();
-            response.insertAt((size_t)0, size);
-            data.read(response.editArray(), size);
+            readVector(data, response);
             reply->writeInt32(provideProvisionResponse(response));
-
             return OK;
         }
 
@@ -491,9 +599,7 @@
         {
             CHECK_INTERFACE(IDrm, data, reply);
             Vector<uint8_t> ssRelease;
-            uint32_t size = data.readInt32();
-            ssRelease.insertAt((size_t)0, size);
-            data.read(ssRelease.editArray(), size);
+            readVector(data, ssRelease);
             reply->writeInt32(releaseSecureStops(ssRelease));
             return OK;
         }
@@ -515,8 +621,7 @@
             String8 name = data.readString8();
             Vector<uint8_t> value;
             status_t result = getPropertyByteArray(name, value);
-            reply->writeInt32(value.size());
-            reply->write(value.array(), value.size());
+            writeVector(reply, value);
             reply->writeInt32(result);
             return OK;
         }
@@ -535,15 +640,97 @@
             CHECK_INTERFACE(IDrm, data, reply);
             String8 name = data.readString8();
             Vector<uint8_t> value;
-            size_t count = data.readInt32();
-            value.insertAt((size_t)0, count);
-            data.read(value.editArray(), count);
+            readVector(data, value);
             reply->writeInt32(setPropertyByteArray(name, value));
             return OK;
         }
 
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
+        case SET_CIPHER_ALGORITHM:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId;
+            readVector(data, sessionId);
+            String8 algorithm = data.readString8();
+            reply->writeInt32(setCipherAlgorithm(sessionId, algorithm));
+            return OK;
+        }
+
+        case SET_MAC_ALGORITHM:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId;
+            readVector(data, sessionId);
+            String8 algorithm = data.readString8();
+            reply->writeInt32(setMacAlgorithm(sessionId, algorithm));
+            return OK;
+        }
+
+        case ENCRYPT:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId, keyId, input, iv, output;
+            readVector(data, sessionId);
+            readVector(data, keyId);
+            readVector(data, input);
+            readVector(data, iv);
+            uint32_t result = encrypt(sessionId, keyId, input, iv, output);
+            writeVector(reply, output);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case DECRYPT:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId, keyId, input, iv, output;
+            readVector(data, sessionId);
+            readVector(data, keyId);
+            readVector(data, input);
+            readVector(data, iv);
+            uint32_t result = decrypt(sessionId, keyId, input, iv, output);
+            writeVector(reply, output);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case SIGN:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId, keyId, message, signature;
+            readVector(data, sessionId);
+            readVector(data, keyId);
+            readVector(data, message);
+            uint32_t result = sign(sessionId, keyId, message, signature);
+            writeVector(reply, signature);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case VERIFY:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId, keyId, message, signature;
+            readVector(data, sessionId);
+            readVector(data, keyId);
+            readVector(data, message);
+            readVector(data, signature);
+            bool match;
+            uint32_t result = verify(sessionId, keyId, message, signature, match);
+            reply->writeInt32(match);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+    case SET_LISTENER: {
+        CHECK_INTERFACE(IDrm, data, reply);
+        sp<IDrmClient> listener =
+            interface_cast<IDrmClient>(data.readStrongBinder());
+        reply->writeInt32(setListener(listener));
+        return NO_ERROR;
+    } break;
+
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
     }
 }
 
diff --git a/media/libmedia/IDrmClient.cpp b/media/libmedia/IDrmClient.cpp
new file mode 100644
index 0000000..f50715e
--- /dev/null
+++ b/media/libmedia/IDrmClient.cpp
@@ -0,0 +1,81 @@
+/*
+**
+** Copyright 2013, 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 "IDrmClient"
+#include <utils/Log.h>
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <media/IMediaPlayerClient.h>
+#include <media/IDrmClient.h>
+
+namespace android {
+
+enum {
+    NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpDrmClient: public BpInterface<IDrmClient>
+{
+public:
+    BpDrmClient(const sp<IBinder>& impl)
+        : BpInterface<IDrmClient>(impl)
+    {
+    }
+
+    virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrmClient::getInterfaceDescriptor());
+        data.writeInt32((int)eventType);
+        data.writeInt32(extra);
+        if (obj && obj->dataSize() > 0) {
+            data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
+        }
+        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(DrmClient, "android.media.IDrmClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnDrmClient::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch (code) {
+        case NOTIFY: {
+            CHECK_INTERFACE(IDrmClient, data, reply);
+            int eventType = data.readInt32();
+            int extra = data.readInt32();
+            Parcel obj;
+            if (data.dataAvail() > 0) {
+                obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
+            }
+
+            notify((DrmPlugin::EventType)eventType, extra, &obj);
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}; // namespace android
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 3defec3..ecae3d3 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -398,6 +398,13 @@
     if (mPlayer != 0 && isValidState) {
         int durationMs;
         status_t ret = mPlayer->getDuration(&durationMs);
+
+        if (ret != OK) {
+            // Do not enter error state just because no duration was available.
+            durationMs = -1;
+            ret = OK;
+        }
+
         if (msec) {
             *msec = durationMs;
         }
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 2a6f3c7..d87bc7f 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -27,6 +27,7 @@
     libbinder                   \
     libcamera_client            \
     libcutils                   \
+    liblog                      \
     libdl                       \
     libgui                      \
     libmedia                    \
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 6ac7530..1e6cd94 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -47,6 +47,7 @@
 
 Drm::Drm()
     : mInitCheck(NO_INIT),
+      mListener(NULL),
       mFactory(NULL),
       mPlugin(NULL) {
 }
@@ -67,6 +68,41 @@
     return mInitCheck;
 }
 
+status_t Drm::setListener(const sp<IDrmClient>& listener)
+{
+    Mutex::Autolock lock(mEventLock);
+    mListener = listener;
+    return NO_ERROR;
+}
+
+void Drm::sendEvent(DrmPlugin::EventType eventType, int extra,
+                    Vector<uint8_t> const *sessionId,
+                    Vector<uint8_t> const *data)
+{
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Parcel obj;
+        if (sessionId && sessionId->size()) {
+            obj.writeInt32(sessionId->size());
+            obj.write(sessionId->array(), sessionId->size());
+        } else {
+            obj.writeInt32(0);
+        }
+
+        if (data && data->size()) {
+            obj.writeInt32(data->size());
+            obj.write(data->array(), data->size());
+        } else {
+            obj.writeInt32(0);
+        }
+
+        Mutex::Autolock lock(mNotifyLock);
+        listener->notify(eventType, extra, &obj);
+    }
+}
 
 /*
  * Search the plugins directory for a plugin that supports the scheme
@@ -195,7 +231,9 @@
         return mInitCheck;
     }
 
-    return mFactory->createDrmPlugin(uuid, &mPlugin);
+    status_t result = mFactory->createDrmPlugin(uuid, &mPlugin);
+    mPlugin->setListener(this);
+    return result;
 }
 
 status_t Drm::destroyPlugin() {
@@ -243,11 +281,11 @@
     return mPlugin->closeSession(sessionId);
 }
 
-status_t Drm::getLicenseRequest(Vector<uint8_t> const &sessionId,
-                                Vector<uint8_t> const &initData,
-                                String8 const &mimeType, DrmPlugin::LicenseType licenseType,
-                                KeyedVector<String8, String8> const &optionalParameters,
-                                Vector<uint8_t> &request, String8 &defaultUrl) {
+status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId,
+                            Vector<uint8_t> const &initData,
+                            String8 const &mimeType, DrmPlugin::KeyType keyType,
+                            KeyedVector<String8, String8> const &optionalParameters,
+                            Vector<uint8_t> &request, String8 &defaultUrl) {
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
@@ -258,12 +296,13 @@
         return -EINVAL;
     }
 
-    return mPlugin->getLicenseRequest(sessionId, initData, mimeType, licenseType,
-                                      optionalParameters, request, defaultUrl);
+    return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType,
+                                  optionalParameters, request, defaultUrl);
 }
 
-status_t Drm::provideLicenseResponse(Vector<uint8_t> const &sessionId,
-                                     Vector<uint8_t> const &response) {
+status_t Drm::provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                 Vector<uint8_t> const &response,
+                                 Vector<uint8_t> &keySetId) {
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
@@ -274,10 +313,10 @@
         return -EINVAL;
     }
 
-    return mPlugin->provideLicenseResponse(sessionId, response);
+    return mPlugin->provideKeyResponse(sessionId, response, keySetId);
 }
 
-status_t Drm::removeLicense(Vector<uint8_t> const &sessionId) {
+status_t Drm::removeKeys(Vector<uint8_t> const &keySetId) {
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
@@ -288,11 +327,11 @@
         return -EINVAL;
     }
 
-    return mPlugin->removeLicense(sessionId);
+    return mPlugin->removeKeys(keySetId);
 }
 
-status_t Drm::queryLicenseStatus(Vector<uint8_t> const &sessionId,
-                                 KeyedVector<String8, String8> &infoMap) const {
+status_t Drm::restoreKeys(Vector<uint8_t> const &sessionId,
+                          Vector<uint8_t> const &keySetId) {
     Mutex::Autolock autoLock(mLock);
 
     if (mInitCheck != OK) {
@@ -303,7 +342,22 @@
         return -EINVAL;
     }
 
-    return mPlugin->queryLicenseStatus(sessionId, infoMap);
+    return mPlugin->restoreKeys(sessionId, keySetId);
+}
+
+status_t Drm::queryKeyStatus(Vector<uint8_t> const &sessionId,
+                             KeyedVector<String8, String8> &infoMap) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->queryKeyStatus(sessionId, infoMap);
 }
 
 status_t Drm::getProvisionRequest(Vector<uint8_t> &request, String8 &defaultUrl) {
@@ -420,4 +474,106 @@
     return mPlugin->setPropertyByteArray(name, value);
 }
 
+
+status_t Drm::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+                                 String8 const &algorithm) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->setCipherAlgorithm(sessionId, algorithm);
+}
+
+status_t Drm::setMacAlgorithm(Vector<uint8_t> const &sessionId,
+                              String8 const &algorithm) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->setMacAlgorithm(sessionId, algorithm);
+}
+
+status_t Drm::encrypt(Vector<uint8_t> const &sessionId,
+                      Vector<uint8_t> const &keyId,
+                      Vector<uint8_t> const &input,
+                      Vector<uint8_t> const &iv,
+                      Vector<uint8_t> &output) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->encrypt(sessionId, keyId, input, iv, output);
+}
+
+status_t Drm::decrypt(Vector<uint8_t> const &sessionId,
+                      Vector<uint8_t> const &keyId,
+                      Vector<uint8_t> const &input,
+                      Vector<uint8_t> const &iv,
+                      Vector<uint8_t> &output) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->decrypt(sessionId, keyId, input, iv, output);
+}
+
+status_t Drm::sign(Vector<uint8_t> const &sessionId,
+                   Vector<uint8_t> const &keyId,
+                   Vector<uint8_t> const &message,
+                   Vector<uint8_t> &signature) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->sign(sessionId, keyId, message, signature);
+}
+
+status_t Drm::verify(Vector<uint8_t> const &sessionId,
+                     Vector<uint8_t> const &keyId,
+                     Vector<uint8_t> const &message,
+                     Vector<uint8_t> const &signature,
+                     bool &match) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->verify(sessionId, keyId, message, signature, match);
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 1b10958..3da8ad4 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -21,6 +21,7 @@
 #include "SharedLibrary.h"
 
 #include <media/IDrm.h>
+#include <media/IDrmClient.h>
 #include <utils/threads.h>
 
 namespace android {
@@ -28,7 +29,7 @@
 struct DrmFactory;
 struct DrmPlugin;
 
-struct Drm : public BnDrm {
+struct Drm : public BnDrm, public DrmPluginListener {
     Drm();
     virtual ~Drm();
 
@@ -45,19 +46,23 @@
     virtual status_t closeSession(Vector<uint8_t> const &sessionId);
 
     virtual status_t
-        getLicenseRequest(Vector<uint8_t> const &sessionId,
-                          Vector<uint8_t> const &initData,
-                          String8 const &mimeType, DrmPlugin::LicenseType licenseType,
-                          KeyedVector<String8, String8> const &optionalParameters,
-                          Vector<uint8_t> &request, String8 &defaultUrl);
+        getKeyRequest(Vector<uint8_t> const &sessionId,
+                      Vector<uint8_t> const &initData,
+                      String8 const &mimeType, DrmPlugin::KeyType keyType,
+                      KeyedVector<String8, String8> const &optionalParameters,
+                      Vector<uint8_t> &request, String8 &defaultUrl);
 
-    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
-                                            Vector<uint8_t> const &response);
+    virtual status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                        Vector<uint8_t> const &response,
+                                        Vector<uint8_t> &keySetId);
 
-    virtual status_t removeLicense(Vector<uint8_t> const &sessionId);
+    virtual status_t removeKeys(Vector<uint8_t> const &keySetId);
 
-    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
-                                        KeyedVector<String8, String8> &infoMap) const;
+    virtual status_t restoreKeys(Vector<uint8_t> const &sessionId,
+                                 Vector<uint8_t> const &keySetId);
+
+    virtual status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
+                                    KeyedVector<String8, String8> &infoMap) const;
 
     virtual status_t getProvisionRequest(Vector<uint8_t> &request,
                                          String8 &defaulUrl);
@@ -75,10 +80,50 @@
     virtual status_t setPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> const &value ) const;
 
+    virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
+                                        String8 const &algorithm);
+
+    virtual status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
+                                     String8 const &algorithm);
+
+    virtual status_t encrypt(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keyId,
+                             Vector<uint8_t> const &input,
+                             Vector<uint8_t> const &iv,
+                             Vector<uint8_t> &output);
+
+    virtual status_t decrypt(Vector<uint8_t> const &sessionId,
+                             Vector<uint8_t> const &keyId,
+                             Vector<uint8_t> const &input,
+                             Vector<uint8_t> const &iv,
+                             Vector<uint8_t> &output);
+
+    virtual status_t sign(Vector<uint8_t> const &sessionId,
+                          Vector<uint8_t> const &keyId,
+                          Vector<uint8_t> const &message,
+                          Vector<uint8_t> &signature);
+
+    virtual status_t verify(Vector<uint8_t> const &sessionId,
+                            Vector<uint8_t> const &keyId,
+                            Vector<uint8_t> const &message,
+                            Vector<uint8_t> const &signature,
+                            bool &match);
+
+    virtual status_t setListener(const sp<IDrmClient>& listener);
+
+    virtual void sendEvent(DrmPlugin::EventType eventType, int extra,
+                           Vector<uint8_t> const *sessionId,
+                           Vector<uint8_t> const *data);
+
 private:
     mutable Mutex mLock;
 
     status_t mInitCheck;
+
+    sp<IDrmClient> mListener;
+    mutable Mutex mEventLock;
+    mutable Mutex mNotifyLock;
+
     sp<SharedLibrary> mLibrary;
     DrmFactory *mFactory;
     DrmPlugin *mPlugin;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 723af09..bdafb29 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -314,11 +314,11 @@
     Mutex::Autolock autoLock(mLock);
 
     if (mDurationUs < 0) {
-        *msec = 0;
-    } else {
-        *msec = (mDurationUs + 500ll) / 1000;
+        return UNKNOWN_ERROR;
     }
 
+    *msec = (mDurationUs + 500ll) / 1000;
+
     return OK;
 }
 
diff --git a/media/libnbaio/Android.mk b/media/libnbaio/Android.mk
index d372d20..5d00d15 100644
--- a/media/libnbaio/Android.mk
+++ b/media/libnbaio/Android.mk
@@ -30,6 +30,7 @@
     libbinder \
     libcommon_time_client \
     libcutils \
-    libutils
+    libutils \
+    liblog
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index c9f8741..ee49033 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -166,6 +166,24 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+struct ACodec::DeathNotifier : public IBinder::DeathRecipient {
+    DeathNotifier(const sp<AMessage> &notify)
+        : mNotify(notify) {
+    }
+
+    virtual void binderDied(const wp<IBinder> &) {
+        mNotify->post();
+    }
+
+protected:
+    virtual ~DeathNotifier() {}
+
+private:
+    sp<AMessage> mNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DeathNotifier);
+};
+
 struct ACodec::UninitializedState : public ACodec::BaseState {
     UninitializedState(ACodec *codec);
 
@@ -177,6 +195,8 @@
     void onSetup(const sp<AMessage> &msg);
     bool onAllocateComponent(const sp<AMessage> &msg);
 
+    sp<DeathNotifier> mDeathNotifier;
+
     DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
 };
 
@@ -1432,14 +1452,6 @@
     CHECK_EQ(err, (status_t)OK);
     CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
 
-    CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
-           || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
-           || format.eColorFormat == OMX_COLOR_FormatCbYCrY
-           || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
-           || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
-           || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka
-           || format.eColorFormat == OMX_SEC_COLOR_FormatNV12Tiled);
-
     return mOMX->setParameter(
             mNode, OMX_IndexParamVideoPortFormat,
             &format, sizeof(format));
@@ -2487,6 +2499,13 @@
             return true;
         }
 
+        case ACodec::kWhatOMXDied:
+        {
+            ALOGE("OMX/mediaserver died, signalling error!");
+            mCodec->signalError(OMX_ErrorResourcesLost, DEAD_OBJECT);
+            break;
+        }
+
         default:
             return false;
     }
@@ -3035,6 +3054,18 @@
 
 void ACodec::UninitializedState::stateEntered() {
     ALOGV("Now uninitialized");
+
+    if (mDeathNotifier != NULL) {
+        mCodec->mOMX->asBinder()->unlinkToDeath(mDeathNotifier);
+        mDeathNotifier.clear();
+    }
+
+    mCodec->mNativeWindow.clear();
+    mCodec->mNode = NULL;
+    mCodec->mOMX.clear();
+    mCodec->mQuirks = 0;
+    mCodec->mFlags = 0;
+    mCodec->mComponentName.clear();
 }
 
 bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
@@ -3106,6 +3137,15 @@
 
     sp<IOMX> omx = client.interface();
 
+    sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec->id());
+
+    mDeathNotifier = new DeathNotifier(notify);
+    if (omx->asBinder()->linkToDeath(mDeathNotifier) != OK) {
+        // This was a local binder, if it dies so do we, we won't care
+        // about any notifications in the afterlife.
+        mDeathNotifier.clear();
+    }
+
     Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
 
     AString mime;
@@ -3170,7 +3210,7 @@
         return false;
     }
 
-    sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
+    notify = new AMessage(kWhatOMXMessage, mCodec->id());
     observer->setNotificationMessage(notify);
 
     mCodec->mComponentName = componentName;
@@ -3224,13 +3264,6 @@
     if (!keepComponentAllocated) {
         CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
 
-        mCodec->mNativeWindow.clear();
-        mCodec->mNode = NULL;
-        mCodec->mOMX.clear();
-        mCodec->mQuirks = 0;
-        mCodec->mFlags = 0;
-        mCodec->mComponentName.clear();
-
         mCodec->changeState(mCodec->mUninitializedState);
     }
 
@@ -3416,6 +3449,21 @@
             return true;
         }
 
+        case kWhatResume:
+        {
+            // We'll be active soon enough.
+            return true;
+        }
+
+        case kWhatFlush:
+        {
+            // We haven't even started yet, so we're flushed alright...
+            sp<AMessage> notify = mCodec->mNotify->dup();
+            notify->setInt32("what", ACodec::kWhatFlushCompleted);
+            notify->post();
+            return true;
+        }
+
         default:
             return BaseState::onMessageReceived(msg);
     }
@@ -3611,7 +3659,6 @@
                      (status_t)OK);
 
             mCodec->changeState(mCodec->mFlushingState);
-
             handled = true;
             break;
         }
@@ -4126,6 +4173,10 @@
 
         mCodec->mInputEOSResult = OK;
 
+        if (mCodec->mSkipCutBuffer != NULL) {
+            mCodec->mSkipCutBuffer->clear();
+        }
+
         mCodec->changeState(mCodec->mExecutingState);
     }
 }
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 19b38ee..fc6fd9c 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -58,6 +58,19 @@
     return true;
 }
 
+bool DataSource::getUInt24(off64_t offset, uint32_t *x) {
+    *x = 0;
+
+    uint8_t byte[3];
+    if (readAt(offset, byte, 3) != 3) {
+        return false;
+    }
+
+    *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
+
+    return true;
+}
+
 bool DataSource::getUInt32(off64_t offset, uint32_t *x) {
     *x = 0;
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 56fad60..3503aaf 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -78,6 +78,19 @@
     int32_t mLastParsedTrackId;
     int32_t mTrackId;
 
+    int32_t mCryptoMode;    // passed in from extractor
+    int32_t mDefaultIVSize; // passed in from extractor
+    uint8_t mCryptoKey[16]; // passed in from extractor
+    uint32_t mCurrentAuxInfoType;
+    uint32_t mCurrentAuxInfoTypeParameter;
+    uint32_t mCurrentDefaultSampleInfoSize;
+    uint32_t mCurrentSampleInfoCount;
+    uint32_t mCurrentSampleInfoAllocSize;
+    uint8_t* mCurrentSampleInfoSizes;
+    uint32_t mCurrentSampleInfoOffsetCount;
+    uint32_t mCurrentSampleInfoOffsetsAllocSize;
+    uint64_t* mCurrentSampleInfoOffsets;
+
     bool mIsAVC;
     size_t mNALLengthSize;
 
@@ -95,6 +108,8 @@
     status_t parseChunk(off64_t *offset);
     status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
     status_t parseTrackFragmentRun(off64_t offset, off64_t size);
+    status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
+    status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
 
     struct TrackFragmentHeaderInfo {
         enum Flags {
@@ -122,6 +137,9 @@
         off64_t offset;
         size_t size;
         uint32_t duration;
+        uint8_t iv[16];
+        Vector<size_t> clearsizes;
+        Vector<size_t> encryptedsizes;
     };
     Vector<Sample> mCurrentSamples;
 
@@ -333,6 +351,10 @@
         sinf = next;
     }
     mFirstSINF = NULL;
+
+    for (size_t i = 0; i < mPssh.size(); i++) {
+        delete [] mPssh[i].data;
+    }
 }
 
 uint32_t MPEG4Extractor::flags() const {
@@ -353,6 +375,7 @@
 size_t MPEG4Extractor::countTracks() {
     status_t err;
     if ((err = readMetaData()) != OK) {
+        ALOGV("MPEG4Extractor::countTracks: no tracks");
         return 0;
     }
 
@@ -363,6 +386,7 @@
         track = track->next;
     }
 
+    ALOGV("MPEG4Extractor::countTracks: %d tracks", n);
     return n;
 }
 
@@ -461,6 +485,23 @@
     }
 
     CHECK_NE(err, (status_t)NO_INIT);
+
+    // copy pssh data into file metadata
+    int psshsize = 0;
+    for (size_t i = 0; i < mPssh.size(); i++) {
+        psshsize += 20 + mPssh[i].datalen;
+    }
+    if (psshsize) {
+        char *buf = (char*)malloc(psshsize);
+        char *ptr = buf;
+        for (size_t i = 0; i < mPssh.size(); i++) {
+            memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
+            memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
+            ptr += (20 + mPssh[i].datalen);
+        }
+        mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
+        free(buf);
+    }
     return mInitCheck;
 }
 
@@ -759,6 +800,8 @@
         case FOURCC('m', 'f', 'r', 'a'):
         case FOURCC('u', 'd', 't', 'a'):
         case FOURCC('i', 'l', 's', 't'):
+        case FOURCC('s', 'i', 'n', 'f'):
+        case FOURCC('s', 'c', 'h', 'i'):
         {
             if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
                 ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
@@ -846,6 +889,69 @@
             break;
         }
 
+        case FOURCC('f', 'r', 'm', 'a'):
+        {
+            int32_t original_fourcc;
+            if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) {
+                return ERROR_IO;
+            }
+            original_fourcc = ntohl(original_fourcc);
+            ALOGV("read original format: %d", original_fourcc);
+            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('t', 'e', 'n', 'c'):
+        {
+            if (chunk_size < 32) {
+                return ERROR_MALFORMED;
+            }
+
+            // tenc box contains 1 byte version, 3 byte flags, 3 byte default algorithm id, one byte
+            // default IV size, 16 bytes default KeyID
+            // (ISO 23001-7)
+            char buf[4];
+            memset(buf, 0, 4);
+            if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) {
+                return ERROR_IO;
+            }
+            uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf));
+            if (defaultAlgorithmId > 1) {
+                // only 0 (clear) and 1 (AES-128) are valid
+                return ERROR_MALFORMED;
+            }
+
+            memset(buf, 0, 4);
+            if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) {
+                return ERROR_IO;
+            }
+            uint32_t defaultIVSize = ntohl(*((int32_t*)buf));
+
+            if ((defaultAlgorithmId == 0 && defaultIVSize != 0) ||
+                    (defaultAlgorithmId != 0 && defaultIVSize == 0)) {
+                // only unencrypted data must have 0 IV size
+                return ERROR_MALFORMED;
+            } else if (defaultIVSize != 0 &&
+                    defaultIVSize != 8 &&
+                    defaultIVSize != 16) {
+                // only supported sizes are 0, 8 and 16
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t defaultKeyId[16];
+
+            if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
+            mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
+            mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
+            *offset += chunk_size;
+            break;
+        }
+
         case FOURCC('t', 'k', 'h', 'd'):
         {
             status_t err;
@@ -857,6 +963,37 @@
             break;
         }
 
+        case FOURCC('p', 's', 's', 'h'):
+        {
+            PsshInfo pssh;
+
+            if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
+                return ERROR_IO;
+            }
+
+            uint32_t psshdatalen = 0;
+            if (mDataSource->readAt(data_offset + 20, &psshdatalen, 4) < 4) {
+                return ERROR_IO;
+            }
+            pssh.datalen = ntohl(psshdatalen);
+            ALOGV("pssh data size: %d", pssh.datalen);
+            if (pssh.datalen + 20 > chunk_size) {
+                // pssh data length exceeds size of containing box
+                return ERROR_MALFORMED;
+            }
+
+            pssh.data = new uint8_t[pssh.datalen];
+            ALOGV("allocated pssh @ %p", pssh.data);
+            ssize_t requested = (ssize_t) pssh.datalen;
+            if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
+                return ERROR_IO;
+            }
+            mPssh.push_back(pssh);
+
+            *offset += chunk_size;
+            break;
+        }
+
         case FOURCC('m', 'd', 'h', 'd'):
         {
             if (chunk_data_size < 4) {
@@ -970,16 +1107,17 @@
                 // For 3GPP timed text, there could be multiple tx3g boxes contain
                 // multiple text display formats. These formats will be used to
                 // display the timed text.
+                // For encrypted files, there may also be more than one entry.
                 const char *mime;
                 CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
-                if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+                if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
+                        strcasecmp(mime, "application/octet-stream")) {
                     // For now we only support a single type of media per track.
                     mLastTrack->skipTrack = true;
                     *offset += chunk_size;
                     break;
                 }
             }
-
             off64_t stop_offset = *offset + chunk_size;
             *offset = data_offset + 8;
             for (uint32_t i = 0; i < entry_count; ++i) {
@@ -1053,6 +1191,7 @@
         }
 
         case FOURCC('m', 'p', '4', 'v'):
+        case FOURCC('e', 'n', 'c', 'v'):
         case FOURCC('s', '2', '6', '3'):
         case FOURCC('H', '2', '6', '3'):
         case FOURCC('h', '2', '6', '3'):
@@ -1075,7 +1214,7 @@
             uint16_t width = U16_AT(&buffer[6 + 18]);
             uint16_t height = U16_AT(&buffer[6 + 20]);
 
-            // The video sample is not stand-compliant if it has invalid dimension.
+            // The video sample is not standard-compliant if it has invalid dimension.
             // Use some default width and height value, and
             // let the decoder figure out the actual width and height (and thus
             // be prepared for INFO_FOMRAT_CHANGED event).
@@ -1085,7 +1224,10 @@
             // printf("*** coding='%s' width=%d height=%d\n",
             //        chunk, width, height);
 
-            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+            if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
+                // if the chunk type is encv, we'll get the type from the sinf/frma box later
+                mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+            }
             mLastTrack->meta->setInt32(kKeyWidth, width);
             mLastTrack->meta->setInt32(kKeyHeight, height);
 
@@ -1442,6 +1584,7 @@
 
         case FOURCC('m', 'd', 'a', 't'):
         {
+            ALOGV("mdat chunk, drm: %d", mIsDrm);
             if (!mIsDrm) {
                 *offset += chunk_size;
                 break;
@@ -1968,6 +2111,8 @@
         return NULL;
     }
 
+    ALOGV("getTrack called, pssh: %d", mPssh.size());
+
     return new MPEG4Source(
             track->meta, mDataSource, track->timescale, track->sampleTable,
             mSidxEntries, mMoofOffset);
@@ -2139,6 +2284,10 @@
       mFirstMoofOffset(firstMoofOffset),
       mCurrentMoofOffset(firstMoofOffset),
       mCurrentTime(0),
+      mCurrentSampleInfoAllocSize(0),
+      mCurrentSampleInfoSizes(NULL),
+      mCurrentSampleInfoOffsetsAllocSize(0),
+      mCurrentSampleInfoOffsets(NULL),
       mIsAVC(false),
       mNALLengthSize(0),
       mStarted(false),
@@ -2146,6 +2295,18 @@
       mBuffer(NULL),
       mWantsNALFragments(false),
       mSrcBuffer(NULL) {
+
+    mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
+    mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
+    uint32_t keytype;
+    const void *key;
+    size_t keysize;
+    if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+        CHECK(keysize <= 16);
+        memset(mCryptoKey, 0, 16);
+        memcpy(mCryptoKey, key, keysize);
+    }
+
     const char *mime;
     bool success = mFormat->findCString(kKeyMIMEType, &mime);
     CHECK(success);
@@ -2179,6 +2340,8 @@
     if (mStarted) {
         stop();
     }
+    free(mCurrentSampleInfoSizes);
+    free(mCurrentSampleInfoOffsets);
 }
 
 status_t MPEG4Source::start(MetaData *params) {
@@ -2274,7 +2437,7 @@
                 }
             }
             if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
-                // *offset points to then mdat box following this moof
+                // *offset points to the mdat box following this moof
                 parseChunk(offset); // doesn't actually parse it, just updates offset
                 mNextMoofOffset = *offset;
             }
@@ -2302,6 +2465,31 @@
                 break;
         }
 
+        case FOURCC('s', 'a', 'i', 'z'): {
+            status_t err;
+            if ((err = parseSampleAuxiliaryInformationSizes(data_offset, chunk_data_size)) != OK) {
+                return err;
+            }
+            *offset += chunk_size;
+            break;
+        }
+        case FOURCC('s', 'a', 'i', 'o'): {
+            status_t err;
+            if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) {
+                return err;
+            }
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'd', 'a', 't'): {
+            // parse DRM info if present
+            ALOGV("MPEG4Source::parseChunk mdat");
+            // if saiz/saoi was previously observed, do something with the sampleinfos
+            *offset += chunk_size;
+            break;
+        }
+
         default: {
             *offset += chunk_size;
             break;
@@ -2310,6 +2498,152 @@
     return OK;
 }
 
+status_t MPEG4Source::parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size) {
+    ALOGV("parseSampleAuxiliaryInformationSizes");
+    // 14496-12 8.7.12
+    uint8_t version;
+    if (mDataSource->readAt(
+            offset, &version, sizeof(version))
+            < (ssize_t)sizeof(version)) {
+        return ERROR_IO;
+    }
+
+    if (version != 0) {
+        return ERROR_UNSUPPORTED;
+    }
+    offset++;
+
+    uint32_t flags;
+    if (!mDataSource->getUInt24(offset, &flags)) {
+        return ERROR_IO;
+    }
+    offset += 3;
+
+    if (flags & 1) {
+        uint32_t tmp;
+        if (!mDataSource->getUInt32(offset, &tmp)) {
+            return ERROR_MALFORMED;
+        }
+        mCurrentAuxInfoType = tmp;
+        offset += 4;
+        if (!mDataSource->getUInt32(offset, &tmp)) {
+            return ERROR_MALFORMED;
+        }
+        mCurrentAuxInfoTypeParameter = tmp;
+        offset += 4;
+    }
+
+    uint8_t defsize;
+    if (mDataSource->readAt(offset, &defsize, 1) != 1) {
+        return ERROR_MALFORMED;
+    }
+    mCurrentDefaultSampleInfoSize = defsize;
+    offset++;
+
+    uint32_t smplcnt;
+    if (!mDataSource->getUInt32(offset, &smplcnt)) {
+        return ERROR_MALFORMED;
+    }
+    offset += 4;
+
+    if (smplcnt > mCurrentSampleInfoAllocSize) {
+        mCurrentSampleInfoSizes = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
+        mCurrentSampleInfoAllocSize = smplcnt;
+    }
+    mCurrentSampleInfoCount = smplcnt;
+
+    mDataSource->readAt(offset, mCurrentSampleInfoSizes, smplcnt);
+    return OK;
+}
+
+status_t MPEG4Source::parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size) {
+    ALOGV("parseSampleAuxiliaryInformationOffsets");
+    // 14496-12 8.7.13
+    uint8_t version;
+    if (mDataSource->readAt(offset, &version, sizeof(version)) != 1) {
+        return ERROR_IO;
+    }
+    offset++;
+
+    uint32_t flags;
+    if (!mDataSource->getUInt24(offset, &flags)) {
+        return ERROR_IO;
+    }
+    offset += 3;
+
+    uint32_t entrycount;
+    if (!mDataSource->getUInt32(offset, &entrycount)) {
+        return ERROR_IO;
+    }
+    offset += 4;
+
+    if (entrycount > mCurrentSampleInfoOffsetsAllocSize) {
+        mCurrentSampleInfoOffsets = (uint64_t*) realloc(mCurrentSampleInfoOffsets, entrycount * 8);
+        mCurrentSampleInfoOffsetsAllocSize = entrycount;
+    }
+    mCurrentSampleInfoOffsetCount = entrycount;
+
+    for (size_t i = 0; i < entrycount; i++) {
+        if (version == 0) {
+            uint32_t tmp;
+            if (!mDataSource->getUInt32(offset, &tmp)) {
+                return ERROR_IO;
+            }
+            mCurrentSampleInfoOffsets[i] = tmp;
+            offset += 4;
+        } else {
+            uint64_t tmp;
+            if (!mDataSource->getUInt64(offset, &tmp)) {
+                return ERROR_IO;
+            }
+            mCurrentSampleInfoOffsets[i] = tmp;
+            offset += 8;
+        }
+    }
+
+    // parse clear/encrypted data
+
+    off64_t drmoffset = mCurrentSampleInfoOffsets[0]; // from moof
+
+    drmoffset += mCurrentMoofOffset;
+    int ivlength;
+    CHECK(mFormat->findInt32(kKeyCryptoDefaultIVSize, &ivlength));
+    int foo = 1;
+    for (size_t i = 0; i < mCurrentSampleInfoCount; i++) {
+        Sample *smpl = &mCurrentSamples.editItemAt(i);
+
+        memset(smpl->iv, 0, 16);
+        if (mDataSource->readAt(drmoffset, smpl->iv, ivlength) != ivlength) {
+            return ERROR_IO;
+        }
+
+        drmoffset += ivlength;
+
+        uint16_t numsubsamples;
+        if (!mDataSource->getUInt16(drmoffset, &numsubsamples)) {
+            return ERROR_IO;
+        }
+        drmoffset += 2;
+        for (size_t j = 0; j < numsubsamples; j++) {
+            uint16_t numclear;
+            uint32_t numencrypted;
+            if (!mDataSource->getUInt16(drmoffset, &numclear)) {
+                return ERROR_IO;
+            }
+            drmoffset += 2;
+            if (!mDataSource->getUInt32(drmoffset, &numencrypted)) {
+                return ERROR_IO;
+            }
+            drmoffset += 4;
+            smpl->clearsizes.add(numclear);
+            smpl->encryptedsizes.add(numencrypted);
+        }
+    }
+
+
+    return OK;
+}
+
 status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) {
 
     if (size < 8) {
@@ -2317,7 +2651,7 @@
     }
 
     uint32_t flags;
-    if (!mDataSource->getUInt32(offset, &flags)) {
+    if (!mDataSource->getUInt32(offset, &flags)) { // actually version + flags
         return ERROR_MALFORMED;
     }
 
@@ -2550,8 +2884,8 @@
             offset += 4;
         }
 
-        ALOGV("adding sample at offset 0x%08llx, size %u, duration %u, "
-              " flags 0x%08x",
+        ALOGV("adding sample %d at offset 0x%08llx, size %u, duration %u, "
+              " flags 0x%08x", i + 1,
                 dataOffset, sampleSize, sampleDuration,
                 (flags & kFirstSampleFlagsPresent) && i == 0
                     ? firstSampleFlags : sampleFlags);
@@ -3111,6 +3445,20 @@
             mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
         }
 
+        const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
+        if (smpl->encryptedsizes.size()) {
+            // store clear/encrypted lengths in metadata
+            sp<MetaData> bufmeta = mBuffer->meta_data();
+            bufmeta->setData(kKeyPlainSizes, 0,
+                    smpl->clearsizes.array(), smpl->clearsizes.size() * 4);
+            bufmeta->setData(kKeyEncryptedSizes, 0,
+                    smpl->encryptedsizes.array(), smpl->encryptedsizes.size() * 4);
+            bufmeta->setData(kKeyCryptoIV, 0, smpl->iv, 16); // use 16 or the actual size?
+            bufmeta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
+            bufmeta->setInt32(kKeyCryptoMode, mCryptoMode);
+            bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
+        }
+
         ++mCurrentSampleIndex;
 
         *out = mBuffer;
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 404fa94..7bc7da2 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -228,6 +228,34 @@
     return convertMetaDataToMessage(meta, format);
 }
 
+status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const {
+    Mutex::Autolock autoLock(mLock);
+
+    *format = NULL;
+
+    if (mImpl == NULL) {
+        return -EINVAL;
+    }
+
+    sp<MetaData> meta = mImpl->getMetaData();
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+    *format = new AMessage();
+    (*format)->setString("mime", mime);
+
+    uint32_t type;
+    const void *pssh;
+    size_t psshsize;
+    if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
+        sp<ABuffer> buf = new ABuffer(psshsize);
+        memcpy(buf->data(), pssh, psshsize);
+        (*format)->setBuffer("pssh", buf);
+    }
+
+    return OK;
+}
+
 status_t NuMediaExtractor::selectTrack(size_t index) {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index ff72e0e..1822f07 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -32,7 +32,7 @@
     MuxOMX(const sp<IOMX> &remoteOMX);
     virtual ~MuxOMX();
 
-    virtual IBinder *onAsBinder() { return NULL; }
+    virtual IBinder *onAsBinder() { return mRemoteOMX->asBinder().get(); }
 
     virtual bool livesLocally(node_id node, pid_t pid);
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 22aefcc..6c0779d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1213,13 +1213,6 @@
         CHECK_EQ(err, (status_t)OK);
         CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
 
-        CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
-               || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
-               || format.eColorFormat == OMX_COLOR_FormatCbYCrY
-               || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
-               || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
-               || format.eColorFormat == OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka);
-
         int32_t colorFormat;
         if (meta->findInt32(kKeyColorFormat, &colorFormat)
                 && colorFormat != OMX_COLOR_FormatUnused
diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk
index 2c6d84c..f26f386 100644
--- a/media/libstagefright/chromium_http/Android.mk
+++ b/media/libstagefright/chromium_http/Android.mk
@@ -22,6 +22,7 @@
         libchromium_net \
         libutils \
         libcutils \
+        liblog \
         libstagefright_foundation \
         libstagefright \
         libdrmframework
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 4dc38a8..ffa64f9 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_STATIC_LIBRARIES := libFraunhoferAAC
 
 LOCAL_SHARED_LIBRARIES := \
-      libstagefright_omx libstagefright_foundation libutils libcutils
+      libstagefright_omx libstagefright_foundation libutils libcutils liblog
 
 LOCAL_MODULE := libstagefright_soft_aacdec
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index a8ab2ac..8ba2afb 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -118,7 +118,7 @@
             status = OK;
         }
     }
-    mIsFirst = true;
+    mDecoderHasData = false;
 
     // for streams that contain metadata, use the mobile profile DRC settings unless overridden
     // by platform properties:
@@ -327,6 +327,7 @@
             notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
             return;
         }
+
         inQueue.erase(inQueue.begin());
         info->mOwnedByUs = false;
         notifyEmptyBufferDone(header);
@@ -358,7 +359,7 @@
             inInfo->mOwnedByUs = false;
             notifyEmptyBufferDone(inHeader);
 
-            if (!mIsFirst) {
+            if (mDecoderHasData) {
                 // flush out the decoder's delayed data by calling DecodeFrame
                 // one more time, with the AACDEC_FLUSH flag set
                 INT_PCM *outBuffer =
@@ -370,6 +371,7 @@
                                            outBuffer,
                                            outHeader->nAllocLen,
                                            AACDEC_FLUSH);
+                mDecoderHasData = false;
 
                 if (decoderErr != AAC_DEC_OK) {
                     mSignalledError = true;
@@ -385,9 +387,7 @@
                             * sizeof(int16_t)
                             * mStreamInfo->numChannels;
             } else {
-                // Since we never discarded frames from the start, we won't have
-                // to add any padding at the end either.
-
+                // we never submitted any data to the decoder, so there's nothing to flush out
                 outHeader->nFilledLen = 0;
             }
 
@@ -473,6 +473,7 @@
                             inBuffer,
                             inBufferLength,
                             bytesValid);
+            mDecoderHasData = true;
 
             decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
                                                 outBuffer,
@@ -484,45 +485,6 @@
             }
         }
 
-        /*
-         * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
-         * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
-         * rate system and the sampling rate in the final output is actually
-         * doubled compared with the core AAC decoder sampling rate.
-         *
-         * Explicit signalling is done by explicitly defining SBR audio object
-         * type in the bitstream. Implicit signalling is done by embedding
-         * SBR content in AAC extension payload specific to SBR, and hence
-         * requires an AAC decoder to perform pre-checks on actual audio frames.
-         *
-         * Thus, we could not say for sure whether a stream is
-         * AAC+/eAAC+ until the first data frame is decoded.
-         */
-        if (mInputBufferCount <= 2) {
-            if (mStreamInfo->sampleRate != prevSampleRate ||
-                mStreamInfo->numChannels != prevNumChannels) {
-                maybeConfigureDownmix();
-                ALOGI("Reconfiguring decoder: %d Hz, %d channels",
-                      mStreamInfo->sampleRate,
-                      mStreamInfo->numChannels);
-
-                // We're going to want to revisit this input buffer, but
-                // may have already advanced the offset. Undo that if
-                // necessary.
-                inHeader->nOffset -= adtsHeaderSize;
-                inHeader->nFilledLen += adtsHeaderSize;
-
-                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
-                mOutputPortSettingsChange = AWAITING_DISABLED;
-                return;
-            }
-        } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
-            ALOGW("Invalid AAC stream");
-            mSignalledError = true;
-            notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-            return;
-        }
-
         size_t numOutBytes =
             mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
 
@@ -544,17 +506,51 @@
             // fall through
         }
 
+        if (inHeader->nFilledLen == 0) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
+
+        /*
+         * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+         * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+         * rate system and the sampling rate in the final output is actually
+         * doubled compared with the core AAC decoder sampling rate.
+         *
+         * Explicit signalling is done by explicitly defining SBR audio object
+         * type in the bitstream. Implicit signalling is done by embedding
+         * SBR content in AAC extension payload specific to SBR, and hence
+         * requires an AAC decoder to perform pre-checks on actual audio frames.
+         *
+         * Thus, we could not say for sure whether a stream is
+         * AAC+/eAAC+ until the first data frame is decoded.
+         */
+        if (mInputBufferCount <= 2) {
+            if (mStreamInfo->sampleRate != prevSampleRate ||
+                mStreamInfo->numChannels != prevNumChannels) {
+                maybeConfigureDownmix();
+                ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                      prevSampleRate, mStreamInfo->sampleRate,
+                      prevNumChannels, mStreamInfo->numChannels);
+
+                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                mOutputPortSettingsChange = AWAITING_DISABLED;
+                return;
+            }
+        } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+            ALOGW("Invalid AAC stream");
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+            return;
+        }
+
         if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
             // We'll only output data if we successfully decoded it or
             // we've previously decoded valid data, in the latter case
             // (decode failed) we'll output a silent frame.
-            if (mIsFirst) {
-                mIsFirst = false;
-                // the first decoded frame should be discarded to account
-                // for decoder delay
-                numOutBytes = 0;
-            }
-
             outHeader->nFilledLen = numOutBytes;
             outHeader->nFlags = 0;
 
@@ -571,14 +567,6 @@
             outHeader = NULL;
         }
 
-        if (inHeader->nFilledLen == 0) {
-            inInfo->mOwnedByUs = false;
-            inQueue.erase(inQueue.begin());
-            inInfo = NULL;
-            notifyEmptyBufferDone(inHeader);
-            inHeader = NULL;
-        }
-
         if (decoderErr == AAC_DEC_OK) {
             ++mInputBufferCount;
         }
@@ -589,14 +577,21 @@
     if (portIndex == 0) {
         // Make sure that the next buffer output does not still
         // depend on fragments from the last one decoded.
-        aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-        mIsFirst = true;
+        // drain all existing data
+        drainDecoder();
     }
 }
 
-void SoftAAC2::onReset() {
+void SoftAAC2::drainDecoder() {
+    short buf [2048];
+    aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
+    aacDecoder_DecodeFrame(mAACDecoder, buf, 4096, AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
     aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-    mIsFirst = true;
+    mDecoderHasData = false;
+}
+
+void SoftAAC2::onReset() {
+    drainDecoder();
 }
 
 void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 6957ade..2d960ab 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -52,7 +52,7 @@
     HANDLE_AACDECODER mAACDecoder;
     CStreamInfo *mStreamInfo;
     bool mIsADTS;
-    bool mIsFirst;
+    bool mDecoderHasData;
     size_t mInputBufferCount;
     bool mSignalledError;
     int64_t mAnchorTimeUs;
@@ -68,6 +68,7 @@
     status_t initDecoder();
     bool isConfigured() const;
     void maybeConfigureDownmix() const;
+    void drainDecoder();
 
     DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
 };
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 820734d..057c69b 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -109,7 +109,7 @@
   LOCAL_STATIC_LIBRARIES := libFraunhoferAAC
 
   LOCAL_SHARED_LIBRARIES := \
-          libstagefright_omx libstagefright_foundation libutils
+          libstagefright_omx libstagefright_foundation libutils liblog
 
   LOCAL_MODULE := libstagefright_soft_aacenc
   LOCAL_MODULE_TAGS := optional
@@ -132,7 +132,7 @@
           libstagefright_aacenc
 
   LOCAL_SHARED_LIBRARIES := \
-          libstagefright_omx libstagefright_foundation libutils \
+          libstagefright_omx libstagefright_foundation libutils liblog \
           libstagefright_enc_common
 
   LOCAL_MODULE := libstagefright_soft_aacenc
diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
index 7719435..5749733 100644
--- a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
+++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp
@@ -481,7 +481,7 @@
 
         void* inBuffer[]        = { (unsigned char *)mInputFrame };
         INT   inBufferIds[]     = { IN_AUDIO_DATA };
-        INT   inBufferSize[]    = { numBytesPerInputFrame };
+        INT   inBufferSize[]    = { (INT)numBytesPerInputFrame };
         INT   inBufferElSize[]  = { sizeof(int16_t) };
 
         AACENC_BufDesc inBufDesc;
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index b48a459..8d6c6f8 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -72,7 +72,7 @@
         libstagefright_amrnbdec libstagefright_amrwbdec
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright_omx libstagefright_foundation libutils \
+        libstagefright_omx libstagefright_foundation libutils liblog \
         libstagefright_amrnb_common
 
 LOCAL_MODULE := libstagefright_soft_amrdec
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.mk b/media/libstagefright/codecs/amrnb/enc/Android.mk
index 457656a..f4e467a 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.mk
+++ b/media/libstagefright/codecs/amrnb/enc/Android.mk
@@ -92,7 +92,7 @@
         libstagefright_amrnbenc
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright_omx libstagefright_foundation libutils \
+        libstagefright_omx libstagefright_foundation libutils liblog \
         libstagefright_amrnb_common
 
 LOCAL_MODULE := libstagefright_soft_amrnbenc
diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
index 07f8b4f..50b739c 100644
--- a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp
@@ -257,7 +257,7 @@
             }
 
             if (pcmParams->nChannels != 1
-                    || pcmParams->nSamplingRate != kSampleRate) {
+                    || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
                 return OMX_ErrorUndefined;
             }
 
diff --git a/media/libstagefright/codecs/amrwbenc/Android.mk b/media/libstagefright/codecs/amrwbenc/Android.mk
index edfd7b7..c5b8e0c 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.mk
+++ b/media/libstagefright/codecs/amrwbenc/Android.mk
@@ -130,7 +130,7 @@
         libstagefright_amrwbenc
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright_omx libstagefright_foundation libutils \
+        libstagefright_omx libstagefright_foundation libutils liblog \
         libstagefright_enc_common
 
 LOCAL_MODULE := libstagefright_soft_amrwbenc
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
index cffe469..7d17c2a 100644
--- a/media/libstagefright/codecs/avc/enc/Android.mk
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -62,6 +62,7 @@
         libstagefright_foundation \
         libstagefright_omx \
         libutils \
+        liblog \
         libui
 
 
diff --git a/media/libstagefright/codecs/flac/enc/Android.mk b/media/libstagefright/codecs/flac/enc/Android.mk
index 546a357..f01d605 100644
--- a/media/libstagefright/codecs/flac/enc/Android.mk
+++ b/media/libstagefright/codecs/flac/enc/Android.mk
@@ -10,7 +10,7 @@
         external/flac/include
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_STATIC_LIBRARIES := \
         libFLAC \
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index 28be646..4c80da6 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -9,7 +9,7 @@
         frameworks/native/include/media/openmax
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_MODULE := libstagefright_soft_g711dec
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk
index 9c0c6ae..71613d2 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.mk
+++ b/media/libstagefright/codecs/gsm/dec/Android.mk
@@ -10,7 +10,7 @@
         external/libgsm/inc
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_STATIC_LIBRARIES := \
         libgsm
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
index a6b1edc..a3d5779 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
@@ -67,7 +67,7 @@
         libstagefright_m4vh263dec
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_MODULE := libstagefright_soft_mpeg4dec
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 865cc9c..83a2dd2 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -65,6 +65,7 @@
         libstagefright_foundation \
         libstagefright_omx \
         libutils \
+        liblog \
         libui
 
 
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index ec8d7ec..135c715 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -70,7 +70,7 @@
         $(LOCAL_PATH)/include
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_mp3dec
diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk
index 0082d7c..7f2c46d 100644
--- a/media/libstagefright/codecs/on2/dec/Android.mk
+++ b/media/libstagefright/codecs/on2/dec/Android.mk
@@ -15,7 +15,7 @@
         libvpx
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_MODULE := libstagefright_soft_vpxdec
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk
index 5d3317c..a92d376 100644
--- a/media/libstagefright/codecs/on2/enc/Android.mk
+++ b/media/libstagefright/codecs/on2/enc/Android.mk
@@ -16,7 +16,7 @@
         libvpx
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libstagefright_omx libstagefright_foundation libutils \
+        libstagefright libstagefright_omx libstagefright_foundation libutils liblog \
 
 LOCAL_MODULE := libstagefright_soft_vpxenc
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
index cc38dc3..e25637a 100644
--- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
+++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
@@ -655,7 +655,8 @@
         vpx_codec_iter_t encoded_packet_iterator = NULL;
         const vpx_codec_cx_pkt_t* encoded_packet;
 
-        while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) {
+        while ((encoded_packet = vpx_codec_get_cx_data(
+                        mCodecContext, &encoded_packet_iterator))) {
             if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
                 outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts;
                 outputBufferHeader->nFlags = 0;
diff --git a/media/libstagefright/codecs/on2/h264dec/Android.mk b/media/libstagefright/codecs/on2/h264dec/Android.mk
index 772fd60..2539f98 100644
--- a/media/libstagefright/codecs/on2/h264dec/Android.mk
+++ b/media/libstagefright/codecs/on2/h264dec/Android.mk
@@ -97,7 +97,7 @@
 endif
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libstagefright_omx libstagefright_foundation libutils \
+	libstagefright libstagefright_omx libstagefright_foundation libutils liblog \
 
 LOCAL_MODULE := libstagefright_soft_h264dec
 
@@ -124,4 +124,3 @@
 LOCAL_MODULE := decoder
 
 include $(BUILD_EXECUTABLE)
-
diff --git a/media/libstagefright/codecs/raw/Android.mk b/media/libstagefright/codecs/raw/Android.mk
index 285c747..fe90a03 100644
--- a/media/libstagefright/codecs/raw/Android.mk
+++ b/media/libstagefright/codecs/raw/Android.mk
@@ -9,7 +9,7 @@
         frameworks/native/include/media/openmax
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright_omx libstagefright_foundation libutils
+        libstagefright_omx libstagefright_foundation libutils liblog
 
 LOCAL_MODULE := libstagefright_soft_rawdec
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
index 395dd6b..2232353 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.mk
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -11,10 +11,9 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libvorbisidec libstagefright libstagefright_omx \
-        libstagefright_foundation libutils
+        libstagefright_foundation libutils liblog
 
 LOCAL_MODULE := libstagefright_soft_vorbisdec
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
-
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index b7577d6..d65e213 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_SHARED_LIBRARIES := \
         libbinder         \
         libutils          \
+        liblog
 
 LOCAL_CFLAGS += -Wno-multichar
 
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index ff35d4a..80a1a3a 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -16,7 +16,7 @@
 	testid3.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libutils libbinder libstagefright_foundation
+	libstagefright libutils liblog libbinder libstagefright_foundation
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_id3
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index c68623a..35eff96 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -59,6 +59,11 @@
 
 private:
 
+    struct PsshInfo {
+        uint8_t uuid[16];
+        uint32_t datalen;
+        uint8_t *data;
+    };
     struct Track {
         Track *next;
         sp<MetaData> meta;
@@ -72,6 +77,8 @@
     uint64_t mSidxDuration;
     off64_t mMoofOffset;
 
+    Vector<PsshInfo> mPssh;
+
     sp<DataSource> mDataSource;
     status_t mInitCheck;
     bool mHasVideo;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 9499712..9f3b19c 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -147,9 +147,9 @@
                 }
 
                 if (startOffset > 0) {
-                    ALOGI("found something resembling an H.264/MPEG syncword at "
-                         "offset %ld",
-                         startOffset);
+                    ALOGI("found something resembling an H.264/MPEG syncword "
+                          "at offset %d",
+                          startOffset);
                 }
 
                 data = &ptr[startOffset];
@@ -180,9 +180,9 @@
                 }
 
                 if (startOffset > 0) {
-                    ALOGI("found something resembling an H.264/MPEG syncword at "
-                         "offset %ld",
-                         startOffset);
+                    ALOGI("found something resembling an H.264/MPEG syncword "
+                          "at offset %d",
+                          startOffset);
                 }
 
                 data = &ptr[startOffset];
@@ -213,8 +213,9 @@
                 }
 
                 if (startOffset > 0) {
-                    ALOGI("found something resembling an AAC syncword at offset %ld",
-                         startOffset);
+                    ALOGI("found something resembling an AAC syncword at "
+                          "offset %d",
+                          startOffset);
                 }
 
                 data = &ptr[startOffset];
@@ -241,8 +242,8 @@
 
                 if (startOffset > 0) {
                     ALOGI("found something resembling an MPEG audio "
-                         "syncword at offset %ld",
-                         startOffset);
+                          "syncword at offset %d",
+                          startOffset);
                 }
 
                 data = &ptr[startOffset];
@@ -394,10 +395,30 @@
 }
 
 sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
-    int64_t timeUs;
+    if (mBuffer->size() == 0) {
+        return NULL;
+    }
 
+    CHECK(!mRangeInfos.empty());
+
+    const RangeInfo &info = *mRangeInfos.begin();
+    if (mBuffer->size() < info.mLength) {
+        return NULL;
+    }
+
+    CHECK_GE(info.mTimestampUs, 0ll);
+
+    // The idea here is consume all AAC frames starting at offsets before
+    // info.mLength so we can assign a meaningful timestamp without
+    // having to interpolate.
+    // The final AAC frame may well extend into the next RangeInfo but
+    // that's ok.
     size_t offset = 0;
-    while (offset + 7 <= mBuffer->size()) {
+    while (offset < info.mLength) {
+        if (offset + 7 > mBuffer->size()) {
+            return NULL;
+        }
+
         ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset);
 
         // adts_fixed_header
@@ -450,24 +471,15 @@
         }
 
         if (offset + aac_frame_length > mBuffer->size()) {
-            break;
+            return NULL;
         }
 
         size_t headerSize = protection_absent ? 7 : 9;
 
-        int64_t tmpUs = fetchTimestamp(aac_frame_length);
-        CHECK_GE(tmpUs, 0ll);
-
-        if (offset == 0) {
-            timeUs = tmpUs;
-        }
-
         offset += aac_frame_length;
     }
 
-    if (offset == 0) {
-        return NULL;
-    }
+    int64_t timeUs = fetchTimestamp(offset);
 
     sp<ABuffer> accessUnit = new ABuffer(offset);
     memcpy(accessUnit->data(), mBuffer->data(), offset);
@@ -492,7 +504,6 @@
 
         if (first) {
             timeUs = info->mTimestampUs;
-            first = false;
         }
 
         if (info->mLength > size) {
@@ -509,6 +520,8 @@
             mRangeInfos.erase(mRangeInfos.begin());
             info = NULL;
         }
+
+        first = false;
     }
 
     if (timeUs == 0ll) {
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 9129f08..a8b4939 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -19,6 +19,7 @@
         libbinder                       \
         libmedia                        \
         libutils                        \
+        liblog                          \
         libui                           \
         libgui                          \
         libcutils                       \
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 46ff22f..971875f 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -292,15 +292,14 @@
 status_t OMXNodeInstance::enableGraphicBuffers(
         OMX_U32 portIndex, OMX_BOOL enable) {
     Mutex::Autolock autoLock(mLock);
+    OMX_STRING name = const_cast<OMX_STRING>(
+            "OMX.google.android.index.enableAndroidNativeBuffers");
 
     OMX_INDEXTYPE index;
-    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
-            mHandle,
-            const_cast<OMX_STRING>("OMX.google.android.index.enableAndroidNativeBuffers"),
-            &index);
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
 
     if (err != OMX_ErrorNone) {
-        ALOGE("OMX_GetExtensionIndex failed");
+        ALOGE("OMX_GetExtensionIndex %s failed", name);
 
         return StatusFromOMXError(err);
     }
@@ -331,14 +330,12 @@
     Mutex::Autolock autoLock(mLock);
 
     OMX_INDEXTYPE index;
-    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
-            mHandle,
-            const_cast<OMX_STRING>(
-                    "OMX.google.android.index.getAndroidNativeBufferUsage"),
-            &index);
+    OMX_STRING name = const_cast<OMX_STRING>(
+            "OMX.google.android.index.getAndroidNativeBufferUsage");
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
 
     if (err != OMX_ErrorNone) {
-        ALOGE("OMX_GetExtensionIndex failed");
+        ALOGE("OMX_GetExtensionIndex %s failed", name);
 
         return StatusFromOMXError(err);
     }
@@ -381,7 +378,9 @@
 
     OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
     if (err != OMX_ErrorNone) {
-        ALOGE("OMX_GetExtensionIndex %s failed", name);
+        if (enable) {
+            ALOGE("OMX_GetExtensionIndex %s failed", name);
+        }
         return StatusFromOMXError(err);
     }
 
@@ -507,13 +506,12 @@
         return useGraphicBuffer2_l(portIndex, graphicBuffer, buffer);
     }
 
-    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
-            mHandle,
-            const_cast<OMX_STRING>("OMX.google.android.index.useAndroidNativeBuffer"),
-            &index);
+    OMX_STRING name = const_cast<OMX_STRING>(
+        "OMX.google.android.index.useAndroidNativeBuffer");
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
 
     if (err != OMX_ErrorNone) {
-        ALOGE("OMX_GetExtensionIndex failed");
+        ALOGE("OMX_GetExtensionIndex %s failed", name);
 
         return StatusFromOMXError(err);
     }
diff --git a/media/libstagefright/omx/tests/Android.mk b/media/libstagefright/omx/tests/Android.mk
index 04441ca..1061c39 100644
--- a/media/libstagefright/omx/tests/Android.mk
+++ b/media/libstagefright/omx/tests/Android.mk
@@ -5,7 +5,7 @@
 	OMXHarness.cpp  \
 
 LOCAL_SHARED_LIBRARIES := \
-	libstagefright libbinder libmedia libutils libstagefright_foundation
+	libstagefright libbinder libmedia libutils liblog libstagefright_foundation
 
 LOCAL_C_INCLUDES := \
 	$(TOP)/frameworks/av/media/libstagefright \
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 57fff0b..06ce16b 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -26,6 +26,7 @@
 	libsync \
 	libui \
 	libutils \
+	liblog
 
 LOCAL_STATIC_LIBRARIES := \
 	libgtest \
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 88ca1cc..f074438 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -451,24 +451,6 @@
             const Fragment &frag = *mOutFragments.begin();
             const sp<ABuffer> &datagram = frag.mBuffer;
 
-            uint8_t *data = datagram->data();
-            if (data[0] == 0x80 && (data[1] & 0x7f) == 33) {
-                int64_t nowUs = ALooper::GetNowUs();
-
-                uint32_t prevRtpTime = U32_AT(&data[4]);
-
-                // 90kHz time scale
-                uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-                int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime;
-
-                ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0);
-
-                data[4] = rtpTime >> 24;
-                data[5] = (rtpTime >> 16) & 0xff;
-                data[6] = (rtpTime >> 8) & 0xff;
-                data[7] = rtpTime & 0xff;
-            }
-
             int n;
             do {
                 n = send(mSocket, datagram->data(), datagram->size(), 0);
@@ -874,6 +856,14 @@
             err = -errno;
             goto bail2;
         }
+
+        int tos = 224;  // VOICE
+        res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+
+        if (res < 0) {
+            err = -errno;
+            goto bail2;
+        }
     }
 
     err = MakeSocketNonBlocking(s);
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index f1f9f45..f99ef60 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -31,6 +31,7 @@
 LOCAL_SHARED_LIBRARIES:= \
         libbinder                       \
         libcutils                       \
+        liblog                          \
         libgui                          \
         libmedia                        \
         libstagefright                  \
@@ -59,6 +60,7 @@
         libstagefright_foundation       \
         libstagefright_wfd              \
         libutils                        \
+        liblog                          \
 
 LOCAL_MODULE:= wfd
 
@@ -81,6 +83,7 @@
         libstagefright_foundation       \
         libstagefright_wfd              \
         libutils                        \
+        liblog                          \
 
 LOCAL_MODULE:= udptest
 
@@ -103,9 +106,33 @@
         libstagefright_foundation       \
         libstagefright_wfd              \
         libutils                        \
+        liblog                          \
 
 LOCAL_MODULE:= rtptest
 
 LOCAL_MODULE_TAGS := debug
 
 include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        nettest.cpp                     \
+
+LOCAL_SHARED_LIBRARIES:= \
+        libbinder                       \
+        libgui                          \
+        libmedia                        \
+        libstagefright                  \
+        libstagefright_foundation       \
+        libstagefright_wfd              \
+        libutils                        \
+        liblog                          \
+
+LOCAL_MODULE:= nettest
+
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp
index 64e182e..cb429bc 100644
--- a/media/libstagefright/wifi-display/TimeSyncer.cpp
+++ b/media/libstagefright/wifi-display/TimeSyncer.cpp
@@ -102,6 +102,10 @@
 
         case kWhatSendPacket:
         {
+            if (mHistory.size() == 0) {
+                ALOGI("starting batch");
+            }
+
             TimeInfo ti;
             memset(&ti, 0, sizeof(ti));
 
@@ -229,6 +233,8 @@
                         } else {
                             notifyOffset();
 
+                            ALOGI("batch done");
+
                             mHistory.clear();
                             postSendPacket(kBatchDelayUs);
                         }
diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h
index 0e3aed7..4e7571f 100644
--- a/media/libstagefright/wifi-display/TimeSyncer.h
+++ b/media/libstagefright/wifi-display/TimeSyncer.h
@@ -75,7 +75,7 @@
         kNumPacketsPerBatch = 30,
     };
     static const int64_t kTimeoutDelayUs = 500000ll;
-    static const int64_t kBatchDelayUs = 10000000ll;  // every 10 secs
+    static const int64_t kBatchDelayUs = 60000000ll;  // every minute
 
     sp<ANetworkSession> mNetSession;
     sp<AMessage> mNotify;
diff --git a/media/libstagefright/wifi-display/nettest.cpp b/media/libstagefright/wifi-display/nettest.cpp
new file mode 100644
index 0000000..0779bf5
--- /dev/null
+++ b/media/libstagefright/wifi-display/nettest.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2013, 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_NEBUG 0
+#define LOG_TAG "nettest"
+#include <utils/Log.h>
+
+#include "ANetworkSession.h"
+#include "TimeSyncer.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+struct TestHandler : public AHandler {
+    TestHandler(const sp<ANetworkSession> &netSession);
+
+    void listen(int32_t port);
+    void connect(const char *host, int32_t port);
+
+protected:
+    virtual ~TestHandler();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kTimeSyncerPort = 8123,
+    };
+
+    enum {
+        kWhatListen,
+        kWhatConnect,
+        kWhatTimeSyncerNotify,
+        kWhatNetNotify,
+        kWhatSendMore,
+        kWhatStop,
+    };
+
+    sp<ANetworkSession> mNetSession;
+    sp<TimeSyncer> mTimeSyncer;
+
+    int32_t mServerSessionID;
+    int32_t mSessionID;
+
+    int64_t mTimeOffsetUs;
+    bool mTimeOffsetValid;
+
+    int32_t mCounter;
+
+    int64_t mMaxDelayMs;
+
+    void dumpDelay(int32_t counter, int64_t delayMs);
+
+    DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
+};
+
+TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
+    : mNetSession(netSession),
+      mServerSessionID(0),
+      mSessionID(0),
+      mTimeOffsetUs(-1ll),
+      mTimeOffsetValid(false),
+      mCounter(0),
+      mMaxDelayMs(-1ll) {
+}
+
+TestHandler::~TestHandler() {
+}
+
+void TestHandler::listen(int32_t port) {
+    sp<AMessage> msg = new AMessage(kWhatListen, id());
+    msg->setInt32("port", port);
+    msg->post();
+}
+
+void TestHandler::connect(const char *host, int32_t port) {
+    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    msg->setString("host", host);
+    msg->setInt32("port", port);
+    msg->post();
+}
+
+void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) {
+    static const int64_t kMinDelayMs = 0;
+    static const int64_t kMaxDelayMs = 300;
+
+    const char *kPattern = "########################################";
+    size_t kPatternSize = strlen(kPattern);
+
+    int n = (kPatternSize * (delayMs - kMinDelayMs))
+                / (kMaxDelayMs - kMinDelayMs);
+
+    if (n < 0) {
+        n = 0;
+    } else if ((size_t)n > kPatternSize) {
+        n = kPatternSize;
+    }
+
+    if (delayMs > mMaxDelayMs) {
+        mMaxDelayMs = delayMs;
+    }
+
+    ALOGI("[%d] (%4lld ms / %4lld ms) %s",
+          counter,
+          delayMs,
+          mMaxDelayMs,
+          kPattern + kPatternSize - n);
+}
+
+void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatListen:
+        {
+            sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+            mTimeSyncer = new TimeSyncer(mNetSession, notify);
+            looper()->registerHandler(mTimeSyncer);
+
+            notify = new AMessage(kWhatNetNotify, id());
+
+            int32_t port;
+            CHECK(msg->findInt32("port", &port));
+
+            struct in_addr ifaceAddr;
+            ifaceAddr.s_addr = INADDR_ANY;
+
+            CHECK_EQ((status_t)OK,
+                     mNetSession->createTCPDatagramSession(
+                         ifaceAddr,
+                         port,
+                         notify,
+                         &mServerSessionID));
+            break;
+        }
+
+        case kWhatConnect:
+        {
+            sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+            mTimeSyncer = new TimeSyncer(mNetSession, notify);
+            looper()->registerHandler(mTimeSyncer);
+            mTimeSyncer->startServer(kTimeSyncerPort);
+
+            AString host;
+            CHECK(msg->findString("host", &host));
+
+            int32_t port;
+            CHECK(msg->findInt32("port", &port));
+
+            notify = new AMessage(kWhatNetNotify, id());
+
+            CHECK_EQ((status_t)OK,
+                     mNetSession->createTCPDatagramSession(
+                         0 /* localPort */,
+                         host.c_str(),
+                         port,
+                         notify,
+                         &mSessionID));
+            break;
+        }
+
+        case kWhatNetNotify:
+        {
+            int32_t reason;
+            CHECK(msg->findInt32("reason", &reason));
+
+            switch (reason) {
+                case ANetworkSession::kWhatConnected:
+                {
+                    ALOGI("kWhatConnected");
+
+                    (new AMessage(kWhatSendMore, id()))->post();
+                    break;
+                }
+
+                case ANetworkSession::kWhatClientConnected:
+                {
+                    ALOGI("kWhatClientConnected");
+
+                    CHECK_EQ(mSessionID, 0);
+                    CHECK(msg->findInt32("sessionID", &mSessionID));
+
+                    AString clientIP;
+                    CHECK(msg->findString("client-ip", &clientIP));
+
+                    mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort);
+                    break;
+                }
+
+                case ANetworkSession::kWhatDatagram:
+                {
+                    sp<ABuffer> packet;
+                    CHECK(msg->findBuffer("data", &packet));
+
+                    CHECK_EQ(packet->size(), 12u);
+
+                    int32_t counter = U32_AT(packet->data());
+                    int64_t timeUs = U64_AT(packet->data() + 4);
+
+                    if (mTimeOffsetValid) {
+                        timeUs -= mTimeOffsetUs;
+                        int64_t nowUs = ALooper::GetNowUs();
+                        int64_t delayMs = (nowUs - timeUs) / 1000ll;
+
+                        dumpDelay(counter, delayMs);
+                    } else {
+                        ALOGI("received %d", counter);
+                    }
+                    break;
+                }
+
+                case ANetworkSession::kWhatError:
+                {
+                    ALOGE("kWhatError");
+                    break;
+                }
+
+                default:
+                    TRESPASS();
+            }
+            break;
+        }
+
+        case kWhatTimeSyncerNotify:
+        {
+            CHECK(msg->findInt64("offset", &mTimeOffsetUs));
+            mTimeOffsetValid = true;
+            break;
+        }
+
+        case kWhatSendMore:
+        {
+            uint8_t buffer[4 + 8];
+            buffer[0] = mCounter >> 24;
+            buffer[1] = (mCounter >> 16) & 0xff;
+            buffer[2] = (mCounter >> 8) & 0xff;
+            buffer[3] = mCounter & 0xff;
+
+            int64_t nowUs = ALooper::GetNowUs();
+
+            buffer[4] = nowUs >> 56;
+            buffer[5] = (nowUs >> 48) & 0xff;
+            buffer[6] = (nowUs >> 40) & 0xff;
+            buffer[7] = (nowUs >> 32) & 0xff;
+            buffer[8] = (nowUs >> 24) & 0xff;
+            buffer[9] = (nowUs >> 16) & 0xff;
+            buffer[10] = (nowUs >> 8) & 0xff;
+            buffer[11] = nowUs & 0xff;
+
+            ++mCounter;
+
+            CHECK_EQ((status_t)OK,
+                     mNetSession->sendRequest(
+                         mSessionID,
+                         buffer,
+                         sizeof(buffer),
+                         true /* timeValid */,
+                         nowUs));
+
+            msg->post(100000ll);
+            break;
+        }
+
+        case kWhatStop:
+        {
+            if (mSessionID != 0) {
+                mNetSession->destroySession(mSessionID);
+                mSessionID = 0;
+            }
+
+            if (mServerSessionID != 0) {
+                mNetSession->destroySession(mServerSessionID);
+                mServerSessionID = 0;
+            }
+
+            looper()->stop();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
+}  // namespace android
+
+static void usage(const char *me) {
+    fprintf(stderr,
+            "usage: %s -c host:port\tconnect to remote host\n"
+            "               -l port   \tlisten\n",
+            me);
+}
+
+int main(int argc, char **argv) {
+    using namespace android;
+
+    // srand(time(NULL));
+
+    ProcessState::self()->startThreadPool();
+
+    DataSource::RegisterDefaultSniffers();
+
+    int32_t connectToPort = -1;
+    AString connectToHost;
+
+    int32_t listenOnPort = -1;
+
+    int res;
+    while ((res = getopt(argc, argv, "hc:l:")) >= 0) {
+        switch (res) {
+            case 'c':
+            {
+                const char *colonPos = strrchr(optarg, ':');
+
+                if (colonPos == NULL) {
+                    usage(argv[0]);
+                    exit(1);
+                }
+
+                connectToHost.setTo(optarg, colonPos - optarg);
+
+                char *end;
+                connectToPort = strtol(colonPos + 1, &end, 10);
+
+                if (*end != '\0' || end == colonPos + 1
+                        || connectToPort < 0 || connectToPort > 65535) {
+                    fprintf(stderr, "Illegal port specified.\n");
+                    exit(1);
+                }
+                break;
+            }
+
+            case 'l':
+            {
+                char *end;
+                listenOnPort = strtol(optarg, &end, 10);
+
+                if (*end != '\0' || end == optarg
+                        || listenOnPort < 0 || listenOnPort > 65535) {
+                    fprintf(stderr, "Illegal port specified.\n");
+                    exit(1);
+                }
+                break;
+            }
+
+            case '?':
+            case 'h':
+                usage(argv[0]);
+                exit(1);
+        }
+    }
+
+    if ((listenOnPort < 0 && connectToPort < 0)
+            || (listenOnPort >= 0 && connectToPort >= 0)) {
+        fprintf(stderr,
+                "You need to select either client or server mode.\n");
+        exit(1);
+    }
+
+    sp<ANetworkSession> netSession = new ANetworkSession;
+    netSession->start();
+
+    sp<ALooper> looper = new ALooper;
+
+    sp<TestHandler> handler = new TestHandler(netSession);
+    looper->registerHandler(handler);
+
+    if (listenOnPort) {
+        handler->listen(listenOnPort);
+    }
+
+    if (connectToPort >= 0) {
+        handler->connect(connectToHost.c_str(), connectToPort);
+    }
+
+    looper->start(true /* runOnCallingThread */);
+
+    return 0;
+}
diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
index 5f189e7..7a96081 100644
--- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp
@@ -40,7 +40,6 @@
     notify->setInt32("followsDiscontinuity", followsDiscontinuity);
     notify->post();
 }
-
 ////////////////////////////////////////////////////////////////////////////////
 
 RTPReceiver::TSAssembler::TSAssembler(const sp<AMessage> &notify)
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
index e3fa845..6178f00 100644
--- a/media/libstagefright/wifi-display/rtp/RTPBase.h
+++ b/media/libstagefright/wifi-display/rtp/RTPBase.h
@@ -25,6 +25,7 @@
         PACKETIZATION_TRANSPORT_STREAM,
         PACKETIZATION_H264,
         PACKETIZATION_AAC,
+        PACKETIZATION_NONE,
     };
 
     enum TransportMode {
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
index 238fb82..8fa1dae 100644
--- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp
@@ -30,11 +30,13 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/Utils.h>
 
+#define TRACK_PACKET_LOSS       0
+
 namespace android {
 
 ////////////////////////////////////////////////////////////////////////////////
 
-struct RTPReceiver::Source : public RefBase {
+struct RTPReceiver::Source : public AHandler {
     Source(RTPReceiver *receiver, uint32_t ssrc);
 
     void onPacketReceived(uint16_t seq, const sp<ABuffer> &buffer);
@@ -44,7 +46,14 @@
 protected:
     virtual ~Source();
 
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
 private:
+    enum {
+        kWhatRetransmit,
+        kWhatDeclareLost,
+    };
+
     static const uint32_t kMinSequential = 2;
     static const uint32_t kMaxDropout = 3000;
     static const uint32_t kMaxMisorder = 100;
@@ -67,6 +76,17 @@
     // Ordered by extended seq number.
     List<sp<ABuffer> > mPackets;
 
+    enum StatusBits {
+        STATUS_DECLARED_LOST            = 1,
+        STATUS_REQUESTED_RETRANSMISSION = 2,
+        STATUS_ARRIVED_LATE             = 4,
+    };
+#if TRACK_PACKET_LOSS
+    KeyedVector<int32_t, uint32_t> mLostPackets;
+#endif
+
+    void modifyPacketStatus(int32_t extSeqNo, uint32_t mask);
+
     int32_t mAwaitingExtSeqNo;
     bool mRequestedRetransmission;
 
@@ -78,12 +98,20 @@
     int32_t mNumDeclaredLost;
     int32_t mNumDeclaredLostPrior;
 
+    int32_t mRetransmitGeneration;
+    int32_t mDeclareLostGeneration;
+    bool mDeclareLostTimerPending;
+
     void queuePacket(const sp<ABuffer> &packet);
     void dequeueMore();
 
     sp<ABuffer> getNextPacket();
     void resync();
 
+    void postRetransmitTimer(int64_t delayUs);
+    void postDeclareLostTimer(int64_t delayUs);
+    void cancelTimers();
+
     DISALLOW_EVIL_CONSTRUCTORS(Source);
 };
 
@@ -106,12 +134,71 @@
       mActivePacketType(-1),
       mNextReportTimeUs(-1ll),
       mNumDeclaredLost(0),
-      mNumDeclaredLostPrior(0) {
+      mNumDeclaredLostPrior(0),
+      mRetransmitGeneration(0),
+      mDeclareLostGeneration(0),
+      mDeclareLostTimerPending(false) {
 }
 
 RTPReceiver::Source::~Source() {
 }
 
+void RTPReceiver::Source::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatRetransmit:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mRetransmitGeneration) {
+                break;
+            }
+
+            mRequestedRetransmission = true;
+            mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
+
+            modifyPacketStatus(
+                    mAwaitingExtSeqNo, STATUS_REQUESTED_RETRANSMISSION);
+            break;
+        }
+
+        case kWhatDeclareLost:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mDeclareLostGeneration) {
+                break;
+            }
+
+            cancelTimers();
+
+            ALOGV("Lost packet extSeqNo %d %s",
+                  mAwaitingExtSeqNo,
+                  mRequestedRetransmission ? "*" : "");
+
+            mRequestedRetransmission = false;
+            if (mActiveAssembler != NULL) {
+                mActiveAssembler->signalDiscontinuity();
+            }
+
+            modifyPacketStatus(mAwaitingExtSeqNo, STATUS_DECLARED_LOST);
+
+            // resync();
+            ++mAwaitingExtSeqNo;
+            ++mNumDeclaredLost;
+
+            mReceiver->notifyPacketLost();
+
+            dequeueMore();
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+}
+
 void RTPReceiver::Source::onPacketReceived(
         uint16_t seq, const sp<ABuffer> &buffer) {
     if (mFirst) {
@@ -164,6 +251,8 @@
     if (mAwaitingExtSeqNo >= 0 && newExtendedSeqNo < mAwaitingExtSeqNo) {
         // We're no longer interested in these. They're old.
         ALOGV("dropping stale extSeqNo %d", newExtendedSeqNo);
+
+        modifyPacketStatus(newExtendedSeqNo, STATUS_ARRIVED_LATE);
         return;
     }
 
@@ -230,68 +319,34 @@
         }
 
         mNextReportTimeUs = nowUs + kReportIntervalUs;
+
+#if TRACK_PACKET_LOSS
+        for (size_t i = 0; i < mLostPackets.size(); ++i) {
+            int32_t key = mLostPackets.keyAt(i);
+            uint32_t value = mLostPackets.valueAt(i);
+
+            AString status;
+            if (value & STATUS_REQUESTED_RETRANSMISSION) {
+                status.append("retrans ");
+            }
+            if (value & STATUS_ARRIVED_LATE) {
+                status.append("arrived-late ");
+            }
+            ALOGI("Packet %d declared lost %s", key, status.c_str());
+        }
+#endif
     }
 
-    for (;;) {
-        sp<ABuffer> packet = getNextPacket();
-
-        if (packet == NULL) {
-            if (mPackets.empty()) {
-                break;
-            }
-
-            CHECK_GE(mAwaitingExtSeqNo, 0);
-
-            const sp<ABuffer> &firstPacket = *mPackets.begin();
-
-            uint32_t rtpTime;
-            CHECK(firstPacket->meta()->findInt32(
-                        "rtp-time", (int32_t *)&rtpTime));
-
-
-            int64_t rtpUs = (rtpTime * 100ll) / 9ll;
-
-            int64_t maxArrivalTimeUs =
-                mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
-
-            int64_t nowUs = ALooper::GetNowUs();
-
-            CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
-
-            ALOGV("waiting for %d, comparing against %d, %lld us left",
-                  mAwaitingExtSeqNo,
-                  firstPacket->int32Data(),
-                  maxArrivalTimeUs - nowUs);
-
-            if (maxArrivalTimeUs + kPacketLostAfterUs <= nowUs) {
-                ALOGV("Lost packet extSeqNo %d %s",
-                      mAwaitingExtSeqNo,
-                      mRequestedRetransmission ? "*" : "");
-
-                mRequestedRetransmission = false;
-                if (mActiveAssembler != NULL) {
-                    mActiveAssembler->signalDiscontinuity();
-                }
-
-                // resync();
-                ++mAwaitingExtSeqNo;
-                ++mNumDeclaredLost;
-
-                mReceiver->notifyPacketLost();
-                continue;
-            } else if (kRequestRetransmissionAfterUs > 0
-                    && maxArrivalTimeUs + kRequestRetransmissionAfterUs <= nowUs
-                    && !mRequestedRetransmission
-                    && mAwaitingExtSeqNo >= 0) {
-                mRequestedRetransmission = true;
-                mReceiver->requestRetransmission(mSSRC, mAwaitingExtSeqNo);
-                break;
-            } else {
-                break;
-            }
+    sp<ABuffer> packet;
+    while ((packet = getNextPacket()) != NULL) {
+        if (mDeclareLostTimerPending) {
+            cancelTimers();
         }
 
-        mRequestedRetransmission = false;
+        CHECK_GE(mAwaitingExtSeqNo, 0);
+#if TRACK_PACKET_LOSS
+        mLostPackets.removeItem(mAwaitingExtSeqNo);
+#endif
 
         int32_t packetType;
         CHECK(packet->meta()->findInt32("PT", &packetType));
@@ -301,14 +356,52 @@
             mActivePacketType = packetType;
         }
 
-        if (mActiveAssembler == NULL) {
-            continue;
+        if (mActiveAssembler != NULL) {
+            status_t err = mActiveAssembler->processPacket(packet);
+            if (err != OK) {
+                ALOGV("assembler returned error %d", err);
+            }
         }
 
-        status_t err = mActiveAssembler->processPacket(packet);
-        if (err != OK) {
-            ALOGV("assembler returned error %d", err);
-        }
+        ++mAwaitingExtSeqNo;
+    }
+
+    if (mDeclareLostTimerPending) {
+        return;
+    }
+
+    if (mPackets.empty()) {
+        return;
+    }
+
+    CHECK_GE(mAwaitingExtSeqNo, 0);
+
+    const sp<ABuffer> &firstPacket = *mPackets.begin();
+
+    uint32_t rtpTime;
+    CHECK(firstPacket->meta()->findInt32(
+                "rtp-time", (int32_t *)&rtpTime));
+
+
+    int64_t rtpUs = (rtpTime * 100ll) / 9ll;
+
+    int64_t maxArrivalTimeUs =
+        mFirstArrivalTimeUs + rtpUs - mFirstRTPTimeUs;
+
+    nowUs = ALooper::GetNowUs();
+
+    CHECK_LT(mAwaitingExtSeqNo, firstPacket->int32Data());
+
+    ALOGV("waiting for %d, comparing against %d, %lld us left",
+          mAwaitingExtSeqNo,
+          firstPacket->int32Data(),
+          maxArrivalTimeUs - nowUs);
+
+    postDeclareLostTimer(maxArrivalTimeUs + kPacketLostAfterUs);
+
+    if (kRequestRetransmissionAfterUs > 0ll) {
+        postRetransmitTimer(
+                maxArrivalTimeUs + kRequestRetransmissionAfterUs);
     }
 }
 
@@ -328,8 +421,6 @@
     sp<ABuffer> packet = *mPackets.begin();
     mPackets.erase(mPackets.begin());
 
-    ++mAwaitingExtSeqNo;
-
     return packet;
 }
 
@@ -404,9 +495,11 @@
 
 RTPReceiver::RTPReceiver(
         const sp<ANetworkSession> &netSession,
-        const sp<AMessage> &notify)
+        const sp<AMessage> &notify,
+        uint32_t flags)
     : mNetSession(netSession),
       mNotify(notify),
+      mFlags(flags),
       mRTPMode(TRANSPORT_UNDEFINED),
       mRTCPMode(TRANSPORT_UNDEFINED),
       mRTPSessionID(0),
@@ -693,6 +786,20 @@
             CHECK(msg->findBuffer("data", &data));
 
             if (isRTP) {
+                if (mFlags & FLAG_AUTO_CONNECT) {
+                    AString fromAddr;
+                    CHECK(msg->findString("fromAddr", &fromAddr));
+
+                    int32_t fromPort;
+                    CHECK(msg->findInt32("fromPort", &fromPort));
+
+                    CHECK_EQ((status_t)OK,
+                             connect(
+                                 fromAddr.c_str(), fromPort, fromPort + 1));
+
+                    mFlags &= ~FLAG_AUTO_CONNECT;
+                }
+
                 onRTPData(data);
             } else {
                 onRTCPData(data);
@@ -835,6 +942,8 @@
     sp<Source> source;
     if (index < 0) {
         source = new Source(this, srcId);
+        looper()->registerHandler(source);
+
         mSources.add(srcId, source);
     } else {
         source = mSources.valueAt(index);
@@ -965,6 +1074,7 @@
     PacketizationMode mode = mPacketTypes.valueAt(index);
 
     switch (mode) {
+        case PACKETIZATION_NONE:
         case PACKETIZATION_TRANSPORT_STREAM:
             return new TSAssembler(mNotify);
 
@@ -1005,5 +1115,39 @@
      mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
 }
 
+void RTPReceiver::Source::modifyPacketStatus(int32_t extSeqNo, uint32_t mask) {
+#if TRACK_PACKET_LOSS
+    ssize_t index = mLostPackets.indexOfKey(extSeqNo);
+    if (index < 0) {
+        mLostPackets.add(extSeqNo, mask);
+    } else {
+        mLostPackets.editValueAt(index) |= mask;
+    }
+#endif
+}
+
+void RTPReceiver::Source::postRetransmitTimer(int64_t timeUs) {
+    int64_t delayUs = timeUs - ALooper::GetNowUs();
+    sp<AMessage> msg = new AMessage(kWhatRetransmit, id());
+    msg->setInt32("generation", mRetransmitGeneration);
+    msg->post(delayUs);
+}
+
+void RTPReceiver::Source::postDeclareLostTimer(int64_t timeUs) {
+    CHECK(!mDeclareLostTimerPending);
+    mDeclareLostTimerPending = true;
+
+    int64_t delayUs = timeUs - ALooper::GetNowUs();
+    sp<AMessage> msg = new AMessage(kWhatDeclareLost, id());
+    msg->setInt32("generation", mDeclareLostGeneration);
+    msg->post(delayUs);
+}
+
+void RTPReceiver::Source::cancelTimers() {
+    ++mRetransmitGeneration;
+    ++mDeclareLostGeneration;
+    mDeclareLostTimerPending = false;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
index 630bce9..240ab2e 100644
--- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h
+++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h
@@ -39,9 +39,14 @@
         kWhatAccessUnit,
         kWhatPacketLost,
     };
+
+    enum Flags {
+        FLAG_AUTO_CONNECT = 1,
+    };
     RTPReceiver(
             const sp<ANetworkSession> &netSession,
-            const sp<AMessage> &notify);
+            const sp<AMessage> &notify,
+            uint32_t flags = 0);
 
     status_t registerPacketType(
             uint8_t packetType, PacketizationMode mode);
@@ -82,6 +87,7 @@
 
     sp<ANetworkSession> mNetSession;
     sp<AMessage> mNotify;
+    uint32_t mFlags;
     TransportMode mRTPMode;
     TransportMode mRTCPMode;
     int32_t mRTPSessionID;
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index 9eeeabd..6bbe650 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -187,6 +187,10 @@
     status_t err;
 
     switch (mode) {
+        case PACKETIZATION_NONE:
+            err = queueRawPacket(buffer, packetType);
+            break;
+
         case PACKETIZATION_TRANSPORT_STREAM:
             err = queueTSPackets(buffer, packetType);
             break;
@@ -202,6 +206,46 @@
     return err;
 }
 
+status_t RTPSender::queueRawPacket(
+        const sp<ABuffer> &packet, uint8_t packetType) {
+    CHECK_LE(packet->size(), kMaxUDPPacketSize - 12);
+
+    int64_t timeUs;
+    CHECK(packet->meta()->findInt64("timeUs", &timeUs));
+
+    sp<ABuffer> udpPacket = new ABuffer(12 + packet->size());
+
+    udpPacket->setInt32Data(mRTPSeqNo);
+
+    uint8_t *rtp = udpPacket->data();
+    rtp[0] = 0x80;
+    rtp[1] = packetType;
+
+    rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+    rtp[3] = mRTPSeqNo & 0xff;
+    ++mRTPSeqNo;
+
+    uint32_t rtpTime = (timeUs * 9) / 100ll;
+
+    rtp[4] = rtpTime >> 24;
+    rtp[5] = (rtpTime >> 16) & 0xff;
+    rtp[6] = (rtpTime >> 8) & 0xff;
+    rtp[7] = rtpTime & 0xff;
+
+    rtp[8] = kSourceID >> 24;
+    rtp[9] = (kSourceID >> 16) & 0xff;
+    rtp[10] = (kSourceID >> 8) & 0xff;
+    rtp[11] = kSourceID & 0xff;
+
+    memcpy(&rtp[12], packet->data(), packet->size());
+
+    return sendRTPPacket(
+            udpPacket,
+            true /* storeInHistory */,
+            true /* timeValid */,
+            ALooper::GetNowUs());
+}
+
 status_t RTPSender::queueTSPackets(
         const sp<ABuffer> &tsPackets, uint8_t packetType) {
     CHECK_EQ(0, tsPackets->size() % 188);
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
index 3a926ea..fefcab7 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.h
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.h
@@ -94,6 +94,7 @@
 
     static uint64_t GetNowNTP();
 
+    status_t queueRawPacket(const sp<ABuffer> &tsPackets, uint8_t packetType);
     status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
     status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
 
diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp
index eade832..764a38b 100644
--- a/media/libstagefright/wifi-display/rtptest.cpp
+++ b/media/libstagefright/wifi-display/rtptest.cpp
@@ -21,6 +21,7 @@
 #include "ANetworkSession.h"
 #include "rtp/RTPSender.h"
 #include "rtp/RTPReceiver.h"
+#include "TimeSyncer.h"
 
 #include <binder/ProcessState.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -28,12 +29,116 @@
 #include <media/stagefright/foundation/AHandler.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NuMediaExtractor.h>
+#include <media/stagefright/Utils.h>
+
+#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4"
 
 namespace android {
 
+struct PacketSource : public RefBase {
+    PacketSource() {}
+
+    virtual sp<ABuffer> getNextAccessUnit() = 0;
+
+protected:
+    virtual ~PacketSource() {}
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(PacketSource);
+};
+
+struct MediaPacketSource : public PacketSource {
+    MediaPacketSource()
+        : mMaxSampleSize(1024 * 1024) {
+        mExtractor = new NuMediaExtractor;
+        CHECK_EQ((status_t)OK,
+                 mExtractor->setDataSource(MEDIA_FILENAME));
+
+        bool haveVideo = false;
+        for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+            sp<AMessage> format;
+            CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
+
+            AString mime;
+            CHECK(format->findString("mime", &mime));
+
+            if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
+                mExtractor->selectTrack(i);
+                haveVideo = true;
+                break;
+            }
+        }
+
+        CHECK(haveVideo);
+    }
+
+    virtual sp<ABuffer> getNextAccessUnit() {
+        int64_t timeUs;
+        status_t err = mExtractor->getSampleTime(&timeUs);
+
+        if (err != OK) {
+            return NULL;
+        }
+
+        sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
+        CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
+
+        accessUnit->meta()->setInt64("timeUs", timeUs);
+
+        CHECK_EQ((status_t)OK, mExtractor->advance());
+
+        return accessUnit;
+    }
+
+protected:
+    virtual ~MediaPacketSource() {
+    }
+
+private:
+    sp<NuMediaExtractor> mExtractor;
+    size_t mMaxSampleSize;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource);
+};
+
+struct SimplePacketSource : public PacketSource {
+    SimplePacketSource()
+        : mCounter(0) {
+    }
+
+    virtual sp<ABuffer> getNextAccessUnit() {
+        sp<ABuffer> buffer = new ABuffer(4);
+        uint8_t *dst = buffer->data();
+        dst[0] = mCounter >> 24;
+        dst[1] = (mCounter >> 16) & 0xff;
+        dst[2] = (mCounter >> 8) & 0xff;
+        dst[3] = mCounter & 0xff;
+
+        buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate);
+
+        ++mCounter;
+
+        return buffer;
+    }
+
+protected:
+    virtual ~SimplePacketSource() {
+    }
+
+private:
+    enum {
+        kFrameRate = 30
+    };
+
+    uint32_t mCounter;
+
+    DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource);
+};
+
 struct TestHandler : public AHandler {
     TestHandler(const sp<ANetworkSession> &netSession);
 
@@ -52,18 +157,39 @@
         kWhatSenderNotify,
         kWhatSendMore,
         kWhatStop,
+        kWhatTimeSyncerNotify,
     };
 
+#if 1
+    static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP;
+    static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP;
+#else
+    static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP;
+    static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE;
+#endif
+
+#if 1
+    static const RTPBase::PacketizationMode kPacketizationMode
+        = RTPBase::PACKETIZATION_H264;
+#else
+    static const RTPBase::PacketizationMode kPacketizationMode
+        = RTPBase::PACKETIZATION_NONE;
+#endif
+
     sp<ANetworkSession> mNetSession;
-    sp<NuMediaExtractor> mExtractor;
+    sp<PacketSource> mSource;
     sp<RTPSender> mSender;
     sp<RTPReceiver> mReceiver;
 
-    size_t mMaxSampleSize;
+    sp<TimeSyncer> mTimeSyncer;
+    bool mTimeSyncerStarted;
 
     int64_t mFirstTimeRealUs;
     int64_t mFirstTimeMediaUs;
 
+    int64_t mTimeOffsetUs;
+    bool mTimeOffsetValid;
+
     status_t readMore();
 
     DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
@@ -71,9 +197,11 @@
 
 TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
     : mNetSession(netSession),
-      mMaxSampleSize(1024 * 1024),
+      mTimeSyncerStarted(false),
       mFirstTimeRealUs(-1ll),
-      mFirstTimeMediaUs(-1ll) {
+      mFirstTimeMediaUs(-1ll),
+      mTimeOffsetUs(-1ll),
+      mTimeOffsetValid(false) {
 }
 
 TestHandler::~TestHandler() {
@@ -91,23 +219,48 @@
     msg->post();
 }
 
+static void dumpDelay(int64_t delayMs) {
+    static const int64_t kMinDelayMs = 0;
+    static const int64_t kMaxDelayMs = 300;
+
+    const char *kPattern = "########################################";
+    size_t kPatternSize = strlen(kPattern);
+
+    int n = (kPatternSize * (delayMs - kMinDelayMs))
+                / (kMaxDelayMs - kMinDelayMs);
+
+    if (n < 0) {
+        n = 0;
+    } else if ((size_t)n > kPatternSize) {
+        n = kPatternSize;
+    }
+
+    ALOGI("(%4lld ms) %s\n",
+          delayMs,
+          kPattern + kPatternSize - n);
+}
+
 void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatListen:
         {
-            sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
-            mReceiver = new RTPReceiver(mNetSession, notify);
+            sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+            mTimeSyncer = new TimeSyncer(mNetSession, notify);
+            looper()->registerHandler(mTimeSyncer);
+
+            notify = new AMessage(kWhatReceiverNotify, id());
+            mReceiver = new RTPReceiver(
+                    mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT);
             looper()->registerHandler(mReceiver);
 
             CHECK_EQ((status_t)OK,
-                     mReceiver->registerPacketType(
-                         33, RTPReceiver::PACKETIZATION_H264));
+                     mReceiver->registerPacketType(33, kPacketizationMode));
 
             int32_t receiverRTPPort;
             CHECK_EQ((status_t)OK,
                      mReceiver->initAsync(
-                         RTPReceiver::TRANSPORT_UDP,  // rtpMode
-                         RTPReceiver::TRANSPORT_UDP,  // rtcpMode
+                         kRTPMode,
+                         kRTCPMode,
                          &receiverRTPPort));
 
             printf("picked receiverRTPPort %d\n", receiverRTPPort);
@@ -125,33 +278,23 @@
             AString host;
             CHECK(msg->findString("host", &host));
 
+            sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+            mTimeSyncer = new TimeSyncer(mNetSession, notify);
+            looper()->registerHandler(mTimeSyncer);
+            mTimeSyncer->startServer(8123);
+
             int32_t receiverRTPPort;
             CHECK(msg->findInt32("port", &receiverRTPPort));
 
-            mExtractor = new NuMediaExtractor;
-            CHECK_EQ((status_t)OK,
-                     mExtractor->setDataSource(
-                             "/sdcard/Frame Counter HD 30FPS_1080p.mp4"));
+#if 1
+            mSource = new MediaPacketSource;
+#else
+            mSource = new SimplePacketSource;
+#endif
 
-            bool haveVideo = false;
-            for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
-                sp<AMessage> format;
-                CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
-
-                AString mime;
-                CHECK(format->findString("mime", &mime));
-
-                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
-                    mExtractor->selectTrack(i);
-                    haveVideo = true;
-                    break;
-                }
-            }
-
-            CHECK(haveVideo);
-
-            sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+            notify = new AMessage(kWhatSenderNotify, id());
             mSender = new RTPSender(mNetSession, notify);
+
             looper()->registerHandler(mSender);
 
             int32_t senderRTPPort;
@@ -159,9 +302,10 @@
                      mSender->initAsync(
                          host.c_str(),
                          receiverRTPPort,
-                         RTPSender::TRANSPORT_UDP,  // rtpMode
-                         receiverRTPPort + 1,
-                         RTPSender::TRANSPORT_UDP,  // rtcpMode
+                         kRTPMode,
+                         kRTCPMode == RTPBase::TRANSPORT_NONE
+                            ? -1 : receiverRTPPort + 1,
+                         kRTCPMode,
                          &senderRTPPort));
 
             printf("picked senderRTPPort %d\n", senderRTPPort);
@@ -201,7 +345,7 @@
 
         case kWhatReceiverNotify:
         {
-            ALOGI("kWhatReceiverNotify");
+            ALOGV("kWhatReceiverNotify");
 
             int32_t what;
             CHECK(msg->findInt32("what", &what));
@@ -216,8 +360,40 @@
                     break;
                 }
 
-                case RTPSender::kWhatError:
+                case RTPReceiver::kWhatError:
                     break;
+
+                case RTPReceiver::kWhatAccessUnit:
+                {
+#if 0
+                    if (!mTimeSyncerStarted) {
+                        mTimeSyncer->startClient("172.18.41.216", 8123);
+                        mTimeSyncerStarted = true;
+                    }
+
+                    sp<ABuffer> accessUnit;
+                    CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+                    int64_t timeUs;
+                    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+                    if (mTimeOffsetValid) {
+                        timeUs -= mTimeOffsetUs;
+                        int64_t nowUs = ALooper::GetNowUs();
+                        int64_t delayMs = (nowUs - timeUs) / 1000ll;
+
+                        dumpDelay(delayMs);
+                    }
+#endif
+                    break;
+                }
+
+                case RTPReceiver::kWhatPacketLost:
+                    ALOGV("kWhatPacketLost");
+                    break;
+
+                default:
+                    TRESPASS();
             }
             break;
         }
@@ -231,7 +407,7 @@
                      mSender->queueBuffer(
                          accessUnit,
                          33,
-                         RTPSender::PACKETIZATION_H264));
+                         kPacketizationMode));
 
             status_t err = readMore();
 
@@ -253,31 +429,33 @@
                 mSender.clear();
             }
 
-            mExtractor.clear();
+            mSource.clear();
 
             looper()->stop();
             break;
         }
 
+        case kWhatTimeSyncerNotify:
+        {
+            CHECK(msg->findInt64("offset", &mTimeOffsetUs));
+            mTimeOffsetValid = true;
+            break;
+        }
+
         default:
             TRESPASS();
     }
 }
 
 status_t TestHandler::readMore() {
-    int64_t timeUs;
-    status_t err = mExtractor->getSampleTime(&timeUs);
+    sp<ABuffer> accessUnit = mSource->getNextAccessUnit();
 
-    if (err != OK) {
-        return err;
+    if (accessUnit == NULL) {
+        return ERROR_END_OF_STREAM;
     }
 
-    sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
-    CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
-
-    accessUnit->meta()->setInt64("timeUs", timeUs);
-
-    CHECK_EQ((status_t)OK, mExtractor->advance());
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
 
     int64_t nowUs = ALooper::GetNowUs();
     int64_t whenUs;
@@ -289,6 +467,8 @@
         whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
     }
 
+    accessUnit->meta()->setInt64("timeUs", whenUs);
+
     sp<AMessage> msg = new AMessage(kWhatSendMore, id());
     msg->setBuffer("accessUnit", accessUnit);
     msg->post(whenUs - nowUs);
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
index 1a08bf5..5db2099 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
@@ -57,7 +57,8 @@
       mSetupDeferred(false),
       mLatencyCount(0),
       mLatencySumUs(0ll),
-      mLatencyMaxUs(0ll) {
+      mLatencyMaxUs(0ll),
+      mMaxDelayMs(-1ll) {
     // We support any and all resolutions, but prefer 720p30
     mSinkSupportedVideoFormats.setNativeResolution(
             VideoFormats::RESOLUTION_CEA, 5);  // 1280 x 720 p30
@@ -296,9 +297,13 @@
     }
 }
 
-static void dumpDelay(size_t trackIndex, int64_t timeUs) {
+void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) {
     int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll;
 
+    if (delayMs > mMaxDelayMs) {
+        mMaxDelayMs = delayMs;
+    }
+
     static const int64_t kMinDelayMs = 0;
     static const int64_t kMaxDelayMs = 300;
 
@@ -314,9 +319,10 @@
         n = kPatternSize;
     }
 
-    ALOGI("[%lld]: (%4lld ms) %s",
+    ALOGI("[%lld]: (%4lld ms / %4lld ms) %s",
           timeUs / 1000,
           delayMs,
+          mMaxDelayMs,
           kPattern + kPatternSize - n);
 }
 
@@ -350,14 +356,19 @@
                 looper()->registerHandler(mRenderer);
             }
 
-            CHECK(mTimeOffsetValid);
-
             sp<ABuffer> accessUnit;
             CHECK(msg->findBuffer("accessUnit", &accessUnit));
 
             int64_t timeUs;
             CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
 
+            if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) {
+                mTimeOffsetUs = timeUs - ALooper::GetNowUs();
+                mTimeOffsetValid = true;
+            }
+
+            CHECK(mTimeOffsetValid);
+
             // We are the timesync _client_,
             // client time = server time - time offset.
             timeUs -= mTimeOffsetUs;
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
index 7c62057..adb9d89 100644
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
@@ -132,6 +132,8 @@
     int64_t mLatencySumUs;
     int64_t mLatencyMaxUs;
 
+    int64_t mMaxDelayMs;
+
     status_t sendM2(int32_t sessionID);
     status_t sendSetup(int32_t sessionID, const char *uri);
     status_t sendPlay(int32_t sessionID, const char *uri);
@@ -184,6 +186,8 @@
             const char *url, AString *host, int32_t *port, AString *path,
             AString *user, AString *pass);
 
+    void dumpDelay(size_t trackIndex, int64_t timeUs);
+
     DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink);
 };
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 715d0b5..cacfcca 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -709,8 +709,11 @@
                 Converter::GetInt32Property(
                         "media.wfd.video-framerate", -1);
 
-            if (rateHz < 0.0) {
-                rateHz = repeaterSource->getFrameRate();
+            char val[PROPERTY_VALUE_MAX];
+            if (rateHz < 0.0
+                    && property_get("media.wfd.video-framerate", val, NULL)
+                    && !strcasecmp("adaptive", val)) {
+                 rateHz = repeaterSource->getFrameRate();
 
                 if (avgLatencyUs > 300000ll) {
                     rateHz *= 0.9;
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
index a4253f6..b3f7b1b 100644
--- a/media/libstagefright/yuv/Android.mk
+++ b/media/libstagefright/yuv/Android.mk
@@ -6,7 +6,8 @@
         YUVCanvas.cpp
 
 LOCAL_SHARED_LIBRARIES :=       \
-        libcutils
+        libcutils \
+        liblog
 
 LOCAL_MODULE:= libstagefright_yuv
 
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index a485646..1ac647a 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -22,6 +22,7 @@
 	libmedia \
 	libmediaplayerservice \
 	libutils \
+	liblog \
 	libbinder
 
 LOCAL_STATIC_LIBRARIES := \
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index bee28d4..ac608a1 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -42,6 +42,6 @@
 # Needed for <bionic_time.h>
 LOCAL_C_INCLUDES := bionic/libc/private
 
-LOCAL_SHARED_LIBRARIES := libutils libcutils libusbhost libbinder
+LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libusbhost libbinder
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 7806f48..061a079 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -22,7 +22,7 @@
     AudioResampler.cpp.arm      \
     AudioPolicyService.cpp      \
     ServiceUtilities.cpp        \
-	AudioResamplerCubic.cpp.arm \
+    AudioResamplerCubic.cpp.arm \
     AudioResamplerSinc.cpp.arm
 
 LOCAL_SRC_FILES += StateQueue.cpp
@@ -39,6 +39,7 @@
     libcommon_time_client \
     libcutils \
     libutils \
+    liblog \
     libbinder \
     libmedia \
     libnbaio \
@@ -94,9 +95,10 @@
     AudioResamplerSinc.cpp.arm
 
 LOCAL_SHARED_LIBRARIES := \
-	libdl \
+    libdl \
     libcutils \
-    libutils
+    libutils \
+    liblog
 
 LOCAL_MODULE:= test-resample
 
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 74ba59e..d66294c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -225,12 +225,18 @@
                    0,
                    mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
         }
-        start_l();
-        mState = ACTIVE;
+        if (start_l() == NO_ERROR) {
+            mState = ACTIVE;
+        } else {
+            mState = IDLE;
+        }
         break;
     case STOPPING:
-        stop_l();
-        mDisableWaitCnt = mMaxDisableWaitCnt;
+        if (stop_l() == NO_ERROR) {
+            mDisableWaitCnt = mMaxDisableWaitCnt;
+        } else {
+            mDisableWaitCnt = 1; // will cause immediate transition to IDLE
+        }
         mState = STOPPED;
         break;
     case STOPPED:
@@ -297,7 +303,7 @@
 
 void AudioFlinger::EffectModule::reset_l()
 {
-    if (mEffectInterface == NULL) {
+    if (mStatus != NO_ERROR || mEffectInterface == NULL) {
         return;
     }
     (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
@@ -305,17 +311,24 @@
 
 status_t AudioFlinger::EffectModule::configure()
 {
+    status_t status;
+    sp<ThreadBase> thread;
+    uint32_t size;
+    audio_channel_mask_t channelMask;
+
     if (mEffectInterface == NULL) {
-        return NO_INIT;
+        status = NO_INIT;
+        goto exit;
     }
 
-    sp<ThreadBase> thread = mThread.promote();
+    thread = mThread.promote();
     if (thread == 0) {
-        return DEAD_OBJECT;
+        status = DEAD_OBJECT;
+        goto exit;
     }
 
     // TODO: handle configuration of effects replacing track process
-    audio_channel_mask_t channelMask = thread->channelMask();
+    channelMask = thread->channelMask();
 
     if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
         mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
@@ -357,8 +370,8 @@
             this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
 
     status_t cmdStatus;
-    uint32_t size = sizeof(int);
-    status_t status = (*mEffectInterface)->command(mEffectInterface,
+    size = sizeof(int);
+    status = (*mEffectInterface)->command(mEffectInterface,
                                                    EFFECT_CMD_SET_CONFIG,
                                                    sizeof(effect_config_t),
                                                    &mConfig,
@@ -396,6 +409,8 @@
     mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
             (1000 * mConfig.outputCfg.buffer.frameCount);
 
+exit:
+    mStatus = status;
     return status;
 }
 
@@ -430,6 +445,9 @@
     if (mEffectInterface == NULL) {
         return NO_INIT;
     }
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t cmdStatus;
     uint32_t size = sizeof(status_t);
     status_t status = (*mEffectInterface)->command(mEffectInterface,
@@ -466,6 +484,9 @@
     if (mEffectInterface == NULL) {
         return NO_INIT;
     }
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t cmdStatus;
     uint32_t size = sizeof(status_t);
     status_t status = (*mEffectInterface)->command(mEffectInterface,
@@ -503,6 +524,9 @@
     if (mState == DESTROYED || mEffectInterface == NULL) {
         return NO_INIT;
     }
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t status = (*mEffectInterface)->command(mEffectInterface,
                                                    cmdCode,
                                                    cmdSize,
@@ -592,6 +616,10 @@
 
 bool AudioFlinger::EffectModule::isProcessEnabled() const
 {
+    if (mStatus != NO_ERROR) {
+        return false;
+    }
+
     switch (mState) {
     case RESTART:
     case ACTIVE:
@@ -609,8 +637,10 @@
 status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
 {
     Mutex::Autolock _l(mLock);
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t status = NO_ERROR;
-
     // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
     // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set)
     if (isProcessEnabled() &&
@@ -646,6 +676,9 @@
     }
 
     Mutex::Autolock _l(mLock);
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t status = NO_ERROR;
     if (device && (mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
         status_t cmdStatus;
@@ -665,6 +698,9 @@
 status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode)
 {
     Mutex::Autolock _l(mLock);
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t status = NO_ERROR;
     if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
         status_t cmdStatus;
@@ -685,6 +721,9 @@
 status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source)
 {
     Mutex::Autolock _l(mLock);
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
     status_t status = NO_ERROR;
     if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) {
         uint32_t size = 0;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 9d98f0b..47ca100 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3122,16 +3122,15 @@
     Vector< sp<Track> > *tracksToRemove
 )
 {
-    sp<Track> trackToRemove;
-
+    size_t count = mActiveTracks.size();
     mixer_state mixerStatus = MIXER_IDLE;
 
     // find out which tracks need to be processed
-    if (mActiveTracks.size() != 0) {
-        sp<Track> t = mActiveTracks[0].promote();
+    for (size_t i = 0; i < count; i++) {
+        sp<Track> t = mActiveTracks[i].promote();
         // The track died recently
         if (t == 0) {
-            return MIXER_IDLE;
+            continue;
         }
 
         Track* const track = t.get();
@@ -3180,35 +3179,40 @@
                 }
                 right = v_clamped/MAX_GAIN;
             }
+            // Only consider last track started for volume and mixer state control.
+            // This is the last entry in mActiveTracks unless a track underruns.
+            // As we only care about the transition phase between two tracks on a
+            // direct output, it is not a problem to ignore the underrun case.
+            if (i == (count - 1)) {
+                if (left != mLeftVolFloat || right != mRightVolFloat) {
+                    mLeftVolFloat = left;
+                    mRightVolFloat = right;
 
-            if (left != mLeftVolFloat || right != mRightVolFloat) {
-                mLeftVolFloat = left;
-                mRightVolFloat = right;
+                    // Convert volumes from float to 8.24
+                    uint32_t vl = (uint32_t)(left * (1 << 24));
+                    uint32_t vr = (uint32_t)(right * (1 << 24));
 
-                // Convert volumes from float to 8.24
-                uint32_t vl = (uint32_t)(left * (1 << 24));
-                uint32_t vr = (uint32_t)(right * (1 << 24));
-
-                // Delegate volume control to effect in track effect chain if needed
-                // only one effect chain can be present on DirectOutputThread, so if
-                // there is one, the track is connected to it
-                if (!mEffectChains.isEmpty()) {
-                    // Do not ramp volume if volume is controlled by effect
-                    mEffectChains[0]->setVolume_l(&vl, &vr);
-                    left = (float)vl / (1 << 24);
-                    right = (float)vr / (1 << 24);
+                    // Delegate volume control to effect in track effect chain if needed
+                    // only one effect chain can be present on DirectOutputThread, so if
+                    // there is one, the track is connected to it
+                    if (!mEffectChains.isEmpty()) {
+                        // Do not ramp volume if volume is controlled by effect
+                        mEffectChains[0]->setVolume_l(&vl, &vr);
+                        left = (float)vl / (1 << 24);
+                        right = (float)vr / (1 << 24);
+                    }
+                    mOutput->stream->set_volume(mOutput->stream, left, right);
                 }
-                mOutput->stream->set_volume(mOutput->stream, left, right);
-            }
 
-            // reset retry count
-            track->mRetryCount = kMaxTrackRetriesDirect;
-            mActiveTrack = t;
-            mixerStatus = MIXER_TRACKS_READY;
+                // reset retry count
+                track->mRetryCount = kMaxTrackRetriesDirect;
+                mActiveTrack = t;
+                mixerStatus = MIXER_TRACKS_READY;
+            }
         } else {
-            // clear effect chain input buffer if an active track underruns to avoid sending
-            // previous audio buffer again to effects
-            if (!mEffectChains.isEmpty()) {
+            // clear effect chain input buffer if the last active track started underruns
+            // to avoid sending previous audio buffer again to effects
+            if (!mEffectChains.isEmpty() && (i == (count -1))) {
                 mEffectChains[0]->clearInputBuffer();
             }
 
@@ -3224,33 +3228,36 @@
                     if (track->isStopped()) {
                         track->reset();
                     }
-                    trackToRemove = track;
+                    tracksToRemove->add(track);
                 }
             } else {
                 // No buffers for this track. Give it a few chances to
                 // fill a buffer, then remove it from active list.
+                // Only consider last track started for mixer state control
                 if (--(track->mRetryCount) <= 0) {
                     ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
-                    trackToRemove = track;
-                } else {
+                    tracksToRemove->add(track);
+                } else if (i == (count -1)){
                     mixerStatus = MIXER_TRACKS_ENABLED;
                 }
             }
         }
     }
 
-    // FIXME merge this with similar code for removing multiple tracks
     // remove all the tracks that need to be...
-    if (CC_UNLIKELY(trackToRemove != 0)) {
-        tracksToRemove->add(trackToRemove);
-        mActiveTracks.remove(trackToRemove);
-        if (!mEffectChains.isEmpty()) {
-            ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
-                    trackToRemove->sessionId());
-            mEffectChains[0]->decActiveTrackCnt();
-        }
-        if (trackToRemove->isTerminated()) {
-            removeTrack_l(trackToRemove);
+    count = tracksToRemove->size();
+    if (CC_UNLIKELY(count)) {
+        for (size_t i = 0 ; i < count ; i++) {
+            const sp<Track>& track = tracksToRemove->itemAt(i);
+            mActiveTracks.remove(track);
+            if (!mEffectChains.isEmpty()) {
+                ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
+                      track->sessionId());
+                mEffectChains[0]->decActiveTrackCnt();
+            }
+            if (track->isTerminated()) {
+                removeTrack_l(track);
+            }
         }
     }
 
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 6847bf8..3c84703 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -32,6 +32,7 @@
 
 LOCAL_SHARED_LIBRARIES:= \
     libui \
+    liblog \
     libutils \
     libbinder \
     libcutils \
diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp
index 6cf652c..08aef83 100644
--- a/services/camera/libcameraservice/Camera3Device.cpp
+++ b/services/camera/libcameraservice/Camera3Device.cpp
@@ -25,6 +25,18 @@
 #define ALOGVV(...) ((void)0)
 #endif
 
+// Convenience macro for transient errors
+#define CLOGE(fmt, ...) ALOGE("Camera %d: %s: " fmt, mId, __FUNCTION__, \
+            ##__VA_ARGS__)
+
+// Convenience macros for transitioning to the error state
+#define SET_ERR(fmt, ...) setErrorState(   \
+    "%s: " fmt, __FUNCTION__,              \
+    ##__VA_ARGS__)
+#define SET_ERR_L(fmt, ...) setErrorStateLocked( \
+    "%s: " fmt, __FUNCTION__,                    \
+    ##__VA_ARGS__)
+
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include <utils/Timers.h>
@@ -69,7 +81,7 @@
 
     ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
     if (mStatus != STATUS_UNINITIALIZED) {
-        ALOGE("%s: Already initialized!", __FUNCTION__);
+        CLOGE("Already initialized!");
         return INVALID_OPERATION;
     }
 
@@ -84,21 +96,18 @@
             reinterpret_cast<hw_device_t**>(&device));
 
     if (res != OK) {
-        ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__,
-                mId, strerror(-res), res);
-        mStatus = STATUS_ERROR;
+        SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);
         return res;
     }
 
     /** Cross-check device version */
 
     if (device->common.version != CAMERA_DEVICE_API_VERSION_3_0) {
-        ALOGE("%s: Could not open camera %d: "
+        SET_ERR_L("Could not open camera: "
                 "Camera device is not version %x, reports %x instead",
-                __FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_3_0,
+                CAMERA_DEVICE_API_VERSION_3_0,
                 device->common.version);
         device->common.close(&device->common);
-        mStatus = STATUS_ERROR;
         return BAD_VALUE;
     }
 
@@ -107,11 +116,10 @@
     if (res != OK) return res;
 
     if (info.device_version != device->common.version) {
-        ALOGE("%s: HAL reporting mismatched camera_info version (%x)"
-                " and device version (%x).", __FUNCTION__,
+        SET_ERR_L("HAL reporting mismatched camera_info version (%x)"
+                " and device version (%x).",
                 device->common.version, info.device_version);
         device->common.close(&device->common);
-        mStatus = STATUS_ERROR;
         return BAD_VALUE;
     }
 
@@ -119,10 +127,9 @@
 
     res = device->ops->initialize(device, this);
     if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to initialize HAL device: %s (%d)",
-                __FUNCTION__, mId, strerror(-res), res);
+        SET_ERR_L("Unable to initialize HAL device: %s (%d)",
+                strerror(-res), res);
         device->common.close(&device->common);
-        mStatus = STATUS_ERROR;
         return BAD_VALUE;
     }
 
@@ -135,10 +142,9 @@
     if (mVendorTagOps.get_camera_vendor_section_name != NULL) {
         res = set_camera_metadata_vendor_tag_ops(&mVendorTagOps);
         if (res != OK) {
-            ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)",
-                    __FUNCTION__, mId, strerror(-res), res);
+            SET_ERR_L("Unable to set tag ops: %s (%d)",
+                    strerror(-res), res);
             device->common.close(&device->common);
-            mStatus = STATUS_ERROR;
             return res;
         }
     }
@@ -148,11 +154,10 @@
     mRequestThread = new RequestThread(this, device);
     res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string());
     if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to start request queue thread: %s (%d)",
-                __FUNCTION__, mId, strerror(-res), res);
+        SET_ERR_L("Unable to start request queue thread: %s (%d)",
+                strerror(-res), res);
         device->common.close(&device->common);
         mRequestThread.clear();
-        mStatus = STATUS_ERROR;
         return res;
     }
 
@@ -179,12 +184,12 @@
             (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
         res = mRequestThread->clearRepeatingRequests();
         if (res != OK) {
-            ALOGE("%s: Can't stop streaming", __FUNCTION__);
+            SET_ERR_L("Can't stop streaming");
             return res;
         }
         res = waitUntilDrainedLocked();
         if (res != OK) {
-            ALOGE("%s: Timeout waiting for HAL to drain", __FUNCTION__);
+            SET_ERR_L("Timeout waiting for HAL to drain");
             return res;
         }
     }
@@ -225,6 +230,9 @@
             mStatus == STATUS_ACTIVE        ? "ACTIVE" :
             "Unknown";
     lines.appendFormat("    Device status: %s\n", status);
+    if (mStatus == STATUS_ERROR) {
+        lines.appendFormat("    Error cause: %s\n", mErrorCause.string());
+    }
     lines.appendFormat("    Stream configuration:\n");
 
     if (mInputStream != NULL) {
@@ -251,7 +259,7 @@
     ALOGVV("%s: E", __FUNCTION__);
     if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED ||
                     mStatus == STATUS_ERROR)) {
-        ALOGE("%s: Access to static info %s!", __FUNCTION__,
+        ALOGW("%s: Access to static info %s!", __FUNCTION__,
                 mStatus == STATUS_ERROR ?
                 "when in error state" : "before init");
     }
@@ -262,25 +270,27 @@
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
 
+    // TODO: take ownership of the request
+
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device not initialized", __FUNCTION__);
+            CLOGE("Device not initialized");
             return INVALID_OPERATION;
         case STATUS_IDLE:
         case STATUS_ACTIVE:
             // OK
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
 
     sp<CaptureRequest> newRequest = setUpRequestLocked(request);
     if (newRequest == NULL) {
-        ALOGE("%s: Can't create capture request", __FUNCTION__);
+        CLOGE("Can't create capture request");
         return BAD_VALUE;
     }
 
@@ -294,23 +304,23 @@
 
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device not initialized", __FUNCTION__);
+            CLOGE("Device not initialized");
             return INVALID_OPERATION;
         case STATUS_IDLE:
         case STATUS_ACTIVE:
             // OK
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
 
     sp<CaptureRequest> newRepeatingRequest = setUpRequestLocked(request);
     if (newRepeatingRequest == NULL) {
-        ALOGE("%s: Can't create repeating request", __FUNCTION__);
+        CLOGE("Can't create repeating request");
         return BAD_VALUE;
     }
 
@@ -328,8 +338,7 @@
     if (mStatus == STATUS_IDLE) {
         res = configureStreamsLocked();
         if (res != OK) {
-            ALOGE("%s: Can't set up streams: %s (%d)",
-                    __FUNCTION__, strerror(-res), res);
+            SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res);
             return NULL;
         }
     }
@@ -344,17 +353,17 @@
 
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device not initialized", __FUNCTION__);
+            CLOGE("Device not initialized");
             return INVALID_OPERATION;
         case STATUS_IDLE:
         case STATUS_ACTIVE:
             // OK
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
 
@@ -363,10 +372,8 @@
 
 status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
     ATRACE_CALL();
-    (void)requestId; (void)timeout;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
-    return INVALID_OPERATION;
+    return mRequestThread->waitUntilRequestProcessed(requestId, timeout);
 }
 
 status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
@@ -379,10 +386,10 @@
 
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device not initialized", __FUNCTION__);
+            CLOGE("Device not initialized");
             return INVALID_OPERATION;
         case STATUS_IDLE:
             // OK
@@ -394,13 +401,12 @@
             if (res != OK) {
                 ALOGE("%s: Can't pause captures to reconfigure streams!",
                         __FUNCTION__);
-                mStatus = STATUS_ERROR;
                 return res;
             }
             wasActive = true;
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
     assert(mStatus == STATUS_IDLE);
@@ -416,8 +422,7 @@
 
     res = mOutputStreams.add(mNextStreamId, newStream);
     if (res < 0) {
-        ALOGE("%s: Can't add new stream to set: %s (%d)",
-                __FUNCTION__, strerror(-res), res);
+        SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
         return res;
     }
 
@@ -428,8 +433,8 @@
         ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__);
         res = configureStreamsLocked();
         if (res != OK) {
-            ALOGE("%s: Can't reconfigure device for new stream %d: %s (%d)",
-                    __FUNCTION__, mNextStreamId, strerror(-res), res);
+            CLOGE("Can't reconfigure device for new stream %d: %s (%d)",
+                    mNextStreamId, strerror(-res), res);
             return res;
         }
         mRequestThread->setPaused(false);
@@ -442,7 +447,7 @@
     ATRACE_CALL();
     (void)outputId; (void)id;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
+    CLOGE("Unimplemented");
     return INVALID_OPERATION;
 }
 
@@ -454,23 +459,23 @@
 
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device not initialized!", __FUNCTION__);
+            CLOGE("Device not initialized!");
             return INVALID_OPERATION;
         case STATUS_IDLE:
         case STATUS_ACTIVE:
             // OK
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
 
     ssize_t idx = mOutputStreams.indexOfKey(id);
     if (idx == NAME_NOT_FOUND) {
-        ALOGE("%s: Stream %d is unknown", __FUNCTION__, id);
+        CLOGE("Stream %d is unknown", id);
         return idx;
     }
 
@@ -488,24 +493,24 @@
 
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device not initialized", __FUNCTION__);
+            CLOGE("Device not initialized");
             return INVALID_OPERATION;
         case STATUS_IDLE:
         case STATUS_ACTIVE:
             // OK
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
 
     ssize_t idx = mOutputStreams.indexOfKey(id);
     if (idx == NAME_NOT_FOUND) {
-        ALOGE("%s: Stream %d does not exist",
-                __FUNCTION__, id);
+        CLOGE("Stream %d does not exist",
+                id);
         return BAD_VALUE;
     }
 
@@ -520,7 +525,7 @@
     // CameraDevice semantics require device to already be idle before
     // deleteStream is called, unlike for createStream.
     if (mStatus != STATUS_IDLE) {
-        ALOGE("%s: Device not idle", __FUNCTION__);
+        CLOGE("Device not idle");
         return INVALID_OPERATION;
     }
 
@@ -531,8 +536,7 @@
     } else {
         ssize_t idx = mOutputStreams.indexOfKey(id);
         if (idx == NAME_NOT_FOUND) {
-            ALOGE("%s: Stream %d does not exist",
-                    __FUNCTION__, id);
+            CLOGE("Stream %d does not exist", id);
             return BAD_VALUE;
         }
         deletedStream = mOutputStreams.editValueAt(idx);
@@ -542,7 +546,7 @@
     // Free up the stream endpoint so that it can be used by some other stream
     res = deletedStream->disconnect();
     if (res != OK) {
-        ALOGE("%s: Can't disconnect deleted stream", __FUNCTION__);
+        SET_ERR_L("Can't disconnect deleted stream %d", id);
         // fall through since we want to still list the stream as deleted.
     }
     mDeletedStreams.add(deletedStream);
@@ -554,7 +558,7 @@
     ATRACE_CALL();
     (void)id;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
+    CLOGE("Unimplemented");
     return INVALID_OPERATION;
 }
 
@@ -567,24 +571,28 @@
 
     switch (mStatus) {
         case STATUS_ERROR:
-            ALOGE("%s: Device has encountered a serious error", __FUNCTION__);
+            CLOGE("Device has encountered a serious error");
             return INVALID_OPERATION;
         case STATUS_UNINITIALIZED:
-            ALOGE("%s: Device is not initialized!", __FUNCTION__);
+            CLOGE("Device is not initialized!");
             return INVALID_OPERATION;
         case STATUS_IDLE:
         case STATUS_ACTIVE:
             // OK
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
 
     const camera_metadata_t *rawRequest;
     rawRequest = mHal3Device->ops->construct_default_request_settings(
         mHal3Device, templateId);
-    if (rawRequest == NULL) return DEAD_OBJECT;
+    if (rawRequest == NULL) {
+        SET_ERR_L("HAL is unable to construct default settings for template %d",
+                templateId);
+        return DEAD_OBJECT;
+    }
     *request = rawRequest;
 
     return OK;
@@ -611,35 +619,31 @@
             // Need to shut down
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d",mStatus);
             return INVALID_OPERATION;
     }
 
     if (mRequestThread != NULL) {
         res = mRequestThread->waitUntilPaused(kShutdownTimeout);
         if (res != OK) {
-            ALOGE("%s: Can't stop request thread in %f seconds!",
-                    __FUNCTION__, kShutdownTimeout/1e9);
-            mStatus = STATUS_ERROR;
+            SET_ERR_L("Can't stop request thread in %f seconds!",
+                    kShutdownTimeout/1e9);
             return res;
         }
     }
     if (mInputStream != NULL) {
         res = mInputStream->waitUntilIdle(kShutdownTimeout);
         if (res != OK) {
-            ALOGE("%s: Can't idle input stream %d in %f seconds!",
-                    __FUNCTION__, mInputStream->getId(), kShutdownTimeout/1e9);
-            mStatus = STATUS_ERROR;
+            SET_ERR_L("Can't idle input stream %d in %f seconds!",
+                    mInputStream->getId(), kShutdownTimeout/1e9);
             return res;
         }
     }
     for (size_t i = 0; i < mOutputStreams.size(); i++) {
         res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout);
         if (res != OK) {
-            ALOGE("%s: Can't idle output stream %d in %f seconds!",
-                    __FUNCTION__, mOutputStreams.keyAt(i),
-                    kShutdownTimeout/1e9);
-            mStatus = STATUS_ERROR;
+            SET_ERR_L("Can't idle output stream %d in %f seconds!",
+                    mOutputStreams.keyAt(i), kShutdownTimeout/1e9);
             return res;
         }
     }
@@ -673,8 +677,8 @@
         if (res == TIMED_OUT) {
             return res;
         } else if (res != OK) {
-            ALOGE("%s: Camera %d: Error waiting for frame: %s (%d)",
-                    __FUNCTION__, mId, strerror(-res), res);
+            ALOGW("%s: Camera %d: No frame in %lld ns: %s (%d)",
+                    __FUNCTION__, mId, timeout, strerror(-res), res);
             return res;
         }
     }
@@ -698,28 +702,62 @@
 
 status_t Camera3Device::triggerAutofocus(uint32_t id) {
     ATRACE_CALL();
-    (void)id;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
-    return INVALID_OPERATION;
+    ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
+    // Mix-in this trigger into the next request and only the next request.
+    RequestTrigger trigger[] = {
+        {
+            ANDROID_CONTROL_AF_TRIGGER,
+            ANDROID_CONTROL_AF_TRIGGER_START
+        },
+        {
+            ANDROID_CONTROL_AF_TRIGGER_ID,
+            static_cast<int32_t>(id)
+        },
+    };
+
+    return mRequestThread->queueTrigger(trigger,
+                                        sizeof(trigger)/sizeof(trigger[0]));
 }
 
 status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
     ATRACE_CALL();
-    (void)id;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
-    return INVALID_OPERATION;
+    ALOGV("%s: Triggering cancel autofocus, id %d", __FUNCTION__, id);
+    // Mix-in this trigger into the next request and only the next request.
+    RequestTrigger trigger[] = {
+        {
+            ANDROID_CONTROL_AF_TRIGGER,
+            ANDROID_CONTROL_AF_TRIGGER_CANCEL
+        },
+        {
+            ANDROID_CONTROL_AF_TRIGGER_ID,
+            static_cast<int32_t>(id)
+        },
+    };
 
+    return mRequestThread->queueTrigger(trigger,
+                                        sizeof(trigger)/sizeof(trigger[0]));
 }
 
 status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) {
     ATRACE_CALL();
-    (void)id;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
-    return INVALID_OPERATION;
+    ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
+    // Mix-in this trigger into the next request and only the next request.
+    RequestTrigger trigger[] = {
+        {
+            ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+            ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START
+        },
+        {
+            ANDROID_CONTROL_AE_PRECAPTURE_ID,
+            static_cast<int32_t>(id)
+        },
+    };
 
+    return mRequestThread->queueTrigger(trigger,
+                                        sizeof(trigger)/sizeof(trigger[0]));
 }
 
 status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId,
@@ -727,7 +765,7 @@
     ATRACE_CALL();
     (void)reprocessStreamId; (void)buffer; (void)listener;
 
-    ALOGE("%s: Unimplemented", __FUNCTION__);
+    CLOGE("Unimplemented");
     return INVALID_OPERATION;
 }
 
@@ -748,8 +786,8 @@
     if (inputStreams.count > 0) {
         if (mInputStream == NULL ||
                 mInputStream->getId() != inputStreams.data.u8[0]) {
-            ALOGE("%s: Request references unknown input stream %d",
-                    __FUNCTION__, inputStreams.data.u8[0]);
+            CLOGE("Request references unknown input stream %d",
+                    inputStreams.data.u8[0]);
             return NULL;
         }
         // Lazy completion of stream configuration (allocation/registration)
@@ -757,10 +795,9 @@
         if (mInputStream->isConfiguring()) {
             res = mInputStream->finishConfiguration(mHal3Device);
             if (res != OK) {
-                ALOGE("%s: Unable to finish configuring input stream %d:"
+                SET_ERR_L("Unable to finish configuring input stream %d:"
                         " %s (%d)",
-                        __FUNCTION__, mInputStream->getId(),
-                        strerror(-res), res);
+                        mInputStream->getId(), strerror(-res), res);
                 return NULL;
             }
         }
@@ -772,15 +809,15 @@
     camera_metadata_entry_t streams =
             newRequest->mSettings.find(ANDROID_REQUEST_OUTPUT_STREAMS);
     if (streams.count == 0) {
-        ALOGE("%s: Zero output streams specified!", __FUNCTION__);
+        CLOGE("Zero output streams specified!");
         return NULL;
     }
 
     for (size_t i = 0; i < streams.count; i++) {
         int idx = mOutputStreams.indexOfKey(streams.data.u8[i]);
         if (idx == NAME_NOT_FOUND) {
-            ALOGE("%s: Request references unknown stream %d",
-                    __FUNCTION__, streams.data.u8[i]);
+            CLOGE("Request references unknown stream %d",
+                    streams.data.u8[i]);
             return NULL;
         }
         sp<Camera3OutputStream> stream = mOutputStreams.editValueAt(idx);
@@ -790,8 +827,8 @@
         if (stream->isConfiguring()) {
             res = stream->finishConfiguration(mHal3Device);
             if (res != OK) {
-                ALOGE("%s: Unable to finish configuring stream %d: %s (%d)",
-                        __FUNCTION__, stream->getId(), strerror(-res), res);
+                SET_ERR_L("Unable to finish configuring stream %d: %s (%d)",
+                        stream->getId(), strerror(-res), res);
                 return NULL;
             }
         }
@@ -808,7 +845,7 @@
     status_t res;
 
     if (mStatus != STATUS_IDLE) {
-        ALOGE("%s: Not idle", __FUNCTION__);
+        CLOGE("Not idle");
         return INVALID_OPERATION;
     }
 
@@ -825,9 +862,7 @@
         camera3_stream_t *inputStream;
         inputStream = mInputStream->startConfiguration();
         if (inputStream == NULL) {
-            ALOGE("%s: Can't start input stream configuration",
-                    __FUNCTION__);
-            // TODO: Make sure the error flow here is correct
+            SET_ERR_L("Can't start input stream configuration");
             return INVALID_OPERATION;
         }
         streams.add(inputStream);
@@ -837,9 +872,7 @@
         camera3_stream_t *outputStream;
         outputStream = mOutputStreams.editValueAt(i)->startConfiguration();
         if (outputStream == NULL) {
-            ALOGE("%s: Can't start output stream configuration",
-                    __FUNCTION__);
-            // TODO: Make sure the error flow here is correct
+            SET_ERR_L("Can't start output stream configuration");
             return INVALID_OPERATION;
         }
         streams.add(outputStream);
@@ -853,8 +886,8 @@
     res = mHal3Device->ops->configure_streams(mHal3Device, &config);
 
     if (res != OK) {
-        ALOGE("%s: Unable to configure streams with HAL: %s (%d)",
-                __FUNCTION__, strerror(-res), res);
+        SET_ERR_L("Unable to configure streams with HAL: %s (%d)",
+                strerror(-res), res);
         return res;
     }
 
@@ -869,6 +902,38 @@
     return OK;
 }
 
+void Camera3Device::setErrorState(const char *fmt, ...) {
+    Mutex::Autolock l(mLock);
+    va_list args;
+    va_start(args, fmt);
+
+    setErrorStateLockedV(fmt, args);
+
+    va_end(args);
+}
+
+void Camera3Device::setErrorStateV(const char *fmt, va_list args) {
+    Mutex::Autolock l(mLock);
+    setErrorStateLockedV(fmt, args);
+}
+
+void Camera3Device::setErrorStateLocked(const char *fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+
+    setErrorStateLockedV(fmt, args);
+
+    va_end(args);
+}
+
+void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) {
+    // Only accept the first failure cause
+    if (mStatus == STATUS_ERROR) return;
+
+    mErrorCause = String8::formatV(fmt, args);
+    ALOGE("Camera %d: %s", mId, mErrorCause.string());
+    mStatus = STATUS_ERROR;
+}
 
 /**
  * Camera HAL device callback methods
@@ -880,8 +945,7 @@
     status_t res;
 
     if (result->result == NULL) {
-        // TODO: Report error upstream
-        ALOGW("%s: No metadata for frame %d", __FUNCTION__,
+        SET_ERR("No metadata provided by HAL for frame %d",
                 result->frame_number);
         return;
     }
@@ -899,20 +963,26 @@
 
         // Push result metadata into queue
         mResultQueue.push_back(CameraMetadata());
-        CameraMetadata &captureResult = *(mResultQueue.end());
+        // Lets avoid copies! Too bad there's not a #back method
+        CameraMetadata &captureResult = *(--mResultQueue.end());
 
         captureResult = result->result;
-        captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
-                (int32_t*)&result->frame_number, 1);
+        if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
+                        (int32_t*)&result->frame_number, 1) != OK) {
+            SET_ERR("Failed to set frame# in metadata (%d)",
+                    result->frame_number);
+        } else {
+            ALOGVV("%s: Camera %d: Set frame# in metadata (%d)",
+                    __FUNCTION__, mId, result->frame_number);
+        }
 
         // Get timestamp from result metadata
 
         camera_metadata_entry entry =
                 captureResult.find(ANDROID_SENSOR_TIMESTAMP);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: No timestamp provided by HAL for frame %d!",
-                    __FUNCTION__, mId, result->frame_number);
-            // TODO: Report error upstream
+            SET_ERR("No timestamp provided by HAL for frame %d!",
+                    result->frame_number);
         } else {
             timestamp = entry.data.i64[0];
         }
@@ -921,8 +991,8 @@
 
         entry = captureResult.find(ANDROID_CONTROL_AE_STATE);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: No AE state provided by HAL for frame %d!",
-                    __FUNCTION__, mId, result->frame_number);
+            CLOGE("No AE state provided by HAL for frame %d!",
+                    result->frame_number);
         } else {
             new3aState.aeState =
                     static_cast<camera_metadata_enum_android_control_ae_state>(
@@ -931,8 +1001,8 @@
 
         entry = captureResult.find(ANDROID_CONTROL_AF_STATE);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: No AF state provided by HAL for frame %d!",
-                    __FUNCTION__, mId, result->frame_number);
+            CLOGE("No AF state provided by HAL for frame %d!",
+                    result->frame_number);
         } else {
             new3aState.afState =
                     static_cast<camera_metadata_enum_android_control_af_state>(
@@ -941,8 +1011,8 @@
 
         entry = captureResult.find(ANDROID_CONTROL_AWB_STATE);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: No AWB state provided by HAL for frame %d!",
-                    __FUNCTION__, mId, result->frame_number);
+            CLOGE("No AWB state provided by HAL for frame %d!",
+                    result->frame_number);
         } else {
             new3aState.awbState =
                     static_cast<camera_metadata_enum_android_control_awb_state>(
@@ -951,16 +1021,16 @@
 
         entry = captureResult.find(ANDROID_CONTROL_AF_TRIGGER_ID);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: No AF trigger ID provided by HAL for frame %d!",
-                    __FUNCTION__, mId, result->frame_number);
+            CLOGE("No AF trigger ID provided by HAL for frame %d!",
+                    result->frame_number);
         } else {
             afTriggerId = entry.data.i32[0];
         }
 
         entry = captureResult.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: No AE precapture trigger ID provided by HAL"
-                    " for frame %d!", __FUNCTION__, mId, result->frame_number);
+            CLOGE("No AE precapture trigger ID provided by HAL"
+                    " for frame %d!", result->frame_number);
         } else {
             aeTriggerId = entry.data.i32[0];
         }
@@ -979,19 +1049,21 @@
         // Note: stream may be deallocated at this point, if this buffer was the
         // last reference to it.
         if (res != OK) {
-            ALOGE("%s: Camera %d: Can't return buffer %d for frame %d to its"
-                    "  stream:%s (%d)",  __FUNCTION__, mId, i,
-                    result->frame_number, strerror(-res), res);
-            // TODO: Report error upstream
+            SET_ERR("Can't return buffer %d for frame %d to its stream: "
+                    " %s (%d)", i, result->frame_number, strerror(-res), res);
         }
     }
 
     // Dispatch any 3A change events to listeners
     if (listener != NULL) {
         if (new3aState.aeState != cur3aState.aeState) {
+            ALOGVV("%s: AE state changed from 0x%x to 0x%x",
+                   __FUNCTION__, cur3aState.aeState, new3aState.aeState);
             listener->notifyAutoExposure(new3aState.aeState, aeTriggerId);
         }
         if (new3aState.afState != cur3aState.afState) {
+            ALOGVV("%s: AF state changed from 0x%x to 0x%x",
+                   __FUNCTION__, cur3aState.afState, new3aState.afState);
             listener->notifyAutoFocus(new3aState.afState, afTriggerId);
         }
         if (new3aState.awbState != cur3aState.awbState) {
@@ -1010,8 +1082,7 @@
     }
 
     if (msg == NULL) {
-        ALOGE("%s: Camera %d: HAL sent NULL notify message!",
-                __FUNCTION__, mId);
+        SET_ERR_L("HAL sent NULL notify message!");
         return;
     }
 
@@ -1034,8 +1105,8 @@
             break;
         }
         default:
-            ALOGE("%s: Camera %d: Unknown notify message from HAL: %d",
-                    __FUNCTION__, mId, msg->type);
+            SET_ERR_L("Unknown notify message from HAL: %d",
+                    msg->type);
     }
 }
 
@@ -1051,7 +1122,8 @@
         mReconfigured(false),
         mDoPause(false),
         mPaused(true),
-        mFrameNumber(0) {
+        mFrameNumber(0),
+        mLatestRequestId(NAME_NOT_FOUND) {
 }
 
 void Camera3Device::RequestThread::configurationComplete() {
@@ -1067,6 +1139,57 @@
     return OK;
 }
 
+
+status_t Camera3Device::RequestThread::queueTrigger(
+        RequestTrigger trigger[],
+        size_t count) {
+
+    Mutex::Autolock l(mTriggerMutex);
+    status_t ret;
+
+    for (size_t i = 0; i < count; ++i) {
+        ret = queueTriggerLocked(trigger[i]);
+
+        if (ret != OK) {
+            return ret;
+        }
+    }
+
+    return OK;
+}
+
+status_t Camera3Device::RequestThread::queueTriggerLocked(
+        RequestTrigger trigger) {
+
+    uint32_t tag = trigger.metadataTag;
+    ssize_t index = mTriggerMap.indexOfKey(tag);
+
+    switch (trigger.getTagType()) {
+        case TYPE_BYTE:
+        // fall-through
+        case TYPE_INT32:
+            break;
+        default:
+            ALOGE("%s: Type not supported: 0x%x",
+                  __FUNCTION__,
+                  trigger.getTagType());
+            return INVALID_OPERATION;
+    }
+
+    /**
+     * Collect only the latest trigger, since we only have 1 field
+     * in the request settings per trigger tag, and can't send more than 1
+     * trigger per request.
+     */
+    if (index != NAME_NOT_FOUND) {
+        mTriggerMap.editValueAt(index) = trigger;
+    } else {
+        mTriggerMap.add(tag, trigger);
+    }
+
+    return OK;
+}
+
 status_t Camera3Device::RequestThread::setRepeatingRequests(
         const RequestList &requests) {
     Mutex::Autolock l(mRequestLock);
@@ -1100,6 +1223,24 @@
     return OK;
 }
 
+status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
+        int32_t requestId, nsecs_t timeout) {
+    Mutex::Autolock l(mLatestRequestMutex);
+    status_t res;
+    while (mLatestRequestId != requestId) {
+        nsecs_t startTime = systemTime();
+
+        res = mLatestRequestSignal.waitRelative(mLatestRequestMutex, timeout);
+        if (res != OK) return res;
+
+        timeout -= (systemTime() - startTime);
+    }
+
+    return OK;
+}
+
+
+
 bool Camera3Device::RequestThread::threadLoop() {
 
     status_t res;
@@ -1117,16 +1258,55 @@
     }
 
     // Create request to HAL
-
     camera3_capture_request_t request = camera3_capture_request_t();
+    Vector<camera3_stream_buffer_t> outputBuffers;
 
-    if (mPrevRequest != nextRequest) {
+    // Insert any queued triggers (before metadata is locked)
+    int32_t triggerCount;
+    res = insertTriggers(nextRequest);
+    if (res < 0) {
+        SET_ERR("RequestThread: Unable to insert triggers "
+                "(capture request %d, HAL device: %s (%d)",
+                (mFrameNumber+1), strerror(-res), res);
+        cleanUpFailedRequest(request, nextRequest, outputBuffers);
+        return false;
+    }
+    triggerCount = res;
+
+    bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
+
+    // If the request is the same as last, or we had triggers last time
+    if (mPrevRequest != nextRequest || triggersMixedIn) {
+        /**
+         * The request should be presorted so accesses in HAL
+         *   are O(logn). Sidenote, sorting a sorted metadata is nop.
+         */
+        nextRequest->mSettings.sort();
         request.settings = nextRequest->mSettings.getAndLock();
         mPrevRequest = nextRequest;
-    } // else leave request.settings NULL to indicate 'reuse latest given'
+        ALOGVV("%s: Request settings are NEW", __FUNCTION__);
+
+        IF_ALOGV() {
+            camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
+            find_camera_metadata_ro_entry(
+                    request.settings,
+                    ANDROID_CONTROL_AF_TRIGGER,
+                    &e
+            );
+            if (e.count > 0) {
+                ALOGV("%s: Request (frame num %d) had AF trigger 0x%x",
+                      __FUNCTION__,
+                      mFrameNumber+1,
+                      e.data.u8[0]);
+            }
+        }
+    } else {
+        // leave request.settings NULL to indicate 'reuse latest given'
+        ALOGVV("%s: Request settings are REUSED",
+               __FUNCTION__);
+    }
 
     camera3_stream_buffer_t inputBuffer;
-    Vector<camera3_stream_buffer_t> outputBuffers;
 
     // Fill in buffers
 
@@ -1134,7 +1314,7 @@
         request.input_buffer = &inputBuffer;
         res = nextRequest->mInputStream->getBuffer(&inputBuffer);
         if (res != OK) {
-            ALOGE("RequestThread: Can't get input buffer, skipping request:"
+            SET_ERR("RequestThread: Can't get input buffer, skipping request:"
                     " %s (%d)", strerror(-res), res);
             cleanUpFailedRequest(request, nextRequest, outputBuffers);
             return true;
@@ -1150,7 +1330,7 @@
         res = nextRequest->mOutputStreams.editItemAt(i)->
                 getBuffer(&outputBuffers.editItemAt(i));
         if (res != OK) {
-            ALOGE("RequestThread: Can't get output buffer, skipping request:"
+            SET_ERR("RequestThread: Can't get output buffer, skipping request:"
                     "%s (%d)", strerror(-res), res);
             cleanUpFailedRequest(request, nextRequest, outputBuffers);
             return true;
@@ -1160,11 +1340,12 @@
 
     request.frame_number = mFrameNumber++;
 
+
     // Submit request and block until ready for next one
 
     res = mHal3Device->ops->process_capture_request(mHal3Device, &request);
     if (res != OK) {
-        ALOGE("RequestThread: Unable to submit capture request %d to HAL"
+        SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
                 " device: %s (%d)", request.frame_number, strerror(-res), res);
         cleanUpFailedRequest(request, nextRequest, outputBuffers);
         return false;
@@ -1173,6 +1354,35 @@
     if (request.settings != NULL) {
         nextRequest->mSettings.unlock(request.settings);
     }
+
+    // Remove any previously queued triggers (after unlock)
+    res = removeTriggers(mPrevRequest);
+    if (res != OK) {
+        SET_ERR("RequestThread: Unable to remove triggers "
+              "(capture request %d, HAL device: %s (%d)",
+              request.frame_number, strerror(-res), res);
+        return false;
+    }
+    mPrevTriggers = triggerCount;
+
+    // Read android.request.id from the request settings metadata
+    // - inform waitUntilRequestProcessed thread of a new request ID
+    {
+        Mutex::Autolock al(mLatestRequestMutex);
+
+        camera_metadata_entry_t requestIdEntry =
+                nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+        if (requestIdEntry.count > 0) {
+            mLatestRequestId = requestIdEntry.data.i32[0];
+        } else {
+            ALOGW("%s: Did not have android.request.id set in the request",
+                  __FUNCTION__);
+            mLatestRequestId = NAME_NOT_FOUND;
+        }
+
+        mLatestRequestSignal.signal();
+    }
+
     return true;
 }
 
@@ -1193,7 +1403,6 @@
         nextRequest->mOutputStreams.editItemAt(i)->returnBuffer(
             outputBuffers[i], 0);
     }
-    // TODO: Report error upstream
 }
 
 sp<Camera3Device::CaptureRequest>
@@ -1277,6 +1486,153 @@
     return false;
 }
 
+void Camera3Device::RequestThread::setErrorState(const char *fmt, ...) {
+    sp<Camera3Device> parent = mParent.promote();
+    if (parent != NULL) {
+        va_list args;
+        va_start(args, fmt);
+
+        parent->setErrorStateV(fmt, args);
+
+        va_end(args);
+    }
+}
+
+status_t Camera3Device::RequestThread::insertTriggers(
+        const sp<CaptureRequest> &request) {
+
+    Mutex::Autolock al(mTriggerMutex);
+
+    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;
+        camera_metadata_entry entry = metadata.find(tag);
+
+        if (entry.count > 0) {
+            /**
+             * Already has an entry for this trigger in the request.
+             * Rewrite it with our requested trigger value.
+             */
+            RequestTrigger oldTrigger = trigger;
+
+            oldTrigger.entryValue = entry.data.u8[0];
+
+            mTriggerReplacedMap.add(tag, oldTrigger);
+        } else {
+            /**
+             * More typical, no trigger entry, so we just add it
+             */
+            mTriggerRemovedMap.add(tag, trigger);
+        }
+
+        status_t res;
+
+        switch (trigger.getTagType()) {
+            case TYPE_BYTE: {
+                uint8_t entryValue = static_cast<uint8_t>(trigger.entryValue);
+                res = metadata.update(tag,
+                                      &entryValue,
+                                      /*count*/1);
+                break;
+            }
+            case TYPE_INT32:
+                res = metadata.update(tag,
+                                      &trigger.entryValue,
+                                      /*count*/1);
+                break;
+            default:
+                ALOGE("%s: Type not supported: 0x%x",
+                      __FUNCTION__,
+                      trigger.getTagType());
+                return INVALID_OPERATION;
+        }
+
+        if (res != OK) {
+            ALOGE("%s: Failed to update request metadata with trigger tag %s"
+                  ", value %d", __FUNCTION__, trigger.getTagName(),
+                  trigger.entryValue);
+            return res;
+        }
+
+        ALOGV("%s: Mixed in trigger %s, value %d", __FUNCTION__,
+              trigger.getTagName(),
+              trigger.entryValue);
+    }
+
+    mTriggerMap.clear();
+
+    return count;
+}
+
+status_t Camera3Device::RequestThread::removeTriggers(
+        const sp<CaptureRequest> &request) {
+    Mutex::Autolock al(mTriggerMutex);
+
+    CameraMetadata &metadata = request->mSettings;
+
+    /**
+     * Replace all old entries with their old values.
+     */
+    for (size_t i = 0; i < mTriggerReplacedMap.size(); ++i) {
+        RequestTrigger trigger = mTriggerReplacedMap.valueAt(i);
+
+        status_t res;
+
+        uint32_t tag = trigger.metadataTag;
+        switch (trigger.getTagType()) {
+            case TYPE_BYTE: {
+                uint8_t entryValue = static_cast<uint8_t>(trigger.entryValue);
+                res = metadata.update(tag,
+                                      &entryValue,
+                                      /*count*/1);
+                break;
+            }
+            case TYPE_INT32:
+                res = metadata.update(tag,
+                                      &trigger.entryValue,
+                                      /*count*/1);
+                break;
+            default:
+                ALOGE("%s: Type not supported: 0x%x",
+                      __FUNCTION__,
+                      trigger.getTagType());
+                return INVALID_OPERATION;
+        }
+
+        if (res != OK) {
+            ALOGE("%s: Failed to restore request metadata with trigger tag %s"
+                  ", trigger value %d", __FUNCTION__,
+                  trigger.getTagName(), trigger.entryValue);
+            return res;
+        }
+    }
+    mTriggerReplacedMap.clear();
+
+    /**
+     * Remove all new entries.
+     */
+    for (size_t i = 0; i < mTriggerRemovedMap.size(); ++i) {
+        RequestTrigger trigger = mTriggerRemovedMap.valueAt(i);
+        status_t res = metadata.erase(trigger.metadataTag);
+
+        if (res != OK) {
+            ALOGE("%s: Failed to erase metadata with trigger tag %s"
+                  ", trigger value %d", __FUNCTION__,
+                  trigger.getTagName(), trigger.entryValue);
+            return res;
+        }
+    }
+    mTriggerRemovedMap.clear();
+
+    return OK;
+}
+
+
+
 /**
  * Static callback forwarding methods from HAL to instance
  */
diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h
index 8600c6c..2e4a303 100644
--- a/services/camera/libcameraservice/Camera3Device.h
+++ b/services/camera/libcameraservice/Camera3Device.h
@@ -109,7 +109,7 @@
 
   private:
     static const nsecs_t       kShutdownTimeout = 5000000000; // 5 sec
-
+    struct                     RequestTrigger;
 
     Mutex                      mLock;
 
@@ -128,6 +128,9 @@
         STATUS_ACTIVE
     }                          mStatus;
 
+    // Tracking cause of fatal errors when in STATUS_ERROR
+    String8                    mErrorCause;
+
     // Mapping of stream IDs to stream instances
     typedef KeyedVector<int, sp<camera3::Camera3OutputStream> > StreamSet;
 
@@ -173,6 +176,33 @@
     status_t           configureStreamsLocked();
 
     /**
+     * Set device into an error state due to some fatal failure, and set an
+     * error message to indicate why. Only the first call's message will be
+     * used. The message is also sent to the log.
+     */
+    void               setErrorState(const char *fmt, ...);
+    void               setErrorStateV(const char *fmt, va_list args);
+    void               setErrorStateLocked(const char *fmt, ...);
+    void               setErrorStateLockedV(const char *fmt, va_list args);
+
+    struct RequestTrigger {
+        // Metadata tag number, e.g. android.control.aePrecaptureTrigger
+        uint32_t metadataTag;
+        // Metadata value, e.g. 'START' or the trigger ID
+        int32_t entryValue;
+
+        // The last part of the fully qualified path, e.g. afTrigger
+        const char *getTagName() const {
+            return get_camera_metadata_tag_name(metadataTag) ?: "NULL";
+        }
+
+        // e.g. TYPE_BYTE, TYPE_INT32, etc.
+        int getTagType() const {
+            return get_camera_metadata_tag_type(metadataTag);
+        }
+    };
+
+    /**
      * Thread for managing capture request submission to HAL device.
      */
     class RequestThread : public Thread {
@@ -198,6 +228,14 @@
         status_t queueRequest(sp<CaptureRequest> request);
 
         /**
+         * Queue a trigger to be dispatched with the next outgoing
+         * process_capture_request. The settings for that request only
+         * will be temporarily rewritten to add the trigger tag/value.
+         * Subsequent requests will not be rewritten (for this tag).
+         */
+        status_t queueTrigger(RequestTrigger trigger[], size_t count);
+
+        /**
          * Pause/unpause the capture thread. Doesn't block, so use
          * waitUntilPaused to wait until the thread is paused.
          */
@@ -210,11 +248,27 @@
          */
         status_t waitUntilPaused(nsecs_t timeout);
 
+        /**
+         * Wait until thread processes the capture request with settings'
+         * android.request.id == requestId.
+         *
+         * Returns TIMED_OUT in case the thread does not process the request
+         * within the timeout.
+         */
+        status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
+
       protected:
 
         virtual bool threadLoop();
 
       private:
+        status_t           queueTriggerLocked(RequestTrigger trigger);
+        // Mix-in queued triggers into this request
+        int32_t            insertTriggers(const sp<CaptureRequest> &request);
+        // Purge the queued triggers from this request,
+        //  restoring the old field values for those tags.
+        status_t           removeTriggers(const sp<CaptureRequest> &request);
+
         static const nsecs_t kRequestTimeout = 50e6; // 50 ms
 
         // Waits for a request, or returns NULL if times out.
@@ -231,6 +285,9 @@
         // Pause handling
         bool               waitIfPaused();
 
+        // Relay error to parent device object setErrorState
+        void               setErrorState(const char *fmt, ...);
+
         wp<Camera3Device>  mParent;
         camera3_device_t  *mHal3Device;
 
@@ -249,8 +306,20 @@
         Condition          mPausedSignal;
 
         sp<CaptureRequest> mPrevRequest;
+        int32_t            mPrevTriggers;
 
         int32_t            mFrameNumber;
+
+        Mutex              mLatestRequestMutex;
+        Condition          mLatestRequestSignal;
+        // android.request.id for latest process_capture_request
+        int32_t            mLatestRequestId;
+
+        typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
+        Mutex              mTriggerMutex;
+        TriggerMap         mTriggerMap;
+        TriggerMap         mTriggerRemovedMap;
+        TriggerMap         mTriggerReplacedMap;
     };
     sp<RequestThread> mRequestThread;
 
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
index 1880912..ee03329 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
@@ -270,6 +270,9 @@
         processor->clearZslQueue();
     }
 
+    /**
+     * Fire the jpegCallback in Camera#takePicture(..., jpegCallback)
+     */
     if (mCaptureBuffer != 0 && res == OK) {
         Camera2Client::SharedCameraCallbacks::Lock
             l(client->mSharedCameraCallbacks);
@@ -367,6 +370,8 @@
 CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart(
         sp<Camera2Client> &client) {
     ATRACE_CALL();
+
+    // Get the onFrameAvailable callback when the requestID == mCaptureId
     client->registerFrameListener(mCaptureId, mCaptureId + 1,
             this);
     {
@@ -426,6 +431,13 @@
     SharedParameters::Lock l(client->getParameters());
     Vector<uint8_t> outputStreams;
 
+    /**
+     * Set up output streams in the request
+     *  - preview
+     *  - capture/jpeg
+     *  - callback (if preview callbacks enabled)
+     *  - recording (if recording enabled)
+     */
     outputStreams.push(client->getPreviewStreamId());
     outputStreams.push(client->getCaptureStreamId());
 
@@ -454,6 +466,7 @@
         return DONE;
     }
 
+    // Create a capture copy since CameraDeviceBase#capture takes ownership
     CameraMetadata captureCopy = mCaptureRequest;
     if (captureCopy.entryCount() == 0) {
         ALOGE("%s: Camera %d: Unable to copy capture request for HAL device",
@@ -461,7 +474,12 @@
         return DONE;
     }
 
+    /**
+     * Clear the streaming request for still-capture pictures
+     *   (as opposed to i.e. video snapshots)
+     */
     if (l.mParameters.state == Parameters::STILL_CAPTURE) {
+        // API definition of takePicture() - stop preview before taking pic
         res = client->stopStream();
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to stop preview for still capture: "
@@ -488,6 +506,8 @@
     status_t res;
     ATRACE_CALL();
     Mutex::Autolock l(mInputMutex);
+
+    // Wait for new metadata result (mNewFrame)
     while (!mNewFrameReceived) {
         res = mNewFrameSignal.waitRelative(mInputMutex, kWaitDuration);
         if (res == TIMED_OUT) {
@@ -495,12 +515,17 @@
             break;
         }
     }
+
+    // Approximation of the shutter being closed
+    // - TODO: use the hal3 exposure callback in Camera3Device instead
     if (mNewFrameReceived && !mShutterNotified) {
         SharedParameters::Lock l(client->getParameters());
         /* warning: this also locks a SharedCameraCallbacks */
         shutterNotifyLocked(l.mParameters, client, mMsgType);
         mShutterNotified = true;
     }
+
+    // Wait until jpeg was captured by JpegProcessor
     while (mNewFrameReceived && !mNewCaptureReceived) {
         res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
         if (res == TIMED_OUT) {
@@ -524,7 +549,9 @@
         }
         if (entry.data.i64[0] != mCaptureTimestamp) {
             ALOGW("Mismatched capture timestamps: Metadata frame %lld,"
-                    " captured buffer %lld", entry.data.i64[0], mCaptureTimestamp);
+                    " captured buffer %lld",
+                    entry.data.i64[0],
+                    mCaptureTimestamp);
         }
         client->removeFrameListener(mCaptureId, mCaptureId + 1, this);
 
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h
index 2283f28..74f4738 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.h
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.h
@@ -44,6 +44,7 @@
     JpegProcessor(wp<Camera2Client> client, wp<CaptureSequencer> sequencer);
     ~JpegProcessor();
 
+    // CpuConsumer listener implementation
     void onFrameAvailable();
 
     status_t updateStream(const Parameters &params);
diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
index d07ae94..276b940 100644
--- a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
@@ -57,7 +57,14 @@
         uint32_t width, uint32_t height, size_t maxSize, int format) :
         Camera3Stream(id, CAMERA3_STREAM_OUTPUT,
                 width, height, maxSize, format),
-        mConsumer(consumer) {
+        mConsumer(consumer),
+        mTransform(0),
+        mTotalBufferCount(0),
+        mDequeuedBufferCount(0),
+        mFrameCount(0),
+        mLastTimestamp(0) {
+
+    mCombinedFence = new Fence();
 
     if (format != HAL_PIXEL_FORMAT_BLOB) {
         ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__,
diff --git a/services/medialog/Android.mk b/services/medialog/Android.mk
index 559b1ed..08006c8 100644
--- a/services/medialog/Android.mk
+++ b/services/medialog/Android.mk
@@ -4,7 +4,7 @@
 
 LOCAL_SRC_FILES := MediaLogService.cpp
 
-LOCAL_SHARED_LIBRARIES := libmedia libbinder libutils libnbaio
+LOCAL_SHARED_LIBRARIES := libmedia libbinder libutils liblog libnbaio
 
 LOCAL_MODULE:= libmedialogservice