Merge changes Ic92924ce,I53107a39 into jb-mr2-dev

* changes:
  Camera2: getSceneMode should return NULL when there are no supported scene modes
  commandStartFaceDetectionL returned the wrong value when face detection is not supported by the HAL. This caused the JNI function to fail since it expected a BAD_VALUE response in that case.
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp
index fac2acc..cca33e0 100644
--- a/cmds/stagefright/muxer.cpp
+++ b/cmds/stagefright/muxer.cpp
@@ -55,7 +55,8 @@
         const char *outputFileName,
         bool enableTrim,
         int trimStartTimeMs,
-        int trimEndTimeMs) {
+        int trimEndTimeMs,
+        int rotationDegrees) {
     sp<NuMediaExtractor> extractor = new NuMediaExtractor;
     if (extractor->setDataSource(path) != OK) {
         fprintf(stderr, "unable to instantiate extractor. %s\n", path);
@@ -141,6 +142,7 @@
     size_t trackIndex = -1;
     sp<ABuffer> newBuffer = new ABuffer(bufferSize);
 
+    muxer->setOrientationHint(rotationDegrees);
     muxer->start();
 
     while (!sawInputEOS) {
@@ -210,12 +212,13 @@
     char *outputFileName = NULL;
     int trimStartTimeMs = -1;
     int trimEndTimeMs = -1;
+    int rotationDegrees = 0;
     // When trimStartTimeMs and trimEndTimeMs seems valid, we turn this switch
     // to true.
     bool enableTrim = false;
 
     int res;
-    while ((res = getopt(argc, argv, "h?avo:s:e:")) >= 0) {
+    while ((res = getopt(argc, argv, "h?avo:s:e:r:")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -247,6 +250,12 @@
                 break;
             }
 
+            case 'r':
+            {
+                rotationDegrees = atoi(optarg);
+                break;
+            }
+
             case '?':
             case 'h':
             default:
@@ -288,7 +297,7 @@
     looper->start();
 
     int result = muxing(looper, argv[0], useAudio, useVideo, outputFileName,
-                        enableTrim, trimStartTimeMs, trimEndTimeMs);
+                        enableTrim, trimStartTimeMs, trimEndTimeMs, rotationDegrees);
 
     looper->stop();
 
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
new file mode 100644
index 0000000..a056cd8
--- /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
+
+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..91f5c9c
--- /dev/null
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -0,0 +1,417 @@
+/*
+ * 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"
+
+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)));
+    }
+
+    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::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)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::getLicenseRequest(sessionId=%s, initData=%s, mimeType=%s"
+              ", licenseType=%d, optionalParameters=%s))",
+              vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(),
+              licenseType, 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 licenseType        -> mock-licensetype
+        //   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 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::provideLicenseResponse(Vector<uint8_t> const &sessionId,
+                                                   Vector<uint8_t> const &response)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::provideLicenseResponse(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);
+
+        return OK;
+    }
+
+    status_t MockDrmPlugin::removeLicense(Vector<uint8_t> const &sessionId)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::removeLicense(sessionId=%s)",
+              vectorToString(sessionId).string());
+        ssize_t index = findSession(sessionId);
+        if (index == kNotFound) {
+            ALOGD("Invalid sessionId");
+            return BAD_VALUE;
+        }
+
+        return OK;
+    }
+
+    status_t MockDrmPlugin::queryLicenseStatus(Vector<uint8_t> const &sessionId,
+                                               KeyedVector<String8, String8> &infoMap) const
+    {
+        ALOGD("MockDrmPlugin::queryLicenseStatus(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()");
+        const uint8_t ss1[] = {0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89};
+        const uint8_t ss2[] = {0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99};
+
+        Vector<uint8_t> vec;
+        vec.appendArray(ss1, sizeof(ss1));
+        secureStops.push_back(vec);
+
+        vec.clear();
+        vec.appendArray(ss2, sizeof(ss2));
+        secureStops.push_back(vec);
+        return OK;
+    }
+
+    status_t MockDrmPlugin::releaseSecureStops(Vector<uint8_t> const &ssRelease)
+    {
+        Mutex::Autolock lock(mLock);
+        ALOGD("MockDrmPlugin::releaseSecureStops(%s)",
+              vectorToString(ssRelease).string());
+        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());
+        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;
+    }
+
+    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;
+    }
+
+    // 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..d46a127
--- /dev/null
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -0,0 +1,122 @@
+/*
+ * 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]);
+        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
+            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 provideLicenseResponse(Vector<uint8_t> const &sessionId,
+                                                Vector<uint8_t> const &response);
+
+        status_t removeLicense(Vector<uint8_t> const &sessionId);
+
+        status_t
+            queryLicenseStatus(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 );
+
+    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;
+
+        static const ssize_t kNotFound = -1;
+        ssize_t findSession(Vector<uint8_t> const &sessionId) 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/include/media/IDrm.h b/include/media/IDrm.h
new file mode 100644
index 0000000..38e2378
--- /dev/null
+++ b/include/media/IDrm.h
@@ -0,0 +1,89 @@
+/*
+ * 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 <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/drm/DrmAPI.h>
+
+#ifndef ANDROID_IDRM_H_
+
+#define ANDROID_IDRM_H_
+
+namespace android {
+
+struct AString;
+
+struct IDrm : public IInterface {
+    DECLARE_META_INTERFACE(Drm);
+
+    virtual status_t initCheck() const = 0;
+
+    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) = 0;
+
+    virtual status_t createPlugin(const uint8_t uuid[16]) = 0;
+
+    virtual status_t destroyPlugin() = 0;
+
+    virtual status_t openSession(Vector<uint8_t> &sessionId) = 0;
+
+    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;
+
+    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
+                                            Vector<uint8_t> const &response) = 0;
+
+    virtual status_t removeLicense(Vector<uint8_t> const &sessionId) = 0;
+
+    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
+                                        KeyedVector<String8, String8> &infoMap) const = 0;
+
+    virtual status_t getProvisionRequest(Vector<uint8_t> &request,
+                                         String8 &defaulUrl) = 0;
+
+    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response) = 0;
+
+    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0;
+
+    virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 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;
+    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;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(IDrm);
+};
+
+struct BnDrm : public BnInterface<IDrm> {
+    virtual status_t onTransact(
+            uint32_t code, const Parcel &data, Parcel *reply,
+            uint32_t flags = 0);
+};
+
+}  // namespace android
+
+#endif // ANDROID_IDRM_H_
+
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index b29d3c7..182d563 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -32,6 +32,7 @@
 namespace android {
 
 struct ICrypto;
+struct IDrm;
 struct IHDCP;
 class IMediaRecorder;
 class IOMX;
@@ -52,6 +53,7 @@
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
     virtual sp<IOMX>            getOMX() = 0;
     virtual sp<ICrypto>         makeCrypto() = 0;
+    virtual sp<IDrm>            makeDrm() = 0;
     virtual sp<IHDCP>           makeHDCP(bool createEncryptionModule) = 0;
 
     // Connects to a remote display.
diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h
index 167d0d9..fad319f 100644
--- a/include/media/stagefright/MediaMuxer.h
+++ b/include/media/stagefright/MediaMuxer.h
@@ -77,11 +77,20 @@
     status_t start();
 
     /**
+     * Set the orientation hint.
+     * @param degrees The rotation degrees. It has to be either 0,
+     *                90, 180 or 270.
+     * @return OK if no error.
+     */
+    status_t setOrientationHint(int degrees);
+
+    /**
      * Stop muxing.
      * This method is a blocking call. Depending on how
      * much data is bufferred internally, the time needed for stopping
      * the muxer may be time consuming. UI thread is
      * not recommended for launching this call.
+     * @return OK if no error.
      */
     status_t stop();
 
@@ -104,12 +113,13 @@
 private:
     sp<MPEG4Writer> mWriter;
     Vector< sp<MediaAdapter> > mTrackList;  // Each track has its MediaAdapter.
+    sp<MetaData> mFileMeta;  // Metadata for the whole file.
 
     Mutex mMuxerLock;
 
     enum State {
-        UNINITED,
-        INITED,
+        UNINITIALIZED,
+        INITIALIZED,
         STARTED,
         STOPPED
     };
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 6b48991..1ada9c3 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -19,6 +19,7 @@
     IAudioTrack.cpp \
     IAudioRecord.cpp \
     ICrypto.cpp \
+    IDrm.cpp \
     IHDCP.cpp \
     AudioRecord.cpp \
     AudioSystem.cpp \
diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp
new file mode 100644
index 0000000..3b13ec6
--- /dev/null
+++ b/media/libmedia/IDrm.cpp
@@ -0,0 +1,551 @@
+/*
+ * 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 "IDrm"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/IDrm.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+enum {
+    INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION,
+    IS_CRYPTO_SUPPORTED,
+    CREATE_PLUGIN,
+    DESTROY_PLUGIN,
+    OPEN_SESSION,
+    CLOSE_SESSION,
+    GET_LICENSE_REQUEST,
+    PROVIDE_LICENSE_RESPONSE,
+    REMOVE_LICENSE,
+    QUERY_LICENSE_STATUS,
+    GET_PROVISION_REQUEST,
+    PROVIDE_PROVISION_RESPONSE,
+    GET_SECURE_STOPS,
+    RELEASE_SECURE_STOPS,
+    GET_PROPERTY_STRING,
+    GET_PROPERTY_BYTE_ARRAY,
+    SET_PROPERTY_STRING,
+    SET_PROPERTY_BYTE_ARRAY
+};
+
+struct BpDrm : public BpInterface<IDrm> {
+    BpDrm(const sp<IBinder> &impl)
+        : BpInterface<IDrm>(impl) {
+    }
+
+    virtual status_t initCheck() const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+        remote()->transact(INIT_CHECK, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+        data.write(uuid, 16);
+        remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply);
+
+        return reply.readInt32() != 0;
+    }
+
+    virtual status_t createPlugin(const uint8_t uuid[16]) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+        data.write(uuid, 16);
+
+        remote()->transact(CREATE_PLUGIN, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t destroyPlugin() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+        remote()->transact(DESTROY_PLUGIN, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t openSession(Vector<uint8_t> &sessionId) {
+        Parcel data, reply;
+        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);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t closeSession(Vector<uint8_t> const &sessionId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeInt32(sessionId.size());
+        data.write(sessionId.array(), sessionId.size());
+        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) {
+        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());
+
+        data.writeString8(mimeType);
+        data.writeInt32((uint32_t)licenseType);
+
+        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);
+
+        uint32_t len = reply.readInt32();
+        request.insertAt((size_t)0, len);
+        reply.read(request.editArray(), len);
+        defaultUrl = reply.readString8();
+
+        return reply.readInt32();
+    }
+
+    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
+                                            Vector<uint8_t> const &response) {
+        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);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t removeLicense(Vector<uint8_t> const &sessionId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeInt32(sessionId.size());
+        data.write(sessionId.array(), sessionId.size());
+        remote()->transact(REMOVE_LICENSE, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t queryLicenseStatus(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);
+
+        infoMap.clear();
+        size_t count = reply.readInt32();
+        for (size_t i = 0; i < count; i++) {
+            String8 key = reply.readString8();
+            String8 value = reply.readString8();
+            infoMap.add(key, value);
+        }
+        return reply.readInt32();
+    }
+
+    virtual status_t getProvisionRequest(Vector<uint8_t> &request,
+                                         String8 &defaultUrl) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        remote()->transact(GET_PROVISION_REQUEST, data, &reply);
+
+        uint32_t len = reply.readInt32();
+        request.insertAt((size_t)0, len);
+        reply.read(request.editArray(), len);
+        defaultUrl = reply.readString8();
+
+        return reply.readInt32();
+    }
+
+    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeInt32(response.size());
+        data.write(response.array(), response.size());
+        remote()->transact(PROVIDE_PROVISION_RESPONSE, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        remote()->transact(GET_SECURE_STOPS, data, &reply);
+
+        secureStops.clear();
+        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);
+            secureStops.push_back(secureStop);
+        }
+        return reply.readInt32();
+    }
+
+    virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeInt32(ssRelease.size());
+        data.write(ssRelease.array(), ssRelease.size());
+        remote()->transact(RELEASE_SECURE_STOPS, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t getPropertyString(String8 const &name, String8 &value) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeString8(name);
+        remote()->transact(GET_PROPERTY_STRING, data, &reply);
+
+        value = reply.readString8();
+        return reply.readInt32();
+    }
+
+    virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        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);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t setPropertyString(String8 const &name, String8 const &value) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeString8(name);
+        data.writeString8(value);
+        remote()->transact(SET_PROPERTY_STRING, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t setPropertyByteArray(String8 const &name,
+                                          Vector<uint8_t> const &value) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        data.writeString8(name);
+        data.writeInt32(value.size());
+        data.write(value.array(), value.size());
+        remote()->transact(SET_PROPERTY_BYTE_ARRAY, data, &reply);
+
+        return reply.readInt32();
+    }
+
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(BpDrm);
+};
+
+IMPLEMENT_META_INTERFACE(Drm, "android.drm.IDrm");
+
+////////////////////////////////////////////////////////////////////////////////
+
+status_t BnDrm::onTransact(
+    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+    switch (code) {
+        case INIT_CHECK:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            reply->writeInt32(initCheck());
+            return OK;
+        }
+
+        case IS_CRYPTO_SUPPORTED:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            uint8_t uuid[16];
+            data.read(uuid, sizeof(uuid));
+            reply->writeInt32(isCryptoSchemeSupported(uuid));
+            return OK;
+        }
+
+        case CREATE_PLUGIN:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            uint8_t uuid[16];
+            data.read(uuid, sizeof(uuid));
+            reply->writeInt32(createPlugin(uuid));
+            return OK;
+        }
+
+        case DESTROY_PLUGIN:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            reply->writeInt32(destroyPlugin());
+            return OK;
+        }
+
+        case OPEN_SESSION:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId;
+            status_t result = openSession(sessionId);
+            reply->writeInt32(sessionId.size());
+            reply->write(sessionId.array(), sessionId.size());
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case CLOSE_SESSION:
+        {
+            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(closeSession(sessionId));
+            return OK;
+        }
+
+        case GET_LICENSE_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> initData;
+            size = data.readInt32();
+            initData.insertAt((size_t)0, size);
+            data.read(initData.editArray(), size);
+
+            String8 mimeType = data.readString8();
+            DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)data.readInt32();
+
+            KeyedVector<String8, String8> optionalParameters;
+            uint32_t count = data.readInt32();
+            for (size_t i = 0; i < count; ++i) {
+                String8 key, value;
+                key = data.readString8();
+                value = data.readString8();
+                optionalParameters.add(key, value);
+            }
+
+            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());
+            reply->writeString8(defaultUrl);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case PROVIDE_LICENSE_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));
+            return OK;
+        }
+
+        case REMOVE_LICENSE:
+        {
+            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));
+            return OK;
+        }
+
+        case QUERY_LICENSE_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);
+            KeyedVector<String8, String8> infoMap;
+
+            status_t result = queryLicenseStatus(sessionId, infoMap);
+
+            size_t count = infoMap.size();
+            reply->writeInt32(count);
+            for (size_t i = 0; i < count; ++i) {
+                reply->writeString8(infoMap.keyAt(i));
+                reply->writeString8(infoMap.valueAt(i));
+            }
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case GET_PROVISION_REQUEST:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> request;
+            String8 defaultUrl;
+            status_t result = getProvisionRequest(request, defaultUrl);
+            reply->writeInt32(request.size());
+            reply->write(request.array(), request.size());
+            reply->writeString8(defaultUrl);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case PROVIDE_PROVISION_RESPONSE:
+        {
+            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);
+            reply->writeInt32(provideProvisionResponse(response));
+
+            return OK;
+        }
+
+        case GET_SECURE_STOPS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            List<Vector<uint8_t> > secureStops;
+            status_t result = getSecureStops(secureStops);
+            size_t count = secureStops.size();
+            reply->writeInt32(count);
+            List<Vector<uint8_t> >::iterator iter = secureStops.begin();
+            while(iter != secureStops.end()) {
+                size_t size = iter->size();
+                reply->writeInt32(size);
+                reply->write(iter->array(), iter->size());
+            }
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case RELEASE_SECURE_STOPS:
+        {
+            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);
+            reply->writeInt32(releaseSecureStops(ssRelease));
+            return OK;
+        }
+
+        case GET_PROPERTY_STRING:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            String8 name = data.readString8();
+            String8 value;
+            status_t result = getPropertyString(name, value);
+            reply->writeString8(value);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case GET_PROPERTY_BYTE_ARRAY:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            String8 name = data.readString8();
+            Vector<uint8_t> value;
+            status_t result = getPropertyByteArray(name, value);
+            reply->writeInt32(value.size());
+            reply->write(value.array(), value.size());
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case SET_PROPERTY_STRING:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            String8 name = data.readString8();
+            String8 value = data.readString8();
+            reply->writeInt32(setPropertyString(name, value));
+            return OK;
+        }
+
+        case SET_PROPERTY_BYTE_ARRAY:
+        {
+            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);
+            reply->writeInt32(setPropertyByteArray(name, value));
+            return OK;
+        }
+
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index a95f4c9..e1ce5a9 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -21,6 +21,7 @@
 #include <binder/Parcel.h>
 #include <binder/IMemory.h>
 #include <media/ICrypto.h>
+#include <media/IDrm.h>
 #include <media/IHDCP.h>
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
@@ -42,6 +43,7 @@
     CREATE_METADATA_RETRIEVER,
     GET_OMX,
     MAKE_CRYPTO,
+    MAKE_DRM,
     MAKE_HDCP,
     ADD_BATTERY_DATA,
     PULL_BATTERY_DATA,
@@ -123,6 +125,13 @@
         return interface_cast<ICrypto>(reply.readStrongBinder());
     }
 
+    virtual sp<IDrm> makeDrm() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+        remote()->transact(MAKE_DRM, data, &reply);
+        return interface_cast<IDrm>(reply.readStrongBinder());
+    }
+
     virtual sp<IHDCP> makeHDCP(bool createEncryptionModule) {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -225,6 +234,12 @@
             reply->writeStrongBinder(crypto->asBinder());
             return NO_ERROR;
         } break;
+        case MAKE_DRM: {
+            CHECK_INTERFACE(IMediaPlayerService, data, reply);
+            sp<IDrm> drm = makeDrm();
+            reply->writeStrongBinder(drm->asBinder());
+            return NO_ERROR;
+        } break;
         case MAKE_HDCP: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
             bool createEncryptionModule = data.readInt32();
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 48f48e4..2a6f3c7 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -9,6 +9,7 @@
 LOCAL_SRC_FILES:=               \
     ActivityManager.cpp         \
     Crypto.cpp                  \
+    Drm.cpp                     \
     HDCP.cpp                    \
     MediaPlayerFactory.cpp      \
     MediaPlayerService.cpp      \
@@ -17,6 +18,7 @@
     MidiFile.cpp                \
     MidiMetadataRetriever.cpp   \
     RemoteDisplay.cpp           \
+    SharedLibrary.cpp           \
     StagefrightPlayer.cpp       \
     StagefrightRecorder.cpp     \
     TestPlayerStub.cpp          \
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
new file mode 100644
index 0000000..6ac7530
--- /dev/null
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -0,0 +1,423 @@
+/*
+ * 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 "Drm"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include "Drm.h"
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+KeyedVector<Vector<uint8_t>, String8> Drm::mUUIDToLibraryPathMap;
+KeyedVector<String8, wp<SharedLibrary> > Drm::mLibraryPathToOpenLibraryMap;
+Mutex Drm::mMapLock;
+
+static bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
+    if (lhs.size() < rhs.size()) {
+        return true;
+    } else if (lhs.size() > rhs.size()) {
+        return false;
+    }
+
+    return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
+}
+
+Drm::Drm()
+    : mInitCheck(NO_INIT),
+      mFactory(NULL),
+      mPlugin(NULL) {
+}
+
+Drm::~Drm() {
+    delete mPlugin;
+    mPlugin = NULL;
+    closeFactory();
+}
+
+void Drm::closeFactory() {
+    delete mFactory;
+    mFactory = NULL;
+    mLibrary.clear();
+}
+
+status_t Drm::initCheck() const {
+    return mInitCheck;
+}
+
+
+/*
+ * Search the plugins directory for a plugin that supports the scheme
+ * specified by uuid
+ *
+ * If found:
+ *    mLibrary holds a strong pointer to the dlopen'd library
+ *    mFactory is set to the library's factory method
+ *    mInitCheck is set to OK
+ *
+ * If not found:
+ *    mLibrary is cleared and mFactory are set to NULL
+ *    mInitCheck is set to an error (!OK)
+ */
+void Drm::findFactoryForScheme(const uint8_t uuid[16]) {
+
+    closeFactory();
+
+    // lock static maps
+    Mutex::Autolock autoLock(mMapLock);
+
+    // first check cache
+    Vector<uint8_t> uuidVector;
+    uuidVector.appendArray(uuid, sizeof(uuid));
+    ssize_t index = mUUIDToLibraryPathMap.indexOfKey(uuidVector);
+    if (index >= 0) {
+        if (loadLibraryForScheme(mUUIDToLibraryPathMap[index], uuid)) {
+            mInitCheck = OK;
+            return;
+        } else {
+            ALOGE("Failed to load from cached library path!");
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+    }
+
+    // no luck, have to search
+    String8 dirPath("/vendor/lib/mediadrm");
+    DIR* pDir = opendir(dirPath.string());
+
+    if (pDir == NULL) {
+        mInitCheck = ERROR_UNSUPPORTED;
+        ALOGE("Failed to open plugin directory %s", dirPath.string());
+        return;
+    }
+
+
+    struct dirent* pEntry;
+    while ((pEntry = readdir(pDir))) {
+
+        String8 pluginPath = dirPath + "/" + pEntry->d_name;
+
+        if (pluginPath.getPathExtension() == ".so") {
+
+            if (loadLibraryForScheme(pluginPath, uuid)) {
+                mUUIDToLibraryPathMap.add(uuidVector, pluginPath);
+                mInitCheck = OK;
+                closedir(pDir);
+                return;
+            }
+        }
+    }
+
+    closedir(pDir);
+
+    ALOGE("Failed to find drm plugin");
+    mInitCheck = ERROR_UNSUPPORTED;
+}
+
+bool Drm::loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]) {
+
+    // get strong pointer to open shared library
+    ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
+    if (index >= 0) {
+        mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
+    } else {
+        index = mLibraryPathToOpenLibraryMap.add(path, NULL);
+    }
+
+    if (!mLibrary.get()) {
+        mLibrary = new SharedLibrary(path);
+        if (!*mLibrary) {
+            return false;
+        }
+
+        mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
+    }
+
+    typedef DrmFactory *(*CreateDrmFactoryFunc)();
+
+    CreateDrmFactoryFunc createDrmFactory =
+        (CreateDrmFactoryFunc)mLibrary->lookup("createDrmFactory");
+
+    if (createDrmFactory == NULL ||
+        (mFactory = createDrmFactory()) == NULL ||
+        !mFactory->isCryptoSchemeSupported(uuid)) {
+        closeFactory();
+        return false;
+    }
+    return true;
+}
+
+bool Drm::isCryptoSchemeSupported(const uint8_t uuid[16]) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mFactory && mFactory->isCryptoSchemeSupported(uuid)) {
+        return true;
+    }
+
+    findFactoryForScheme(uuid);
+    return (mInitCheck == OK);
+}
+
+status_t Drm::createPlugin(const uint8_t uuid[16]) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mPlugin != NULL) {
+        return -EINVAL;
+    }
+
+    if (!mFactory || !mFactory->isCryptoSchemeSupported(uuid)) {
+        findFactoryForScheme(uuid);
+    }
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    return mFactory->createDrmPlugin(uuid, &mPlugin);
+}
+
+status_t Drm::destroyPlugin() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    delete mPlugin;
+    mPlugin = NULL;
+
+    return OK;
+}
+
+status_t Drm::openSession(Vector<uint8_t> &sessionId) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->openSession(sessionId);
+}
+
+status_t Drm::closeSession(Vector<uint8_t> const &sessionId) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    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) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->getLicenseRequest(sessionId, initData, mimeType, licenseType,
+                                      optionalParameters, request, defaultUrl);
+}
+
+status_t Drm::provideLicenseResponse(Vector<uint8_t> const &sessionId,
+                                     Vector<uint8_t> const &response) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->provideLicenseResponse(sessionId, response);
+}
+
+status_t Drm::removeLicense(Vector<uint8_t> const &sessionId) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->removeLicense(sessionId);
+}
+
+status_t Drm::queryLicenseStatus(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->queryLicenseStatus(sessionId, infoMap);
+}
+
+status_t Drm::getProvisionRequest(Vector<uint8_t> &request, String8 &defaultUrl) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->getProvisionRequest(request, defaultUrl);
+}
+
+status_t Drm::provideProvisionResponse(Vector<uint8_t> const &response) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->provideProvisionResponse(response);
+}
+
+
+status_t Drm::getSecureStops(List<Vector<uint8_t> > &secureStops) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->getSecureStops(secureStops);
+}
+
+status_t Drm::releaseSecureStops(Vector<uint8_t> const &ssRelease) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->releaseSecureStops(ssRelease);
+}
+
+status_t Drm::getPropertyString(String8 const &name, String8 &value ) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->getPropertyString(name, value);
+}
+
+status_t Drm::getPropertyByteArray(String8 const &name, Vector<uint8_t> &value ) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->getPropertyByteArray(name, value);
+}
+
+status_t Drm::setPropertyString(String8 const &name, String8 const &value ) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->setPropertyString(name, value);
+}
+
+status_t Drm::setPropertyByteArray(String8 const &name,
+                                   Vector<uint8_t> const &value ) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPlugin == NULL) {
+        return -EINVAL;
+    }
+
+    return mPlugin->setPropertyByteArray(name, value);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
new file mode 100644
index 0000000..1b10958
--- /dev/null
+++ b/media/libmediaplayerservice/Drm.h
@@ -0,0 +1,100 @@
+/*
+ * 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 DRM_H_
+
+#define DRM_H_
+
+#include "SharedLibrary.h"
+
+#include <media/IDrm.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct DrmFactory;
+struct DrmPlugin;
+
+struct Drm : public BnDrm {
+    Drm();
+    virtual ~Drm();
+
+    virtual status_t initCheck() const;
+
+    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
+
+    virtual status_t createPlugin(const uint8_t uuid[16]);
+
+    virtual status_t destroyPlugin();
+
+    virtual status_t openSession(Vector<uint8_t> &sessionId);
+
+    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);
+
+    virtual status_t provideLicenseResponse(Vector<uint8_t> const &sessionId,
+                                            Vector<uint8_t> const &response);
+
+    virtual status_t removeLicense(Vector<uint8_t> const &sessionId);
+
+    virtual status_t queryLicenseStatus(Vector<uint8_t> const &sessionId,
+                                        KeyedVector<String8, String8> &infoMap) const;
+
+    virtual status_t getProvisionRequest(Vector<uint8_t> &request,
+                                         String8 &defaulUrl);
+
+    virtual status_t provideProvisionResponse(Vector<uint8_t> const &response);
+
+    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
+
+    virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
+
+    virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
+    virtual status_t getPropertyByteArray(String8 const &name,
+                                          Vector<uint8_t> &value ) const;
+    virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
+    virtual status_t setPropertyByteArray(String8 const &name,
+                                          Vector<uint8_t> const &value ) const;
+
+private:
+    mutable Mutex mLock;
+
+    status_t mInitCheck;
+    sp<SharedLibrary> mLibrary;
+    DrmFactory *mFactory;
+    DrmPlugin *mPlugin;
+
+    static KeyedVector<Vector<uint8_t>, String8> mUUIDToLibraryPathMap;
+    static KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap;
+    static Mutex mMapLock;
+
+    void findFactoryForScheme(const uint8_t uuid[16]);
+    bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]);
+    void closeFactory();
+
+
+    DISALLOW_EVIL_CONSTRUCTORS(Drm);
+};
+
+}  // namespace android
+
+#endif  // CRYPTO_H_
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 16f1317..ec6ace1 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -72,6 +72,7 @@
 #include <OMX.h>
 
 #include "Crypto.h"
+#include "Drm.h"
 #include "HDCP.h"
 #include "RemoteDisplay.h"
 
@@ -285,6 +286,10 @@
     return new Crypto;
 }
 
+sp<IDrm> MediaPlayerService::makeDrm() {
+    return new Drm;
+}
+
 sp<IHDCP> MediaPlayerService::makeHDCP(bool createEncryptionModule) {
     return new HDCP(createEncryptionModule);
 }
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 2d2a09d..82dc29b 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -249,6 +249,7 @@
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
     virtual sp<IOMX>            getOMX();
     virtual sp<ICrypto>         makeCrypto();
+    virtual sp<IDrm>            makeDrm();
     virtual sp<IHDCP>           makeHDCP(bool createEncryptionModule);
 
     virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
diff --git a/media/libmediaplayerservice/SharedLibrary.cpp b/media/libmediaplayerservice/SharedLibrary.cpp
new file mode 100644
index 0000000..178e15d
--- /dev/null
+++ b/media/libmediaplayerservice/SharedLibrary.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "Drm"
+#include <utils/Log.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <dlfcn.h>
+
+#include "SharedLibrary.h"
+
+namespace android {
+
+    SharedLibrary::SharedLibrary(const String8 &path) {
+        mLibHandle = dlopen(path.string(), RTLD_NOW);
+    }
+
+    SharedLibrary::~SharedLibrary() {
+        if (mLibHandle != NULL) {
+            dlclose(mLibHandle);
+            mLibHandle = NULL;
+        }
+    }
+
+    bool SharedLibrary::operator!() const {
+        return mLibHandle == NULL;
+    }
+
+    void *SharedLibrary::lookup(const char *symbol) const {
+        if (!mLibHandle) {
+            return NULL;
+        }
+        return dlsym(mLibHandle, symbol);
+    }
+};
diff --git a/media/libmediaplayerservice/SharedLibrary.h b/media/libmediaplayerservice/SharedLibrary.h
new file mode 100644
index 0000000..5353642
--- /dev/null
+++ b/media/libmediaplayerservice/SharedLibrary.h
@@ -0,0 +1,39 @@
+/*
+ * 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 SHARED_LIBRARY_H_
+#define SHARED_LIBRARY_H_
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+    class SharedLibrary : public RefBase {
+    public:
+        SharedLibrary(const String8 &path);
+        ~SharedLibrary();
+
+        bool operator!() const;
+        void *lookup(const char *symbol) const;
+
+    private:
+        void *mLibHandle;
+        DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
+    };
+};
+
+#endif // SHARED_LIBRARY_H_
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 21841b3..b948fe2 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -36,18 +36,21 @@
 namespace android {
 
 MediaMuxer::MediaMuxer(const char *path, OutputFormat format)
-    : mState(UNINITED) {
+    : mState(UNINITIALIZED) {
     if (format == OUTPUT_FORMAT_MPEG_4) {
         mWriter = new MPEG4Writer(path);
-        mState = INITED;
+        mFileMeta = new MetaData;
+        mState = INITIALIZED;
     }
+
 }
 
 MediaMuxer::MediaMuxer(int fd, OutputFormat format)
-    : mState(UNINITED) {
+    : mState(UNINITIALIZED) {
     if (format == OUTPUT_FORMAT_MPEG_4) {
         mWriter = new MPEG4Writer(fd);
-        mState = INITED;
+        mFileMeta = new MetaData;
+        mState = INITIALIZED;
     }
 }
 
@@ -55,6 +58,7 @@
     Mutex::Autolock autoLock(mMuxerLock);
 
     // Clean up all the internal resources.
+    mFileMeta.clear();
     mWriter.clear();
     mTrackList.clear();
 }
@@ -67,15 +71,15 @@
         return -EINVAL;
     }
 
-    if (mState != INITED) {
+    if (mState != INITIALIZED) {
         ALOGE("addTrack() must be called after constructor and before start().");
         return INVALID_OPERATION;
     }
 
-    sp<MetaData> meta = new MetaData;
-    convertMessageToMetaData(format, meta);
+    sp<MetaData> trackMeta = new MetaData;
+    convertMessageToMetaData(format, trackMeta);
 
-    sp<MediaAdapter> newTrack = new MediaAdapter(meta);
+    sp<MediaAdapter> newTrack = new MediaAdapter(trackMeta);
     status_t result = mWriter->addSource(newTrack);
     if (result == OK) {
         return mTrackList.add(newTrack);
@@ -83,11 +87,27 @@
     return -1;
 }
 
+status_t MediaMuxer::setOrientationHint(int degrees) {
+    Mutex::Autolock autoLock(mMuxerLock);
+    if (mState != INITIALIZED) {
+        ALOGE("setOrientationHint() must be called before start().");
+        return INVALID_OPERATION;
+    }
+
+    if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) {
+        ALOGE("setOrientationHint() get invalid degrees");
+        return -EINVAL;
+    }
+
+    mFileMeta->setInt32(kKeyRotation, degrees);
+    return OK;
+}
+
 status_t MediaMuxer::start() {
     Mutex::Autolock autoLock(mMuxerLock);
-    if (mState == INITED) {
+    if (mState == INITIALIZED) {
         mState = STARTED;
-        return mWriter->start();
+        return mWriter->start(mFileMeta.get());
     } else {
         ALOGE("start() is called in invalid state %d", mState);
         return INVALID_OPERATION;
@@ -135,13 +155,13 @@
     mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned().
     mediaBuffer->set_range(buffer->offset(), buffer->size());
 
-    sp<MetaData> metaData = mediaBuffer->meta_data();
-    metaData->setInt64(kKeyTime, timeUs);
+    sp<MetaData> sampleMetaData = mediaBuffer->meta_data();
+    sampleMetaData->setInt64(kKeyTime, timeUs);
     // Just set the kKeyDecodingTime as the presentation time for now.
-    metaData->setInt64(kKeyDecodingTime, timeUs);
+    sampleMetaData->setInt64(kKeyDecodingTime, timeUs);
 
     if (flags & SAMPLE_FLAG_SYNC) {
-        metaData->setInt32(kKeyIsSyncFrame, true);
+        sampleMetaData->setInt32(kKeyIsSyncFrame, true);
     }
 
     sp<MediaAdapter> currentTrack = mTrackList[trackIndex];