Merge "Fix SoundPool.play() looping" into klp-dev
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
new file mode 100644
index 0000000..ada23a2
--- /dev/null
+++ b/drm/mediadrm/plugins/mock/Android.mk
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    MockDrmCryptoPlugin.cpp
+
+LOCAL_MODULE := libmockdrmcryptoplugin
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils liblog
+
+LOCAL_C_INCLUDES += \
+    $(TOP)/frameworks/av/include \
+    $(TOP)/frameworks/native/include/media
+
+# Set the following flag to enable the decryption passthru flow
+#LOCAL_CFLAGS += -DENABLE_PASSTHRU_DECRYPTION
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
new file mode 100644
index 0000000..f2cadf7
--- /dev/null
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -0,0 +1,705 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MockDrmCryptoPlugin"
+#include <utils/Log.h>
+
+
+#include "drm/DrmAPI.h"
+#include "MockDrmCryptoPlugin.h"
+#include "media/stagefright/MediaErrors.h"
+
+using namespace android;
+
+// Shared library entry point
+DrmFactory *createDrmFactory()
+{
+    return new MockDrmFactory();
+}
+
+// Shared library entry point
+CryptoFactory *createCryptoFactory()
+{
+    return new MockCryptoFactory();
+}
+
+const uint8_t mock_uuid[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                               0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
+
+namespace android {
+
+    // MockDrmFactory
+    bool MockDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16])
+    {
+        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
+    }
+
+    bool MockDrmFactory::isContentTypeSupported(const String8 &mimeType)
+    {
+        if (mimeType != "video/mp4") {
+            return false;
+        }
+        return true;
+    }
+
+    status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin)
+    {
+        *plugin = new MockDrmPlugin();
+        return OK;
+    }
+
+    // MockCryptoFactory
+    bool MockCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const
+    {
+        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
+    }
+
+    status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
+                                             size_t size, CryptoPlugin **plugin)
+    {
+        *plugin = new MockCryptoPlugin();
+        return OK;
+    }
+
+
+    // MockDrmPlugin methods
+
+    status_t MockDrmPlugin::openSession(Vector<uint8_t> &sessionId)
+    {
+        const size_t kSessionIdSize = 8;
+
+        Mutex::Autolock lock(mLock);
+        for (size_t i = 0; i < kSessionIdSize / sizeof(long); i++) {
+            long r = random();
+            sessionId.appendArray((uint8_t *)&r, sizeof(long));
+        }
+        mSessions.add(sessionId);
+
+        ALOGD("MockDrmPlugin::openSession() -> %s", vectorToString(sessionId).string());
+        return OK;
+    }
+
+    status_t MockDrmPlugin::closeSession(Vector<uint8_t> const &sessionId)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::closeSession(%s)", vectorToString(sessionId).string());
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+        mSessions.removeAt(index);
+        return OK;
+    }
+
+
+    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::getKeyRequest(sessionId=%s, initData=%s, mimeType=%s"
+              ", keyType=%d, optionalParameters=%s))",
+              vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(),
+              keyType, stringMapToString(optionalParameters).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[] initData           -> mock-initdata
+        //   string mimeType           -> mock-mimetype
+        //   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 keyTypeStr;
+        keyTypeStr.appendFormat("%d", (int)keyType);
+        mStringProperties.add(String8("mock-keytype"), keyTypeStr);
+
+        String8 params;
+        for (size_t i = 0; i < optionalParameters.size(); i++) {
+            params.appendFormat("%s{%s,%s}", i ? "," : "",
+                                optionalParameters.keyAt(i).string(),
+                                optionalParameters.valueAt(i).string());
+        }
+        mStringProperties.add(String8("mock-optparams"), params);
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   byte[] mock-request       -> request
+        //   string mock-default-url   -> defaultUrl
+
+        index = mByteArrayProperties.indexOfKey(String8("mock-request"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-request' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            request = mByteArrayProperties.valueAt(index);
+        }
+
+        index = mStringProperties.indexOfKey(String8("mock-defaultUrl"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-defaultUrl' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            defaultUrl = mStringProperties.valueAt(index);
+        }
+        return OK;
+    }
+
+    status_t MockDrmPlugin::provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                               Vector<uint8_t> const &response,
+                                               Vector<uint8_t> &keySetId)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::provideKeyResponse(sessionId=%s, response=%s)",
+              vectorToString(sessionId).string(), vectorToString(response).string());
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+        if (response.size() == 0) {
+            return BAD_VALUE;
+        }
+
+        // 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::removeKeys(Vector<uint8_t> const &keySetId)
+    {
+        Mutex::Autolock lock(mLock);
+        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::queryKeyStatus(Vector<uint8_t> const &sessionId,
+                                               KeyedVector<String8, String8> &infoMap) const
+    {
+        ALOGD("MockDrmPlugin::queryKeyStatus(sessionId=%s)",
+              vectorToString(sessionId).string());
+
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        infoMap.add(String8("purchaseDuration"), String8("1000"));
+        infoMap.add(String8("licenseDuration"), String8("100"));
+        return OK;
+    }
+
+    status_t MockDrmPlugin::getProvisionRequest(Vector<uint8_t> &request,
+                                                String8 &defaultUrl)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::getProvisionRequest()");
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   byte[] mock-request       -> request
+        //   string mock-default-url   -> defaultUrl
+
+        ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-request"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-request' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            request = mByteArrayProperties.valueAt(index);
+        }
+
+        index = mStringProperties.indexOfKey(String8("mock-defaultUrl"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-defaultUrl' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            defaultUrl = mStringProperties.valueAt(index);
+        }
+        return OK;
+    }
+
+    status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
+              vectorToString(response).string());
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] response            -> mock-response
+
+        mByteArrayProperties.add(String8("mock-response"), response);
+        return OK;
+    }
+
+    status_t MockDrmPlugin::getSecureStops(List<Vector<uint8_t> > &secureStops)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::getSecureStops()");
+
+        // Properties used in mock test, set by cts test app returned from mock plugin
+        //   byte[] mock-secure-stop1  -> first secure stop in list
+        //   byte[] mock-secure-stop2  -> second secure stop in list
+
+        Vector<uint8_t> ss1, ss2;
+        ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-secure-stop1"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-secure-stop1' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            ss1 = mByteArrayProperties.valueAt(index);
+        }
+
+        index = mByteArrayProperties.indexOfKey(String8("mock-secure-stop2"));
+        if (index < 0) {
+            ALOGD("Missing 'mock-secure-stop2' parameter for mock");
+            return BAD_VALUE;
+        } else {
+            ss2 = mByteArrayProperties.valueAt(index);
+        }
+
+        secureStops.push_back(ss1);
+        secureStops.push_back(ss2);
+        return OK;
+    }
+
+    status_t MockDrmPlugin::releaseSecureStops(Vector<uint8_t> const &ssRelease)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::releaseSecureStops(%s)",
+              vectorToString(ssRelease).string());
+
+        // Properties used in mock test, set by mock plugin and verifed cts test app
+        //   byte[] secure-stop-release  -> mock-ssrelease
+        mByteArrayProperties.add(String8("mock-ssrelease"), ssRelease);
+
+        return OK;
+    }
+
+    status_t MockDrmPlugin::getPropertyString(String8 const &name, String8 &value) const
+    {
+        ALOGD("MockDrmPlugin::getPropertyString(name=%s)", name.string());
+        ssize_t index = mStringProperties.indexOfKey(name);
+        if (index < 0) {
+            ALOGD("no property for '%s'", name.string());
+            return BAD_VALUE;
+        }
+        value = mStringProperties.valueAt(index);
+        return OK;
+    }
+
+    status_t MockDrmPlugin::getPropertyByteArray(String8 const &name,
+                                                 Vector<uint8_t> &value) const
+    {
+        ALOGD("MockDrmPlugin::getPropertyByteArray(name=%s)", name.string());
+        ssize_t index = mByteArrayProperties.indexOfKey(name);
+        if (index < 0) {
+            ALOGD("no property for '%s'", name.string());
+            return BAD_VALUE;
+        }
+        value = mByteArrayProperties.valueAt(index);
+        return OK;
+    }
+
+    status_t MockDrmPlugin::setPropertyString(String8 const &name,
+                                              String8 const &value)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::setPropertyString(name=%s, value=%s)",
+              name.string(), value.string());
+
+        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;
+    }
+
+    status_t MockDrmPlugin::setPropertyByteArray(String8 const &name,
+                                                 Vector<uint8_t> const &value)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::setPropertyByteArray(name=%s, value=%s)",
+              name.string(), vectorToString(value).string());
+        mByteArrayProperties.add(name, value);
+        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());
+        for (size_t i = 0; i < mSessions.size(); ++i) {
+            if (memcmp(mSessions[i].array(), sessionId.array(), sessionId.size()) == 0) {
+                return i;
+            }
+        }
+        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
+    {
+        return arrayToString(vector.array(), vector.size());
+    }
+
+    String8 MockDrmPlugin::arrayToString(uint8_t const *array, size_t len) const
+    {
+        String8 result("{ ");
+        for (size_t i = 0; i < len; i++) {
+            result.appendFormat("0x%02x ", array[i]);
+        }
+        result += "}";
+        return result;
+    }
+
+    String8 MockDrmPlugin::stringMapToString(KeyedVector<String8, String8> map) const
+    {
+        String8 result("{ ");
+        for (size_t i = 0; i < map.size(); i++) {
+            result.appendFormat("%s{name=%s, value=%s}", i > 0 ? ", " : "",
+                                map.keyAt(i).string(), map.valueAt(i).string());
+        }
+        return result + " }";
+    }
+
+    bool operator<(Vector<uint8_t> const &lhs, Vector<uint8_t> const &rhs) {
+        return lhs.size() < rhs.size() || (memcmp(lhs.array(), rhs.array(), lhs.size()) < 0);
+    }
+
+    //
+    // Crypto Plugin
+    //
+
+    bool MockCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
+    {
+        ALOGD("MockCryptoPlugin::requiresSecureDecoderComponent(mime=%s)", mime);
+        return false;
+    }
+
+    ssize_t
+    MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16],
+                              Mode mode, const void *srcPtr, const SubSample *subSamples,
+                              size_t numSubSamples, void *dstPtr, AString *errorDetailMsg)
+    {
+        ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, "
+              "subSamples=%s, dst=%p)",
+              (int)secure,
+              arrayToString(key, sizeof(key)).string(),
+              arrayToString(iv, sizeof(iv)).string(),
+              (int)mode, srcPtr,
+              subSamplesToString(subSamples, numSubSamples).string(),
+              dstPtr);
+        return OK;
+    }
+
+    // Conversion utilities
+    String8 MockCryptoPlugin::arrayToString(uint8_t const *array, size_t len) const
+    {
+        String8 result("{ ");
+        for (size_t i = 0; i < len; i++) {
+            result.appendFormat("0x%02x ", array[i]);
+        }
+        result += "}";
+        return result;
+    }
+
+    String8 MockCryptoPlugin::subSamplesToString(SubSample const *subSamples,
+                                                 size_t numSubSamples) const
+    {
+        String8 result;
+        for (size_t i = 0; i < numSubSamples; i++) {
+            result.appendFormat("[%d] {clear:%d, encrypted:%d} ", i,
+                                subSamples[i].mNumBytesOfClearData,
+                                subSamples[i].mNumBytesOfEncryptedData);
+        }
+        return result;
+    }
+
+};
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
new file mode 100644
index 0000000..2297f9b
--- /dev/null
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#include <utils/Mutex.h>
+
+#include "drm/DrmAPI.h"
+#include "hardware/CryptoAPI.h"
+
+extern "C" {
+      android::DrmFactory *createDrmFactory();
+      android::CryptoFactory *createCryptoFactory();
+}
+
+namespace android {
+
+    class MockDrmFactory : public DrmFactory {
+    public:
+        MockDrmFactory() {}
+        virtual ~MockDrmFactory() {}
+
+        bool isCryptoSchemeSupported(const uint8_t uuid[16]);
+        bool isContentTypeSupported(const String8 &mimeType);
+        status_t createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin);
+    };
+
+    class MockCryptoFactory : public CryptoFactory {
+    public:
+        MockCryptoFactory() {}
+        virtual ~MockCryptoFactory() {}
+
+        bool isCryptoSchemeSupported(const uint8_t uuid[16]) const;
+        status_t createPlugin(
+            const uint8_t uuid[16], const void *data, size_t size,
+            CryptoPlugin **plugin);
+    };
+
+
+
+    class MockDrmPlugin : public DrmPlugin {
+    public:
+        MockDrmPlugin() {}
+        virtual ~MockDrmPlugin() {}
+
+        // from DrmPlugin
+        status_t openSession(Vector<uint8_t> &sessionId);
+        status_t closeSession(Vector<uint8_t> const &sessionId);
+
+        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 provideKeyResponse(Vector<uint8_t> const &sessionId,
+                                    Vector<uint8_t> const &response,
+                                    Vector<uint8_t> &keySetId);
+
+        status_t removeKeys(Vector<uint8_t> const &keySetId);
+
+        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);
+
+        status_t provideProvisionResponse(Vector<uint8_t> const &response);
+
+        status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
+        status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
+
+        status_t getPropertyString(String8 const &name, String8 &value ) const;
+        status_t getPropertyByteArray(String8 const &name,
+                                              Vector<uint8_t> &value ) const;
+
+        status_t setPropertyString(String8 const &name,
+                                   String8 const &value );
+        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;
+        KeyedVector<String8, Vector<uint8_t> > mByteArrayProperties;
+    };
+
+
+    class MockCryptoPlugin : public CryptoPlugin {
+
+        bool requiresSecureDecoderComponent(const char *mime) const;
+
+        ssize_t decrypt(bool secure,
+            const uint8_t key[16], const uint8_t iv[16],
+            Mode mode, const void *srcPtr,
+            const SubSample *subSamples, size_t numSubSamples,
+            void *dstPtr, AString *errorDetailMsg);
+    private:
+        String8 subSamplesToString(CryptoPlugin::SubSample const *subSamples, size_t numSubSamples) const;
+        String8 arrayToString(uint8_t const *array, size_t len) const;
+    };
+};
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2e55c4f..5c3abd0 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2366,6 +2366,10 @@
 
     while (countBuffersOwnedByNativeWindow() > (size_t)minUndequeuedBufs
             && dequeueBufferFromNativeWindow() != NULL) {
+        // these buffers will be submitted as regular buffers; account for this
+        if (mStoreMetaDataInOutputBuffers && mMetaDataBuffersToSubmit > 0) {
+            --mMetaDataBuffersToSubmit;
+        }
     }
 }
 
@@ -4000,10 +4004,9 @@
 }
 
 void ACodec::ExecutingState::submitOutputBuffers() {
+    submitRegularOutputBuffers();
     if (mCodec->mStoreMetaDataInOutputBuffers) {
         submitOutputMetaBuffers();
-    } else {
-        submitRegularOutputBuffers();
     }
 }
 
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index bc6d629..243888c 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -200,6 +200,13 @@
         const Media &item = mMediaItems.itemAt(i);
         const char *lang = item.mLanguage.empty() ? "und" : item.mLanguage.c_str();
         reply->writeString16(String16(lang));
+
+        if (mType == TYPE_SUBS) {
+            // TO-DO: pass in a MediaFormat instead
+            reply->writeInt32(!!(item.mFlags & MediaGroup::FLAG_AUTOSELECT));
+            reply->writeInt32(!!(item.mFlags & MediaGroup::FLAG_DEFAULT));
+            reply->writeInt32(!!(item.mFlags & MediaGroup::FLAG_FORCED));
+        }
     }
 }
 
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 900b411..4be292f 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -296,9 +296,14 @@
         return 0;
     }
     // already checked by client, but double-check in case the client wrapper is bypassed
-    if (uint32_t(inputSource) >= AUDIO_SOURCE_CNT) {
+    if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) {
         return 0;
     }
+
+    if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
+        return 0;
+    }
+
     Mutex::Autolock _l(mLock);
     // the audio_in_acoustics_t parameter is ignored by get_input()
     audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate,
@@ -308,7 +313,10 @@
         return input;
     }
     // create audio pre processors according to input source
-    ssize_t index = mInputSources.indexOfKey(inputSource);
+    audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
+                                    AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
+
+    ssize_t index = mInputSources.indexOfKey(aliasSource);
     if (index < 0) {
         return input;
     }
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 0308b99..f7ad6b1 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -117,7 +117,10 @@
     enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE};
     mutable uint8_t     mFillingUpStatus;
     int8_t              mRetryCount;
-    const sp<IMemory>   mSharedBuffer;
+
+    // see comment at AudioFlinger::PlaybackThread::Track::~Track for why this can't be const
+    sp<IMemory>         mSharedBuffer;
+
     bool                mResetDone;
     const audio_stream_type_t mStreamType;
     int                 mName;      // track name on the normal mixer,
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index ffe3e9f..cd8f70c 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -36,6 +36,7 @@
 
             void        destroy();
 
+            void        invalidate();
             // clear the buffer overflow flag
             void        clearOverflow() { mOverflow = false; }
             // set the buffer overflow flag and return previous value
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index 9ee513b..152455d 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -43,6 +43,13 @@
     return ok;
 }
 
+bool captureHotwordAllowed() {
+    static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
+    bool ok = checkCallingPermission(sCaptureHotwordAllowed);
+    if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
+    return ok;
+}
+
 bool settingsAllowed() {
     if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
     static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 175cd28..531bc56 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -22,6 +22,7 @@
 
 bool recordingAllowed();
 bool captureAudioOutputAllowed();
+bool captureHotwordAllowed();
 bool settingsAllowed();
 bool dumpAllowed();
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e35f47e..4234965 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -109,6 +109,9 @@
 // maximum normal mix buffer size
 static const uint32_t kMaxNormalMixBufferSizeMs = 24;
 
+// Offloaded output thread standby delay: allows track transition without going to standby
+static const nsecs_t kOffloadStandbyDelayNs = seconds(1);
+
 // Whether to use fast mixer
 static const enum {
     FastMixer_Never,    // never initialize or use: for debugging only
@@ -2137,13 +2140,11 @@
                 mWaitWorkCV.wait(mLock);
                 ALOGV("async completion/wake");
                 acquireWakeLock_l();
+                standbyTime = systemTime() + standbyDelay;
+                sleepTime = 0;
                 if (exitPending()) {
                     break;
                 }
-                if (!mActiveTracks.size() && (systemTime() > standbyTime)) {
-                    continue;
-                }
-                sleepTime = 0;
             } else if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
                                    isSuspended()) {
                 // put audio hardware into standby after short delay
@@ -3509,7 +3510,8 @@
 
             if (track->mFillingUpStatus == Track::FS_FILLED) {
                 track->mFillingUpStatus = Track::FS_ACTIVE;
-                mLeftVolFloat = mRightVolFloat = 0;
+                // make sure processVolume_l() will apply new volume even if 0
+                mLeftVolFloat = mRightVolFloat = -1.0;
                 if (track->mState == TrackBase::RESUMING) {
                     track->mState = TrackBase::ACTIVE;
                 }
@@ -3701,7 +3703,11 @@
 
     // use shorter standby delay as on normal output to release
     // hardware resources as soon as possible
-    standbyDelay = microseconds(activeSleepTime*2);
+    if (audio_is_linear_pcm(mFormat)) {
+        standbyDelay = microseconds(activeSleepTime*2);
+    } else {
+        standbyDelay = kOffloadStandbyDelayNs;
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -3837,6 +3843,9 @@
     size_t count = mActiveTracks.size();
 
     mixer_state mixerStatus = MIXER_IDLE;
+    bool doHwPause = false;
+    bool doHwResume = false;
+
     // find out which tracks need to be processed
     for (size_t i = 0; i < count; i++) {
         sp<Track> t = mActiveTracks[i].promote();
@@ -3868,7 +3877,7 @@
             track->setPaused();
             if (last) {
                 if (!mHwPaused) {
-                    mOutput->stream->pause(mOutput->stream);
+                    doHwPause = true;
                     mHwPaused = true;
                 }
                 // If we were part way through writing the mixbuffer to
@@ -3887,7 +3896,8 @@
             ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
             if (track->mFillingUpStatus == Track::FS_FILLED) {
                 track->mFillingUpStatus = Track::FS_ACTIVE;
-                mLeftVolFloat = mRightVolFloat = 0;
+                // make sure processVolume_l() will apply new volume even if 0
+                mLeftVolFloat = mRightVolFloat = -1.0;
                 if (track->mState == TrackBase::RESUMING) {
                     if (mPausedBytesRemaining) {
                         // Need to continue write that was interrupted
@@ -3901,7 +3911,7 @@
 
             if (last) {
                 if (mHwPaused) {
-                    mOutput->stream->resume(mOutput->stream);
+                    doHwResume = true;
                     mHwPaused = false;
                     // threadLoop_mix() will handle the case that we need to
                     // resume an interrupted write
@@ -3963,10 +3973,17 @@
         processVolume_l(track, last);
     }
 
+    // make sure the pause/flush/resume sequence is executed in the right order
+    if (doHwPause) {
+        mOutput->stream->pause(mOutput->stream);
+    }
     if (mFlushPending) {
         flushHw_l();
         mFlushPending = false;
     }
+    if (doHwResume) {
+        mOutput->stream->resume(mOutput->stream);
+    }
 
     // remove all the tracks that need to be...
     removeTracks_l(*tracksToRemove);
@@ -4451,6 +4468,10 @@
 
     {
         Mutex::Autolock _l(mLock);
+        for (size_t i = 0; i < mTracks.size(); i++) {
+            sp<RecordTrack> track = mTracks[i];
+            track->invalidate();
+        }
         mActiveTrack.clear();
         mStartStopCond.broadcast();
     }
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6002aa3..3b1874e 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -364,6 +364,16 @@
 AudioFlinger::PlaybackThread::Track::~Track()
 {
     ALOGV("PlaybackThread::Track destructor");
+
+    // The destructor would clear mSharedBuffer,
+    // but it will not push the decremented reference count,
+    // leaving the client's IMemory dangling indefinitely.
+    // This prevents that leak.
+    if (mSharedBuffer != 0) {
+        mSharedBuffer.clear();
+        // flush the binder command buffer
+        IPCThreadState::self()->flushCommands();
+    }
 }
 
 void AudioFlinger::PlaybackThread::Track::destroy()
@@ -392,7 +402,7 @@
 
 /*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
 {
-    result.append("   Name Client Type Fmt Chn mask Session fCount S F SRate  "
+    result.append("   Name Client Type      Fmt Chn mask Session fCount S F SRate  "
                   "L dB  R dB    Server Main buf  Aux Buf Flags UndFrmCnt\n");
 }
 
@@ -457,7 +467,7 @@
         nowInUnderrun = '?';
         break;
     }
-    snprintf(&buffer[7], size-7, " %6u %4u %3u %08X %7u %6u %1c %1d %5u %5.2g %5.2g  "
+    snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6u %1c %1d %5u %5.2g %5.2g  "
                                  "%08X %08X %08X 0x%03X %9u%c\n",
             (mClient == 0) ? getpid_cached : mClient->pid(),
             mStreamType,
@@ -1770,6 +1780,16 @@
     }
 }
 
+void AudioFlinger::RecordThread::RecordTrack::invalidate()
+{
+    // FIXME should use proxy, and needs work
+    audio_track_cblk_t* cblk = mCblk;
+    android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+    android_atomic_release_store(0x40000000, &cblk->mFutex);
+    // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+    (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+}
+
 
 /*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
 {
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 0459866..ad55feb 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -58,13 +58,13 @@
     res = buildQuirks();
     if (res != OK) return res;
 
-    camera_metadata_ro_entry_t availableProcessedSizes =
-        staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, 2);
-    if (!availableProcessedSizes.count) return NO_INIT;
+    const Size MAX_PREVIEW_SIZE = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+    res = getFilteredPreviewSizes(MAX_PREVIEW_SIZE, &availablePreviewSizes);
+    if (res != OK) return res;
 
     // TODO: Pick more intelligently
-    previewWidth = availableProcessedSizes.data.i32[0];
-    previewHeight = availableProcessedSizes.data.i32[1];
+    previewWidth = availablePreviewSizes[0].width;
+    previewHeight = availablePreviewSizes[0].height;
     videoWidth = previewWidth;
     videoHeight = previewHeight;
 
@@ -75,12 +75,13 @@
                     previewWidth, previewHeight));
     {
         String8 supportedPreviewSizes;
-        for (size_t i=0; i < availableProcessedSizes.count; i += 2) {
+        for (size_t i = 0; i < availablePreviewSizes.size(); i++) {
             if (i != 0) supportedPreviewSizes += ",";
             supportedPreviewSizes += String8::format("%dx%d",
-                    availableProcessedSizes.data.i32[i],
-                    availableProcessedSizes.data.i32[i+1]);
+                    availablePreviewSizes[i].width,
+                    availablePreviewSizes[i].height);
         }
+        ALOGV("Supported preview sizes are: %s", supportedPreviewSizes.string());
         params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES,
                 supportedPreviewSizes);
         params.set(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES,
@@ -1072,15 +1073,13 @@
                     validatedParams.previewWidth, validatedParams.previewHeight);
             return BAD_VALUE;
         }
-        camera_metadata_ro_entry_t availablePreviewSizes =
-            staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
-        for (i = 0; i < availablePreviewSizes.count; i += 2 ) {
-            if ((availablePreviewSizes.data.i32[i] ==
+        for (i = 0; i < availablePreviewSizes.size(); i++) {
+            if ((availablePreviewSizes[i].width ==
                     validatedParams.previewWidth) &&
-                (availablePreviewSizes.data.i32[i+1] ==
+                (availablePreviewSizes[i].height ==
                     validatedParams.previewHeight)) break;
         }
-        if (i == availablePreviewSizes.count) {
+        if (i == availablePreviewSizes.size()) {
             ALOGE("%s: Requested preview size %d x %d is not supported",
                     __FUNCTION__, validatedParams.previewWidth,
                     validatedParams.previewHeight);
@@ -1618,15 +1617,13 @@
                     __FUNCTION__);
             return BAD_VALUE;
         }
-        camera_metadata_ro_entry_t availableVideoSizes =
-            staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
-        for (i = 0; i < availableVideoSizes.count; i += 2 ) {
-            if ((availableVideoSizes.data.i32[i] ==
+        for (i = 0; i < availablePreviewSizes.size(); i++) {
+            if ((availablePreviewSizes[i].width ==
                     validatedParams.videoWidth) &&
-                (availableVideoSizes.data.i32[i+1] ==
+                (availablePreviewSizes[i].height ==
                     validatedParams.videoHeight)) break;
         }
-        if (i == availableVideoSizes.count) {
+        if (i == availablePreviewSizes.size()) {
             ALOGE("%s: Requested video size %d x %d is not supported",
                     __FUNCTION__, validatedParams.videoWidth,
                     validatedParams.videoHeight);
@@ -2447,6 +2444,38 @@
     return cropYToArray(normalizedYToCrop(y));
 }
 
+status_t Parameters::getFilteredPreviewSizes(Size limit, Vector<Size> *sizes) {
+    if (info == NULL) {
+        ALOGE("%s: Static metadata is not initialized", __FUNCTION__);
+        return NO_INIT;
+    }
+    if (sizes == NULL) {
+        ALOGE("%s: Input size is null", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    const size_t SIZE_COUNT = sizeof(Size) / sizeof(int);
+    camera_metadata_ro_entry_t availableProcessedSizes =
+        staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT);
+    if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE;
+
+    Size previewSize;
+    for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) {
+        previewSize.width = availableProcessedSizes.data.i32[i];
+        previewSize.height = availableProcessedSizes.data.i32[i+1];
+            // Need skip the preview sizes that are too large.
+            if (previewSize.width <= limit.width &&
+                    previewSize.height <= limit.height) {
+                sizes->push(previewSize);
+            }
+    }
+    if (sizes->isEmpty()) {
+        ALOGE("generated preview size list is empty!!");
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
 Parameters::CropRegion Parameters::calculateCropRegion(
                             Parameters::CropRegion::Outputs outputs) const {
 
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 464830c..a7111a3 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -105,6 +105,11 @@
     };
     Vector<Area> focusingAreas;
 
+    struct Size {
+        int32_t width;
+        int32_t height;
+    };
+
     int32_t exposureCompensation;
     bool autoExposureLock;
     bool autoWhiteBalanceLock;
@@ -159,6 +164,9 @@
 
     // Number of zoom steps to simulate
     static const unsigned int NUM_ZOOM_STEPS = 100;
+    // Max preview size allowed
+    static const unsigned int MAX_PREVIEW_WIDTH = 1920;
+    static const unsigned int MAX_PREVIEW_HEIGHT = 1080;
 
     // Full static camera info, object owned by someone else, such as
     // Camera2Device.
@@ -317,6 +325,10 @@
     int cropYToNormalized(int y) const;
     int normalizedXToCrop(int x) const;
     int normalizedYToCrop(int y) const;
+
+    Vector<Size> availablePreviewSizes;
+    // Get size list (that are no larger than limit) from static metadata.
+    status_t getFilteredPreviewSizes(Size limit, Vector<Size> *sizes);
 };
 
 // This class encapsulates the Parameters class so that it can only be accessed