am 68f66b94: Merge "MP3Extractor and MP3 decoder fixes - DO NOT MERGE" into gingerbread

* commit '68f66b945e7a69549689becd2e656d9056f1e7f5':
  MP3Extractor and MP3 decoder fixes - DO NOT MERGE
diff --git a/drm/common/Android.mk b/drm/common/Android.mk
new file mode 100644
index 0000000..c79a91a
--- /dev/null
+++ b/drm/common/Android.mk
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2010 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:= \
+    DrmConstraints.cpp \
+    DrmMetadata.cpp \
+    DrmConvertedStatus.cpp \
+    DrmEngineBase.cpp \
+    DrmInfo.cpp \
+    DrmInfoRequest.cpp \
+    DrmInfoStatus.cpp \
+    DrmRights.cpp \
+    DrmSupportInfo.cpp \
+    IDrmIOService.cpp \
+    IDrmManagerService.cpp \
+    IDrmServiceListener.cpp \
+    DrmInfoEvent.cpp \
+    ReadWriteUtils.cpp
+
+LOCAL_C_INCLUDES := \
+    $(TOP)/frameworks/base/include \
+    $(TOP)/frameworks/base/drm/libdrmframework/include \
+    $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include
+
+LOCAL_MODULE:= libdrmframeworkcommon
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/common/DrmConstraints.cpp b/drm/common/DrmConstraints.cpp
new file mode 100644
index 0000000..4a4d798
--- /dev/null
+++ b/drm/common/DrmConstraints.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmConstraints.h>
+
+using namespace android;
+
+const String8 DrmConstraints::MAX_REPEAT_COUNT("max_repeat_count");
+const String8 DrmConstraints::REMAINING_REPEAT_COUNT("remaining_repeat_count");
+const String8 DrmConstraints::LICENSE_START_TIME("license_start_time");
+const String8 DrmConstraints::LICENSE_EXPIRY_TIME("license_expiry_time");
+const String8 DrmConstraints::LICENSE_AVAILABLE_TIME("license_available_time");
+const String8 DrmConstraints::EXTENDED_METADATA("extended_metadata");
+
+int DrmConstraints::getCount(void) const {
+    return mConstraintMap.size();
+}
+
+status_t DrmConstraints::put(const String8* key, const char* value) {
+    int length = strlen(value);
+    char* charValue = new char[length + 1];
+    if (NULL != charValue) {
+        strncpy(charValue, value, length);
+        charValue[length] = '\0';
+        mConstraintMap.add(*key, charValue);
+    }
+    return DRM_NO_ERROR;
+}
+
+String8 DrmConstraints::get(const String8& key) const {
+    if (NULL != getValue(&key)) {
+        return String8(getValue(&key));
+    }
+    return String8("");
+}
+
+const char* DrmConstraints::getValue(const String8* key) const {
+    if (NAME_NOT_FOUND != mConstraintMap.indexOfKey(*key)) {
+        return mConstraintMap.valueFor(*key);
+    }
+    return NULL;
+}
+
+const char* DrmConstraints::getAsByteArray(const String8* key) const {
+    return getValue(key);
+}
+
+bool DrmConstraints::KeyIterator::hasNext() {
+    return mIndex < mDrmConstraints->mConstraintMap.size();
+}
+
+const String8& DrmConstraints::KeyIterator::next() {
+    const String8& key = mDrmConstraints->mConstraintMap.keyAt(mIndex);
+    mIndex++;
+    return key;
+}
+
+DrmConstraints::KeyIterator DrmConstraints::keyIterator() {
+    return KeyIterator(this);
+}
+
+DrmConstraints::KeyIterator::KeyIterator(const DrmConstraints::KeyIterator& keyIterator)
+    : mDrmConstraints(keyIterator.mDrmConstraints),
+      mIndex(keyIterator.mIndex) {
+}
+
+DrmConstraints::KeyIterator& DrmConstraints::KeyIterator::operator=(
+    const DrmConstraints::KeyIterator& keyIterator) {
+    mDrmConstraints = keyIterator.mDrmConstraints;
+    mIndex = keyIterator.mIndex;
+    return *this;
+}
+
+
+DrmConstraints::Iterator DrmConstraints::iterator() {
+    return Iterator(this);
+}
+
+DrmConstraints::Iterator::Iterator(const DrmConstraints::Iterator& iterator) :
+    mDrmConstraints(iterator.mDrmConstraints),
+    mIndex(iterator.mIndex) {
+}
+
+DrmConstraints::Iterator& DrmConstraints::Iterator::operator=(
+    const DrmConstraints::Iterator& iterator) {
+    mDrmConstraints = iterator.mDrmConstraints;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmConstraints::Iterator::hasNext() {
+    return mIndex < mDrmConstraints->mConstraintMap.size();
+}
+
+String8 DrmConstraints::Iterator::next() {
+    String8 value = String8(mDrmConstraints->mConstraintMap.editValueAt(mIndex));
+    mIndex++;
+    return value;
+}
+
diff --git a/drm/common/DrmConvertedStatus.cpp b/drm/common/DrmConvertedStatus.cpp
new file mode 100644
index 0000000..5d035f5
--- /dev/null
+++ b/drm/common/DrmConvertedStatus.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmConvertedStatus.h>
+
+using namespace android;
+
+DrmConvertedStatus::DrmConvertedStatus(
+    int _statusCode, const DrmBuffer* _convertedData, int _offset) :
+    statusCode(_statusCode),
+    convertedData(_convertedData),
+    offset(_offset) {
+
+}
+
diff --git a/drm/common/DrmEngineBase.cpp b/drm/common/DrmEngineBase.cpp
new file mode 100644
index 0000000..ac360eb
--- /dev/null
+++ b/drm/common/DrmEngineBase.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010 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 "DrmEngineBase.h"
+
+using namespace android;
+
+DrmEngineBase::DrmEngineBase() {
+
+}
+
+DrmEngineBase::~DrmEngineBase() {
+
+}
+
+DrmConstraints* DrmEngineBase::getConstraints(
+    int uniqueId, const String8* path, int action) {
+    return onGetConstraints(uniqueId, path, action);
+}
+
+DrmMetadata* DrmEngineBase::getMetadata(int uniqueId, const String8* path) {
+    return onGetMetadata(uniqueId, path);
+}
+
+status_t DrmEngineBase::initialize(int uniqueId) {
+    return onInitialize(uniqueId);
+}
+
+status_t DrmEngineBase::setOnInfoListener(
+    int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+    return onSetOnInfoListener(uniqueId, infoListener);
+}
+
+status_t DrmEngineBase::terminate(int uniqueId) {
+    return onTerminate(uniqueId);
+}
+
+bool DrmEngineBase::canHandle(int uniqueId, const String8& path) {
+    return onCanHandle(uniqueId, path);
+}
+
+DrmInfoStatus* DrmEngineBase::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    return onProcessDrmInfo(uniqueId, drmInfo);
+}
+
+status_t DrmEngineBase::saveRights(
+            int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) {
+    return onSaveRights(uniqueId, drmRights, rightsPath, contentPath);
+}
+
+DrmInfo* DrmEngineBase::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    return onAcquireDrmInfo(uniqueId, drmInfoRequest);
+}
+
+String8 DrmEngineBase::getOriginalMimeType(int uniqueId, const String8& path) {
+    return onGetOriginalMimeType(uniqueId, path);
+}
+
+int DrmEngineBase::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
+    return onGetDrmObjectType(uniqueId, path, mimeType);
+}
+
+int DrmEngineBase::checkRightsStatus(int uniqueId, const String8& path, int action) {
+    return onCheckRightsStatus(uniqueId, path, action);
+}
+
+status_t DrmEngineBase::consumeRights(
+    int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    return onConsumeRights(uniqueId, decryptHandle, action, reserve);
+}
+
+status_t DrmEngineBase::setPlaybackStatus(
+    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    return onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
+}
+
+bool DrmEngineBase::validateAction(
+    int uniqueId, const String8& path,
+    int action, const ActionDescription& description) {
+    return onValidateAction(uniqueId, path, action, description);
+}
+
+status_t DrmEngineBase::removeRights(int uniqueId, const String8& path) {
+    return onRemoveRights(uniqueId, path);
+}
+
+status_t DrmEngineBase::removeAllRights(int uniqueId) {
+    return onRemoveAllRights(uniqueId);
+}
+
+status_t DrmEngineBase::openConvertSession(int uniqueId, int convertId) {
+    return onOpenConvertSession(uniqueId, convertId);
+}
+
+DrmConvertedStatus* DrmEngineBase::convertData(
+    int uniqueId, int convertId, const DrmBuffer* inputData) {
+    return onConvertData(uniqueId, convertId, inputData);
+}
+
+DrmConvertedStatus* DrmEngineBase::closeConvertSession(int uniqueId, int convertId) {
+    return onCloseConvertSession(uniqueId, convertId);
+}
+
+DrmSupportInfo* DrmEngineBase::getSupportInfo(int uniqueId) {
+    return onGetSupportInfo(uniqueId);
+}
+
+status_t DrmEngineBase::openDecryptSession(
+    int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+    return onOpenDecryptSession(uniqueId, decryptHandle, fd, offset, length);
+}
+
+status_t DrmEngineBase::openDecryptSession(
+    int uniqueId, DecryptHandle* decryptHandle, const char* uri) {
+    return onOpenDecryptSession(uniqueId, decryptHandle, uri);
+}
+
+status_t DrmEngineBase::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+    return onCloseDecryptSession(uniqueId, decryptHandle);
+}
+
+status_t DrmEngineBase::initializeDecryptUnit(
+    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+    return onInitializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
+}
+
+status_t DrmEngineBase::decrypt(
+    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    return onDecrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+}
+
+status_t DrmEngineBase::finalizeDecryptUnit(
+    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    return onFinalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+}
+
+ssize_t DrmEngineBase::pread(
+    int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+    return onPread(uniqueId, decryptHandle, buffer, numBytes, offset);
+}
+
diff --git a/drm/common/DrmInfo.cpp b/drm/common/DrmInfo.cpp
new file mode 100644
index 0000000..ddcab33
--- /dev/null
+++ b/drm/common/DrmInfo.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmInfo.h>
+
+using namespace android;
+
+DrmInfo::DrmInfo(int infoType, const DrmBuffer& drmBuffer, const String8& mimeType) :
+    mInfoType(infoType),
+    mData(drmBuffer),
+    mMimeType(mimeType) {
+
+}
+
+int DrmInfo::getInfoType(void) const {
+    return mInfoType;
+}
+
+String8 DrmInfo::getMimeType(void) const {
+    return mMimeType;
+}
+
+const DrmBuffer& DrmInfo::getData(void) const {
+    return mData;
+}
+
+int DrmInfo::getCount(void) const {
+    return mAttributes.size();
+}
+
+status_t DrmInfo::put(const String8& key, const String8& value) {
+    mAttributes.add(key, value);
+    return DRM_NO_ERROR;
+}
+
+String8 DrmInfo::get(const String8& key) const {
+    if (NAME_NOT_FOUND != mAttributes.indexOfKey(key)) {
+        return mAttributes.valueFor(key);
+    }
+    return String8("");
+}
+
+int DrmInfo::indexOfKey(const String8& key) const {
+    return mAttributes.indexOfKey(key);
+}
+
+DrmInfo::KeyIterator DrmInfo::keyIterator() const {
+    return KeyIterator(this);
+}
+
+DrmInfo::Iterator DrmInfo::iterator() const {
+    return Iterator(this);
+}
+
+// KeyIterator implementation
+DrmInfo::KeyIterator::KeyIterator(const DrmInfo::KeyIterator& keyIterator) :
+    mDrmInfo(keyIterator.mDrmInfo), mIndex(keyIterator.mIndex) {
+
+}
+
+bool DrmInfo::KeyIterator::hasNext() {
+    return (mIndex < mDrmInfo->mAttributes.size());
+}
+
+const String8& DrmInfo::KeyIterator::next() {
+    const String8& key = mDrmInfo->mAttributes.keyAt(mIndex);
+    mIndex++;
+    return key;
+}
+
+DrmInfo::KeyIterator& DrmInfo::KeyIterator::operator=(const DrmInfo::KeyIterator& keyIterator) {
+    mDrmInfo = keyIterator.mDrmInfo;
+    mIndex = keyIterator.mIndex;
+    return *this;
+}
+
+// Iterator implementation
+DrmInfo::Iterator::Iterator(const DrmInfo::Iterator& iterator)
+    : mDrmInfo(iterator.mDrmInfo), mIndex(iterator.mIndex) {
+
+}
+
+DrmInfo::Iterator& DrmInfo::Iterator::operator=(const DrmInfo::Iterator& iterator) {
+    mDrmInfo = iterator.mDrmInfo;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmInfo::Iterator::hasNext() {
+    return mIndex < mDrmInfo->mAttributes.size();
+}
+
+String8& DrmInfo::Iterator::next() {
+    String8& value = mDrmInfo->mAttributes.editValueAt(mIndex);
+    mIndex++;
+    return value;
+}
+
diff --git a/drm/common/DrmInfoEvent.cpp b/drm/common/DrmInfoEvent.cpp
new file mode 100644
index 0000000..8d115a8
--- /dev/null
+++ b/drm/common/DrmInfoEvent.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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/String8.h>
+#include <drm/DrmInfoEvent.h>
+
+using namespace android;
+
+DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8& message)
+    : mUniqueId(uniqueId),
+      mInfoType(infoType),
+      mMessage(message) {
+
+}
+
+int DrmInfoEvent::getUniqueId() const {
+    return mUniqueId;
+}
+
+int DrmInfoEvent::getType() const {
+    return mInfoType;
+}
+
+const String8& DrmInfoEvent::getMessage() const {
+    return mMessage;
+}
+
diff --git a/drm/common/DrmInfoRequest.cpp b/drm/common/DrmInfoRequest.cpp
new file mode 100644
index 0000000..a646859
--- /dev/null
+++ b/drm/common/DrmInfoRequest.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmInfoRequest.h>
+
+using namespace android;
+
+const String8 DrmInfoRequest::ACCOUNT_ID("account_id");
+const String8 DrmInfoRequest::SUBSCRIPTION_ID("subscription_id");
+
+DrmInfoRequest::DrmInfoRequest(int infoType, const String8& mimeType) :
+    mInfoType(infoType), mMimeType(mimeType) {
+
+}
+
+String8 DrmInfoRequest::getMimeType(void) const {
+    return mMimeType;
+}
+
+int DrmInfoRequest::getInfoType(void) const {
+    return mInfoType;
+}
+
+int DrmInfoRequest::getCount(void) const {
+    return mRequestInformationMap.size();
+}
+
+status_t DrmInfoRequest::put(const String8& key, const String8& value) {
+    mRequestInformationMap.add(key, value);
+    return DRM_NO_ERROR;
+}
+
+String8 DrmInfoRequest::get(const String8& key) const {
+    if (NAME_NOT_FOUND != mRequestInformationMap.indexOfKey(key)) {
+        return mRequestInformationMap.valueFor(key);
+    }
+    return String8("");
+}
+
+DrmInfoRequest::KeyIterator DrmInfoRequest::keyIterator() const {
+    return KeyIterator(this);
+}
+
+DrmInfoRequest::Iterator DrmInfoRequest::iterator() const {
+    return Iterator(this);
+}
+
+// KeyIterator implementation
+DrmInfoRequest::KeyIterator::KeyIterator(const DrmInfoRequest::KeyIterator& keyIterator)
+    : mDrmInfoRequest(keyIterator.mDrmInfoRequest),
+      mIndex(keyIterator.mIndex) {
+
+}
+
+bool DrmInfoRequest::KeyIterator::hasNext() {
+    return (mIndex < mDrmInfoRequest->mRequestInformationMap.size());
+}
+
+const String8& DrmInfoRequest::KeyIterator::next() {
+    const String8& key = mDrmInfoRequest->mRequestInformationMap.keyAt(mIndex);
+    mIndex++;
+    return key;
+}
+
+DrmInfoRequest::KeyIterator& DrmInfoRequest::KeyIterator::operator=(
+    const DrmInfoRequest::KeyIterator& keyIterator) {
+    mDrmInfoRequest = keyIterator.mDrmInfoRequest;
+    mIndex = keyIterator.mIndex;
+    return *this;
+}
+
+// Iterator implementation
+DrmInfoRequest::Iterator::Iterator(const DrmInfoRequest::Iterator& iterator) :
+    mDrmInfoRequest(iterator.mDrmInfoRequest), mIndex(iterator.mIndex) {
+}
+
+DrmInfoRequest::Iterator& DrmInfoRequest::Iterator::operator=(
+    const DrmInfoRequest::Iterator& iterator) {
+    mDrmInfoRequest = iterator.mDrmInfoRequest;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmInfoRequest::Iterator::hasNext() {
+    return mIndex < mDrmInfoRequest->mRequestInformationMap.size();
+}
+
+String8& DrmInfoRequest::Iterator::next() {
+    String8& value = mDrmInfoRequest->mRequestInformationMap.editValueAt(mIndex);
+    mIndex++;
+    return value;
+}
+
diff --git a/drm/common/DrmInfoStatus.cpp b/drm/common/DrmInfoStatus.cpp
new file mode 100644
index 0000000..8ec7311
--- /dev/null
+++ b/drm/common/DrmInfoStatus.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmInfoStatus.h>
+
+using namespace android;
+
+DrmInfoStatus::DrmInfoStatus(
+    int _statusCode, int _infoType, const DrmBuffer* _drmBuffer, const String8& _mimeType) :
+    statusCode(_statusCode),
+    infoType(_infoType),
+    drmBuffer(_drmBuffer),
+    mimeType(_mimeType) {
+
+}
+
diff --git a/drm/common/DrmMetadata.cpp b/drm/common/DrmMetadata.cpp
new file mode 100644
index 0000000..6cc5ec1
--- /dev/null
+++ b/drm/common/DrmMetadata.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmMetadata.h>
+
+using namespace android;
+
+int DrmMetadata::getCount(void) const {
+	return mMetadataMap.size();
+}
+
+status_t DrmMetadata::put(const String8* key,
+                          const char* value) {
+    if((value != NULL) && (key != NULL)) {
+        int length = strlen(value);
+        char* charValue = new char[length + 1];
+
+        memcpy(charValue, value, length);
+        charValue[length] = '\0';
+        mMetadataMap.add(*key, charValue);
+    }
+    return NO_ERROR;
+}
+
+String8 DrmMetadata::get(const String8& key) const {
+    if (NULL != getValue(&key)) {
+        return String8(getValue(&key));
+    }
+    else {
+        return String8("");
+    }
+}
+
+const char* DrmMetadata::getValue(const String8* key) const {
+    if(key != NULL) {
+        if (NAME_NOT_FOUND != mMetadataMap.indexOfKey(*key)) {
+            return mMetadataMap.valueFor(*key);
+        }
+        else {
+            return NULL;
+        }
+    } else {
+        return NULL;
+    }
+}
+
+const char* DrmMetadata::getAsByteArray(const String8* key) const {
+    return getValue(key);
+}
+
+bool DrmMetadata::KeyIterator::hasNext() {
+    return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+const String8& DrmMetadata::KeyIterator::next() {
+    const String8& key = mDrmMetadata->mMetadataMap.keyAt(mIndex);
+    mIndex++;
+    return key;
+}
+
+DrmMetadata::KeyIterator DrmMetadata::keyIterator() {
+    return KeyIterator(this);
+}
+
+DrmMetadata::KeyIterator::KeyIterator(const DrmMetadata::KeyIterator& keyIterator) :
+    mDrmMetadata(keyIterator.mDrmMetadata),
+    mIndex(keyIterator.mIndex) {
+    LOGV("DrmMetadata::KeyIterator::KeyIterator");
+}
+
+DrmMetadata::KeyIterator& DrmMetadata::KeyIterator::operator=(const DrmMetadata::KeyIterator& keyIterator) {
+    LOGV("DrmMetadata::KeyIterator::operator=");
+    mDrmMetadata = keyIterator.mDrmMetadata;
+    mIndex = keyIterator.mIndex;
+    return *this;
+}
+
+
+DrmMetadata::Iterator DrmMetadata::iterator() {
+    return Iterator(this);
+}
+
+DrmMetadata::Iterator::Iterator(const DrmMetadata::Iterator& iterator) :
+    mDrmMetadata(iterator.mDrmMetadata),
+    mIndex(iterator.mIndex) {
+    LOGV("DrmMetadata::Iterator::Iterator");
+}
+
+DrmMetadata::Iterator& DrmMetadata::Iterator::operator=(const DrmMetadata::Iterator& iterator) {
+    LOGV("DrmMetadata::Iterator::operator=");
+    mDrmMetadata = iterator.mDrmMetadata;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmMetadata::Iterator::hasNext() {
+    return mIndex < mDrmMetadata->mMetadataMap.size();
+}
+
+String8 DrmMetadata::Iterator::next() {
+    String8 value = String8(mDrmMetadata->mMetadataMap.editValueAt(mIndex));
+    mIndex++;
+    return value;
+}
diff --git a/drm/common/DrmRights.cpp b/drm/common/DrmRights.cpp
new file mode 100644
index 0000000..3aecb3d
--- /dev/null
+++ b/drm/common/DrmRights.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmRights.h>
+#include <ReadWriteUtils.h>
+
+using namespace android;
+
+DrmRights::DrmRights(const String8& rightsFilePath, const String8& mimeType,
+            const String8& accountId, const String8& subscriptionId) :
+    mMimeType(mimeType),
+    mAccountId(accountId),
+    mSubscriptionId(subscriptionId),
+    mRightsFromFile(NULL) {
+    int rightsLength = 0;
+    if (String8("") != rightsFilePath) {
+        rightsLength = ReadWriteUtils::readBytes(rightsFilePath, &mRightsFromFile);
+    }
+    mData = DrmBuffer(mRightsFromFile, rightsLength);
+}
+
+DrmRights::DrmRights(const DrmBuffer& rightsData, const String8& mimeType,
+            const String8& accountId, const String8& subscriptionId) :
+    mData(rightsData),
+    mMimeType(mimeType),
+    mAccountId(accountId),
+    mSubscriptionId(subscriptionId),
+    mRightsFromFile(NULL) {
+}
+
+DrmRights::~DrmRights() {
+    delete[] mRightsFromFile; mRightsFromFile = NULL;
+}
+
+const DrmBuffer& DrmRights::getData(void) const {
+    return mData;
+}
+
+String8 DrmRights::getMimeType(void) const {
+    return mMimeType;
+}
+
+String8 DrmRights::getAccountId(void) const {
+    return mAccountId;
+}
+
+String8 DrmRights::getSubscriptionId(void) const {
+    return mSubscriptionId;
+}
+
diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp
new file mode 100644
index 0000000..ffc8953
--- /dev/null
+++ b/drm/common/DrmSupportInfo.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 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 <drm/DrmSupportInfo.h>
+
+using namespace android;
+
+DrmSupportInfo::DrmSupportInfo() {
+
+}
+
+DrmSupportInfo::DrmSupportInfo(const DrmSupportInfo& drmSupportInfo):
+    mMimeTypeVector(drmSupportInfo.mMimeTypeVector),
+    mFileSuffixVector(drmSupportInfo.mFileSuffixVector),
+    mDescription(drmSupportInfo.mDescription) {
+
+}
+
+bool DrmSupportInfo::operator<(const DrmSupportInfo& drmSupportInfo) const {
+    // Do we need to check mMimeTypeVector & mFileSuffixVector ?
+    // Note Vector doesn't overrides "<" operator
+    return mDescription < drmSupportInfo.mDescription;
+}
+
+bool DrmSupportInfo::operator==(const DrmSupportInfo& drmSupportInfo) const {
+    // Do we need to check mMimeTypeVector & mFileSuffixVector ?
+    // Note Vector doesn't overrides "==" operator
+    return (mDescription == drmSupportInfo.mDescription);
+}
+
+bool DrmSupportInfo::isSupportedMimeType(const String8& mimeType) const {
+    for (unsigned int i = 0; i < mMimeTypeVector.size(); i++) {
+        const String8 item = mMimeTypeVector.itemAt(i);
+
+        if (String8("") != mimeType && item.find(mimeType) != -1) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool DrmSupportInfo::isSupportedFileSuffix(const String8& fileType) const {
+    for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) {
+        const String8 item = mFileSuffixVector.itemAt(i);
+
+        if (String8("") != fileType && item.find(fileType) != -1) {
+            return true;
+        }
+    }
+    return false;
+}
+
+DrmSupportInfo& DrmSupportInfo::operator=(const DrmSupportInfo& drmSupportInfo) {
+    mMimeTypeVector = drmSupportInfo.mMimeTypeVector;
+    mFileSuffixVector = drmSupportInfo.mFileSuffixVector;
+    mDescription = drmSupportInfo.mDescription;
+    return *this;
+}
+
+int DrmSupportInfo::getMimeTypeCount(void) const {
+    return mMimeTypeVector.size();
+}
+
+int DrmSupportInfo::getFileSuffixCount(void) const {
+    return mFileSuffixVector.size();
+}
+
+status_t DrmSupportInfo::addMimeType(const String8& mimeType) {
+    mMimeTypeVector.push(mimeType);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmSupportInfo::addFileSuffix(const String8& fileSuffix) {
+    mFileSuffixVector.push(fileSuffix);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmSupportInfo::setDescription(const String8& description) {
+    mDescription = description;
+    return DRM_NO_ERROR;
+}
+
+String8 DrmSupportInfo::getDescription() const {
+    return mDescription;
+}
+
+DrmSupportInfo::FileSuffixIterator DrmSupportInfo::getFileSuffixIterator() {
+    return FileSuffixIterator(this);
+}
+
+DrmSupportInfo::MimeTypeIterator DrmSupportInfo::getMimeTypeIterator() {
+    return MimeTypeIterator(this);
+}
+
+DrmSupportInfo::FileSuffixIterator::FileSuffixIterator(
+    const DrmSupportInfo::FileSuffixIterator& iterator) :
+    mDrmSupportInfo(iterator.mDrmSupportInfo),
+    mIndex(iterator.mIndex) {
+
+}
+
+DrmSupportInfo::FileSuffixIterator& DrmSupportInfo::FileSuffixIterator::operator=(
+    const DrmSupportInfo::FileSuffixIterator& iterator) {
+    mDrmSupportInfo = iterator.mDrmSupportInfo;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmSupportInfo::FileSuffixIterator::hasNext() {
+    return mIndex < mDrmSupportInfo->mFileSuffixVector.size();
+}
+
+String8& DrmSupportInfo::FileSuffixIterator::next() {
+    String8& value = mDrmSupportInfo->mFileSuffixVector.editItemAt(mIndex);
+    mIndex++;
+    return value;
+}
+
+DrmSupportInfo::MimeTypeIterator::MimeTypeIterator(
+    const DrmSupportInfo::MimeTypeIterator& iterator) :
+    mDrmSupportInfo(iterator.mDrmSupportInfo),
+    mIndex(iterator.mIndex) {
+
+}
+
+DrmSupportInfo::MimeTypeIterator& DrmSupportInfo::MimeTypeIterator::operator=(
+    const DrmSupportInfo::MimeTypeIterator& iterator) {
+    mDrmSupportInfo = iterator.mDrmSupportInfo;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmSupportInfo::MimeTypeIterator::hasNext() {
+    return mIndex < mDrmSupportInfo->mMimeTypeVector.size();
+}
+
+String8& DrmSupportInfo::MimeTypeIterator::next() {
+    String8& value = mDrmSupportInfo->mMimeTypeVector.editItemAt(mIndex);
+    mIndex++;
+    return value;
+}
+
diff --git a/drm/common/IDrmIOService.cpp b/drm/common/IDrmIOService.cpp
new file mode 100644
index 0000000..e44ca55
--- /dev/null
+++ b/drm/common/IDrmIOService.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <drm/drm_framework_common.h>
+#include "IDrmIOService.h"
+
+using namespace android;
+
+void BpDrmIOService::writeToFile(const String8& filePath, const String8& dataBuffer) {
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
+    data.writeString8(filePath);
+    data.writeString8(dataBuffer);
+
+    remote()->transact(WRITE_TO_FILE, data, &reply);
+}
+
+String8 BpDrmIOService::readFromFile(const String8& filePath) {
+
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmIOService::getInterfaceDescriptor());
+    data.writeString8(filePath);
+
+    remote()->transact(READ_FROM_FILE, data, &reply);
+    return reply.readString8();
+}
+
+IMPLEMENT_META_INTERFACE(DrmIOService, "drm.IDrmIOService");
+
+status_t BnDrmIOService::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+
+    switch (code) {
+    case WRITE_TO_FILE:
+    {
+        CHECK_INTERFACE(IDrmIOService, data, reply);
+
+        writeToFile(data.readString8(), data.readString8());
+        return DRM_NO_ERROR;
+    }
+
+    case READ_FROM_FILE:
+    {
+        CHECK_INTERFACE(IDrmIOService, data, reply);
+
+        String8 dataBuffer = readFromFile(data.readString8());
+        reply->writeString8(dataBuffer);
+        return DRM_NO_ERROR;
+    }
+
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
new file mode 100644
index 0000000..723b50e
--- /dev/null
+++ b/drm/common/IDrmManagerService.cpp
@@ -0,0 +1,1501 @@
+/*
+ * Copyright (C) 2010 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 "IDrmManagerService(Native)"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IPCThreadState.h>
+
+#include <drm/DrmInfo.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmRights.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmConvertedStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+
+#include "IDrmManagerService.h"
+
+#define INVALID_BUFFER_LENGTH -1
+
+using namespace android;
+
+int BpDrmManagerService::addUniqueId(int uniqueId) {
+    LOGV("add uniqueid");
+    Parcel data, reply;
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    remote()->transact(ADD_UNIQUEID, data, &reply);
+    return reply.readInt32();
+}
+
+void BpDrmManagerService::removeUniqueId(int uniqueId) {
+    LOGV("remove uniqueid");
+    Parcel data, reply;
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    remote()->transact(REMOVE_UNIQUEID, data, &reply);
+}
+
+void BpDrmManagerService::addClient(int uniqueId) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    remote()->transact(ADD_CLIENT, data, &reply);
+}
+
+void BpDrmManagerService::removeClient(int uniqueId) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    remote()->transact(REMOVE_CLIENT, data, &reply);
+}
+
+status_t BpDrmManagerService::setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
+    LOGV("setDrmServiceListener");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeStrongBinder(drmServiceListener->asBinder());
+    remote()->transact(SET_DRM_SERVICE_LISTENER, data, &reply);
+    return reply.readInt32();
+}
+
+status_t BpDrmManagerService::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+    LOGV("Install DRM Engine");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(drmEngineFile);
+
+    remote()->transact(INSTALL_DRM_ENGINE, data, &reply);
+    return reply.readInt32();
+}
+
+DrmConstraints* BpDrmManagerService::getConstraints(
+            int uniqueId, const String8* path, const int action) {
+    LOGV("Get Constraints");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(*path);
+    data.writeInt32(action);
+
+    remote()->transact(GET_CONSTRAINTS_FROM_CONTENT, data, &reply);
+
+    DrmConstraints* drmConstraints = NULL;
+    if (0 != reply.dataAvail()) {
+        //Filling Drm Constraints
+        drmConstraints = new DrmConstraints();
+
+        const int size = reply.readInt32();
+        for (int index = 0; index < size; ++index) {
+            const String8 key(reply.readString8());
+            const int bufferSize = reply.readInt32();
+            char* data = NULL;
+            if (0 < bufferSize) {
+                data = new char[bufferSize];
+                reply.read(data, bufferSize);
+            }
+            drmConstraints->put(&key, data);
+        }
+    }
+    return drmConstraints;
+}
+
+DrmMetadata* BpDrmManagerService::getMetadata(int uniqueId, const String8* path) {
+    LOGV("Get Metadata");
+    Parcel data, reply;
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    DrmMetadata* drmMetadata = NULL;
+    data.writeString8(*path);
+    remote()->transact(GET_METADATA_FROM_CONTENT, data, &reply);
+
+    if (0 != reply.dataAvail()) {
+        //Filling Drm Metadata
+        drmMetadata = new DrmMetadata();
+
+        const int size = reply.readInt32();
+        for (int index = 0; index < size; ++index) {
+            const String8 key(reply.readString8());
+            const int bufferSize = reply.readInt32();
+            char* data = NULL;
+            if (0 < bufferSize) {
+                data = new char[bufferSize];
+                reply.read(data, bufferSize);
+            }
+            drmMetadata->put(&key, data);
+        }
+    }
+    return drmMetadata;
+}
+
+bool BpDrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+    LOGV("Can Handle");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeString8(path);
+    data.writeString8(mimeType);
+
+    remote()->transact(CAN_HANDLE, data, &reply);
+
+    return static_cast<bool>(reply.readInt32());
+}
+
+DrmInfoStatus* BpDrmManagerService::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    LOGV("Process DRM Info");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    //Filling DRM info
+    data.writeInt32(drmInfo->getInfoType());
+    const DrmBuffer dataBuffer = drmInfo->getData();
+    const int dataBufferSize = dataBuffer.length;
+    data.writeInt32(dataBufferSize);
+    if (0 < dataBufferSize) {
+        data.write(dataBuffer.data, dataBufferSize);
+    }
+    data.writeString8(drmInfo->getMimeType());
+
+    data.writeInt32(drmInfo->getCount());
+    DrmInfo::KeyIterator keyIt = drmInfo->keyIterator();
+
+    while (keyIt.hasNext()) {
+        const String8 key = keyIt.next();
+        data.writeString8(key);
+        const String8 value = drmInfo->get(key);
+        data.writeString8((value == String8("")) ? String8("NULL") : value);
+    }
+
+    remote()->transact(PROCESS_DRM_INFO, data, &reply);
+
+    DrmInfoStatus* drmInfoStatus = NULL;
+    if (0 != reply.dataAvail()) {
+        //Filling DRM Info Status
+        const int statusCode = reply.readInt32();
+        const int infoType = reply.readInt32();
+        const String8 mimeType = reply.readString8();
+
+        DrmBuffer* drmBuffer = NULL;
+        if (0 != reply.dataAvail()) {
+            const int bufferSize = reply.readInt32();
+            char* data = NULL;
+            if (0 < bufferSize) {
+                data = new char[bufferSize];
+                reply.read(data, bufferSize);
+            }
+            drmBuffer = new DrmBuffer(data, bufferSize);
+        }
+        drmInfoStatus = new DrmInfoStatus(statusCode, infoType, drmBuffer, mimeType);
+    }
+    return drmInfoStatus;
+}
+
+DrmInfo* BpDrmManagerService::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) {
+    LOGV("Acquire DRM Info");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    //Filling DRM Info Request
+    data.writeInt32(drmInforequest->getInfoType());
+    data.writeString8(drmInforequest->getMimeType());
+
+    data.writeInt32(drmInforequest->getCount());
+    DrmInfoRequest::KeyIterator keyIt = drmInforequest->keyIterator();
+
+    while (keyIt.hasNext()) {
+        const String8 key = keyIt.next();
+        data.writeString8(key);
+        const String8 value = drmInforequest->get(key);
+        data.writeString8((value == String8("")) ? String8("NULL") : value);
+    }
+
+    remote()->transact(ACQUIRE_DRM_INFO, data, &reply);
+
+    DrmInfo* drmInfo = NULL;
+    if (0 != reply.dataAvail()) {
+        //Filling DRM Info
+        const int infoType = reply.readInt32();
+        const int bufferSize = reply.readInt32();
+        char* data = NULL;
+
+        if (0 < bufferSize) {
+            data = new char[bufferSize];
+            reply.read(data, bufferSize);
+        }
+        drmInfo = new DrmInfo(infoType, DrmBuffer(data, bufferSize), reply.readString8());
+
+        const int size = reply.readInt32();
+        for (int index = 0; index < size; ++index) {
+            const String8 key(reply.readString8());
+            const String8 value(reply.readString8());
+            drmInfo->put(key, (value == String8("NULL")) ? String8("") : value);
+        }
+    }
+    return drmInfo;
+}
+
+status_t BpDrmManagerService::saveRights(
+            int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) {
+    LOGV("Save Rights");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    //Filling Drm Rights
+    const DrmBuffer dataBuffer = drmRights.getData();
+    data.writeInt32(dataBuffer.length);
+    data.write(dataBuffer.data, dataBuffer.length);
+
+    const String8 mimeType = drmRights.getMimeType();
+    data.writeString8((mimeType == String8("")) ? String8("NULL") : mimeType);
+
+    const String8 accountId = drmRights.getAccountId();
+    data.writeString8((accountId == String8("")) ? String8("NULL") : accountId);
+
+    const String8 subscriptionId = drmRights.getSubscriptionId();
+    data.writeString8((subscriptionId == String8("")) ? String8("NULL") : subscriptionId);
+
+    data.writeString8((rightsPath == String8("")) ? String8("NULL") : rightsPath);
+    data.writeString8((contentPath == String8("")) ? String8("NULL") : contentPath);
+
+    remote()->transact(SAVE_RIGHTS, data, &reply);
+    return reply.readInt32();
+}
+
+String8 BpDrmManagerService::getOriginalMimeType(int uniqueId, const String8& path) {
+    LOGV("Get Original MimeType");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(path);
+
+    remote()->transact(GET_ORIGINAL_MIMETYPE, data, &reply);
+    return reply.readString8();
+}
+
+int BpDrmManagerService::getDrmObjectType(
+            int uniqueId, const String8& path, const String8& mimeType) {
+    LOGV("Get Drm object type");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(path);
+    data.writeString8(mimeType);
+
+    remote()->transact(GET_DRM_OBJECT_TYPE, data, &reply);
+
+    return reply.readInt32();
+}
+
+int BpDrmManagerService::checkRightsStatus(int uniqueId, const String8& path, int action) {
+    LOGV("checkRightsStatus");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(path);
+    data.writeInt32(action);
+
+    remote()->transact(CHECK_RIGHTS_STATUS, data, &reply);
+
+    return reply.readInt32();
+}
+
+status_t BpDrmManagerService::consumeRights(
+            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    LOGV("consumeRights");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+
+    data.writeInt32(action);
+    data.writeInt32(static_cast< int>(reserve));
+
+    remote()->transact(CONSUME_RIGHTS, data, &reply);
+    return reply.readInt32();
+}
+
+status_t BpDrmManagerService::setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    LOGV("setPlaybackStatus");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+
+    data.writeInt32(playbackStatus);
+    data.writeInt32(position);
+
+    remote()->transact(SET_PLAYBACK_STATUS, data, &reply);
+    return reply.readInt32();
+}
+
+bool BpDrmManagerService::validateAction(
+            int uniqueId, const String8& path,
+            int action, const ActionDescription& description) {
+    LOGV("validateAction");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(path);
+    data.writeInt32(action);
+    data.writeInt32(description.outputType);
+    data.writeInt32(description.configuration);
+
+    remote()->transact(VALIDATE_ACTION, data, &reply);
+
+    return static_cast<bool>(reply.readInt32());
+}
+
+status_t BpDrmManagerService::removeRights(int uniqueId, const String8& path) {
+    LOGV("removeRights");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(path);
+
+    remote()->transact(REMOVE_RIGHTS, data, &reply);
+    return reply.readInt32();
+}
+
+status_t BpDrmManagerService::removeAllRights(int uniqueId) {
+    LOGV("removeAllRights");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    remote()->transact(REMOVE_ALL_RIGHTS, data, &reply);
+    return reply.readInt32();
+}
+
+int BpDrmManagerService::openConvertSession(int uniqueId, const String8& mimeType) {
+    LOGV("openConvertSession");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(mimeType);
+
+    remote()->transact(OPEN_CONVERT_SESSION, data, &reply);
+    return reply.readInt32();
+}
+
+DrmConvertedStatus* BpDrmManagerService::convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) {
+    LOGV("convertData");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeInt32(convertId);
+    data.writeInt32(inputData->length);
+    data.write(inputData->data, inputData->length);
+
+    remote()->transact(CONVERT_DATA, data, &reply);
+
+    DrmConvertedStatus* drmConvertedStatus = NULL;
+
+    if (0 != reply.dataAvail()) {
+        //Filling DRM Converted Status
+        const int statusCode = reply.readInt32();
+        const int offset = reply.readInt32();
+
+        DrmBuffer* convertedData = NULL;
+        if (0 != reply.dataAvail()) {
+            const int bufferSize = reply.readInt32();
+            char* data = NULL;
+            if (0 < bufferSize) {
+                data = new char[bufferSize];
+                reply.read(data, bufferSize);
+            }
+            convertedData = new DrmBuffer(data, bufferSize);
+        }
+        drmConvertedStatus = new DrmConvertedStatus(statusCode, convertedData, offset);
+    }
+    return drmConvertedStatus;
+}
+
+DrmConvertedStatus* BpDrmManagerService::closeConvertSession(int uniqueId, int convertId) {
+    LOGV("closeConvertSession");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeInt32(convertId);
+
+    remote()->transact(CLOSE_CONVERT_SESSION, data, &reply);
+
+    DrmConvertedStatus* drmConvertedStatus = NULL;
+
+    if (0 != reply.dataAvail()) {
+        //Filling DRM Converted Status
+        const int statusCode = reply.readInt32();
+        const int offset = reply.readInt32();
+
+        DrmBuffer* convertedData = NULL;
+        if (0 != reply.dataAvail()) {
+            const int bufferSize = reply.readInt32();
+            char* data = NULL;
+            if (0 < bufferSize) {
+                data = new char[bufferSize];
+                reply.read(data, bufferSize);
+            }
+            convertedData = new DrmBuffer(data, bufferSize);
+        }
+        drmConvertedStatus = new DrmConvertedStatus(statusCode, convertedData, offset);
+    }
+    return drmConvertedStatus;
+}
+
+status_t BpDrmManagerService::getAllSupportInfo(
+            int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+    LOGV("Get All Support Info");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    remote()->transact(GET_ALL_SUPPORT_INFO, data, &reply);
+
+    //Filling DRM Support Info
+    const int arraySize = reply.readInt32();
+    if (0 < arraySize) {
+        *drmSupportInfoArray = new DrmSupportInfo[arraySize];
+
+        for (int index = 0; index < arraySize; ++index) {
+            DrmSupportInfo drmSupportInfo;
+
+            const int fileSuffixVectorSize = reply.readInt32();
+            for (int i = 0; i < fileSuffixVectorSize; ++i) {
+                drmSupportInfo.addFileSuffix(reply.readString8());
+            }
+
+            const int mimeTypeVectorSize = reply.readInt32();
+            for (int i = 0; i < mimeTypeVectorSize; ++i) {
+                drmSupportInfo.addMimeType(reply.readString8());
+            }
+
+            drmSupportInfo.setDescription(reply.readString8());
+            (*drmSupportInfoArray)[index] = drmSupportInfo;
+        }
+    }
+    *length = arraySize;
+    return reply.readInt32();
+}
+
+DecryptHandle* BpDrmManagerService::openDecryptSession(
+            int uniqueId, int fd, int offset, int length) {
+    LOGV("Entering BpDrmManagerService::openDecryptSession");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeFileDescriptor(fd);
+    data.writeInt32(offset);
+    data.writeInt32(length);
+
+    remote()->transact(OPEN_DECRYPT_SESSION, data, &reply);
+
+    DecryptHandle* handle = NULL;
+    if (0 != reply.dataAvail()) {
+        handle = new DecryptHandle();
+        handle->decryptId = reply.readInt32();
+        handle->mimeType = reply.readString8();
+        handle->decryptApiType = reply.readInt32();
+        handle->status = reply.readInt32();
+        handle->decryptInfo = NULL;
+        if (0 != reply.dataAvail()) {
+            handle->decryptInfo = new DecryptInfo();
+            handle->decryptInfo->decryptBufferLength = reply.readInt32();
+        }
+    } else {
+        LOGE("no decryptHandle is generated in service side");
+    }
+    return handle;
+}
+
+DecryptHandle* BpDrmManagerService::openDecryptSession(int uniqueId, const char* uri) {
+    LOGV("Entering BpDrmManagerService::openDecryptSession");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+    data.writeString8(String8(uri));
+
+    remote()->transact(OPEN_DECRYPT_SESSION_FROM_URI, data, &reply);
+
+    DecryptHandle* handle = NULL;
+    if (0 != reply.dataAvail()) {
+        handle = new DecryptHandle();
+        handle->decryptId = reply.readInt32();
+        handle->mimeType = reply.readString8();
+        handle->decryptApiType = reply.readInt32();
+        handle->status = reply.readInt32();
+        handle->decryptInfo = NULL;
+        if (0 != reply.dataAvail()) {
+            handle->decryptInfo = new DecryptInfo();
+            handle->decryptInfo->decryptBufferLength = reply.readInt32();
+        }
+    } else {
+        LOGE("no decryptHandle is generated in service side");
+    }
+    return handle;
+}
+
+status_t BpDrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+    LOGV("closeDecryptSession");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+
+    remote()->transact(CLOSE_DECRYPT_SESSION, data, &reply);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        LOGV("deleting decryptInfo");
+        delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
+    }
+    delete decryptHandle; decryptHandle = NULL;
+    return reply.readInt32();
+}
+
+status_t BpDrmManagerService::initializeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) {
+    LOGV("initializeDecryptUnit");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+    data.writeInt32(decryptUnitId);
+
+    data.writeInt32(headerInfo->length);
+    data.write(headerInfo->data, headerInfo->length);
+
+    remote()->transact(INITIALIZE_DECRYPT_UNIT, data, &reply);
+    return reply.readInt32();
+}
+
+status_t BpDrmManagerService::decrypt(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    LOGV("decrypt");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+
+    data.writeInt32(decryptUnitId);
+    data.writeInt32((*decBuffer)->length);
+
+    data.writeInt32(encBuffer->length);
+    data.write(encBuffer->data, encBuffer->length);
+
+    if (NULL != IV) {
+        data.writeInt32(IV->length);
+        data.write(IV->data, IV->length);
+    }
+
+    remote()->transact(DECRYPT, data, &reply);
+
+    const status_t status = reply.readInt32();
+    LOGV("Return value of decrypt() is %d", status);
+
+    const int size = reply.readInt32();
+    (*decBuffer)->length = size;
+    reply.read((void *)(*decBuffer)->data, size);
+
+    return status;
+}
+
+status_t BpDrmManagerService::finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    LOGV("finalizeDecryptUnit");
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+
+    data.writeInt32(decryptUnitId);
+
+    remote()->transact(FINALIZE_DECRYPT_UNIT, data, &reply);
+    return reply.readInt32();
+}
+
+ssize_t BpDrmManagerService::pread(
+            int uniqueId, DecryptHandle* decryptHandle, void* buffer,
+            ssize_t numBytes, off_t offset) {
+    LOGV("read");
+    Parcel data, reply;
+    int result;
+
+    data.writeInterfaceToken(IDrmManagerService::getInterfaceDescriptor());
+    data.writeInt32(uniqueId);
+
+    data.writeInt32(decryptHandle->decryptId);
+    data.writeString8(decryptHandle->mimeType);
+    data.writeInt32(decryptHandle->decryptApiType);
+    data.writeInt32(decryptHandle->status);
+
+    if (NULL != decryptHandle->decryptInfo) {
+        data.writeInt32(decryptHandle->decryptInfo->decryptBufferLength);
+    } else {
+        data.writeInt32(INVALID_BUFFER_LENGTH);
+    }
+
+    data.writeInt32(numBytes);
+    data.writeInt32(offset);
+
+    remote()->transact(PREAD, data, &reply);
+    result = reply.readInt32();
+    if (0 < result) {
+        reply.read(buffer, result);
+    }
+    return result;
+}
+
+IMPLEMENT_META_INTERFACE(DrmManagerService, "drm.IDrmManagerService");
+
+status_t BnDrmManagerService::onTransact(
+            uint32_t code, const Parcel& data,
+            Parcel* reply, uint32_t flags) {
+    LOGV("Entering BnDrmManagerService::onTransact with code %d", code);
+
+    switch (code) {
+    case ADD_UNIQUEID:
+    {
+        LOGV("BnDrmManagerService::onTransact :ADD_UNIQUEID");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+        int uniqueId = addUniqueId(data.readInt32());
+        reply->writeInt32(uniqueId);
+        return DRM_NO_ERROR;
+    }
+
+    case REMOVE_UNIQUEID:
+    {
+        LOGV("BnDrmManagerService::onTransact :REMOVE_UNIQUEID");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+        removeUniqueId(data.readInt32());
+        return DRM_NO_ERROR;
+    }
+
+    case ADD_CLIENT:
+    {
+        LOGV("BnDrmManagerService::onTransact :ADD_CLIENT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+        addClient(data.readInt32());
+        return DRM_NO_ERROR;
+    }
+
+    case REMOVE_CLIENT:
+    {
+        LOGV("BnDrmManagerService::onTransact :REMOVE_CLIENT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+        removeClient(data.readInt32());
+        return DRM_NO_ERROR;
+    }
+
+    case SET_DRM_SERVICE_LISTENER:
+    {
+        LOGV("BnDrmManagerService::onTransact :SET_DRM_SERVICE_LISTENER");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const sp<IDrmServiceListener> drmServiceListener
+            = interface_cast<IDrmServiceListener> (data.readStrongBinder());
+
+        status_t status = setDrmServiceListener(uniqueId, drmServiceListener);
+
+        reply->writeInt32(status);
+        return DRM_NO_ERROR;
+    }
+
+    case INSTALL_DRM_ENGINE:
+    {
+        LOGV("BnDrmManagerService::onTransact :INSTALL_DRM_ENGINE");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        status_t status = installDrmEngine(data.readInt32(), data.readString8());
+
+        reply->writeInt32(status);
+        return DRM_NO_ERROR;
+    }
+
+    case GET_CONSTRAINTS_FROM_CONTENT:
+    {
+        LOGV("BnDrmManagerService::onTransact :GET_CONSTRAINTS_FROM_CONTENT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+
+        DrmConstraints* drmConstraints = getConstraints(uniqueId, &path, data.readInt32());
+
+        if (NULL != drmConstraints) {
+            //Filling DRM Constraints contents
+            reply->writeInt32(drmConstraints->getCount());
+
+            DrmConstraints::KeyIterator keyIt = drmConstraints->keyIterator();
+            while (keyIt.hasNext()) {
+                const String8 key = keyIt.next();
+                reply->writeString8(key);
+                const char* value = drmConstraints->getAsByteArray(&key);
+                int bufferSize = 0;
+                if (NULL != value) {
+                    bufferSize = strlen(value);
+                }
+                reply->writeInt32(bufferSize + 1);
+                reply->write(value, bufferSize + 1);
+            }
+        }
+        delete drmConstraints; drmConstraints = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case GET_METADATA_FROM_CONTENT:
+    {
+        LOGV("BnDrmManagerService::onTransact :GET_METADATA_FROM_CONTENT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+
+        DrmMetadata* drmMetadata = getMetadata(uniqueId, &path);
+        if (NULL != drmMetadata) {
+            //Filling DRM Metadata contents
+            reply->writeInt32(drmMetadata->getCount());
+
+            DrmMetadata::KeyIterator keyIt = drmMetadata->keyIterator();
+            while (keyIt.hasNext()) {
+                const String8 key = keyIt.next();
+                reply->writeString8(key);
+                const char* value = drmMetadata->getAsByteArray(&key);
+                int bufferSize = 0;
+                if (NULL != value) {
+                    bufferSize = strlen(value);
+                    reply->writeInt32(bufferSize + 1);
+                    reply->write(value, bufferSize + 1);
+                } else {
+                    reply->writeInt32(0);
+                }
+            }
+        }
+        delete drmMetadata; drmMetadata = NULL;
+        return NO_ERROR;
+    }
+
+    case CAN_HANDLE:
+    {
+        LOGV("BnDrmManagerService::onTransact :CAN_HANDLE");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+        const String8 mimeType = data.readString8();
+
+        bool result = canHandle(uniqueId, path, mimeType);
+
+        reply->writeInt32(result);
+        return DRM_NO_ERROR;
+    }
+
+    case PROCESS_DRM_INFO:
+    {
+        LOGV("BnDrmManagerService::onTransact :PROCESS_DRM_INFO");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        //Filling DRM info
+        const int infoType = data.readInt32();
+        const int bufferSize = data.readInt32();
+        char* buffer = NULL;
+        if (0 < bufferSize) {
+            buffer = (char *)data.readInplace(bufferSize);
+        }
+        const DrmBuffer drmBuffer(buffer, bufferSize);
+        DrmInfo* drmInfo = new DrmInfo(infoType, drmBuffer, data.readString8());
+
+        const int size = data.readInt32();
+        for (int index = 0; index < size; ++index) {
+            const String8 key(data.readString8());
+            const String8 value(data.readString8());
+            drmInfo->put(key, (value == String8("NULL")) ? String8("") : value);
+        }
+
+        DrmInfoStatus* drmInfoStatus = processDrmInfo(uniqueId, drmInfo);
+
+        if (NULL != drmInfoStatus) {
+            //Filling DRM Info Status contents
+            reply->writeInt32(drmInfoStatus->statusCode);
+            reply->writeInt32(drmInfoStatus->infoType);
+            reply->writeString8(drmInfoStatus->mimeType);
+
+            if (NULL != drmInfoStatus->drmBuffer) {
+                const DrmBuffer* drmBuffer = drmInfoStatus->drmBuffer;
+                const int bufferSize = drmBuffer->length;
+                reply->writeInt32(bufferSize);
+                if (0 < bufferSize) {
+                    reply->write(drmBuffer->data, bufferSize);
+                }
+                delete [] drmBuffer->data;
+                delete drmBuffer; drmBuffer = NULL;
+            }
+        }
+        delete drmInfo; drmInfo = NULL;
+        delete drmInfoStatus; drmInfoStatus = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case ACQUIRE_DRM_INFO:
+    {
+        LOGV("BnDrmManagerService::onTransact :ACQUIRE_DRM_INFO");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        //Filling DRM info Request
+        DrmInfoRequest* drmInfoRequest = new DrmInfoRequest(data.readInt32(), data.readString8());
+
+        const int size = data.readInt32();
+        for (int index = 0; index < size; ++index) {
+            const String8 key(data.readString8());
+            const String8 value(data.readString8());
+            drmInfoRequest->put(key, (value == String8("NULL")) ? String8("") : value);
+        }
+
+        DrmInfo* drmInfo = acquireDrmInfo(uniqueId, drmInfoRequest);
+
+        if (NULL != drmInfo) {
+            //Filling DRM Info
+            const DrmBuffer drmBuffer = drmInfo->getData();
+            reply->writeInt32(drmInfo->getInfoType());
+
+            const int bufferSize = drmBuffer.length;
+            reply->writeInt32(bufferSize);
+            if (0 < bufferSize) {
+                reply->write(drmBuffer.data, bufferSize);
+            }
+            reply->writeString8(drmInfo->getMimeType());
+            reply->writeInt32(drmInfo->getCount());
+
+            DrmInfo::KeyIterator keyIt = drmInfo->keyIterator();
+            while (keyIt.hasNext()) {
+                const String8 key = keyIt.next();
+                reply->writeString8(key);
+                const String8 value = drmInfo->get(key);
+                reply->writeString8((value == String8("")) ? String8("NULL") : value);
+            }
+            delete [] drmBuffer.data;
+        }
+        delete drmInfoRequest; drmInfoRequest = NULL;
+        delete drmInfo; drmInfo = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case SAVE_RIGHTS:
+    {
+        LOGV("BnDrmManagerService::onTransact :SAVE_RIGHTS");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        //Filling DRM Rights
+        const int bufferSize = data.readInt32();
+        const DrmBuffer drmBuffer((char *)data.readInplace(bufferSize), bufferSize);
+
+        const String8 mimeType(data.readString8());
+        const String8 accountId(data.readString8());
+        const String8 subscriptionId(data.readString8());
+        const String8 rightsPath(data.readString8());
+        const String8 contentPath(data.readString8());
+
+        DrmRights drmRights(drmBuffer,
+                            ((mimeType == String8("NULL")) ? String8("") : mimeType),
+                            ((accountId == String8("NULL")) ? String8("") : accountId),
+                            ((subscriptionId == String8("NULL")) ? String8("") : subscriptionId));
+
+        const status_t status = saveRights(uniqueId, drmRights,
+                            ((rightsPath == String8("NULL")) ? String8("") : rightsPath),
+                            ((contentPath == String8("NULL")) ? String8("") : contentPath));
+
+        reply->writeInt32(status);
+        return DRM_NO_ERROR;
+    }
+
+    case GET_ORIGINAL_MIMETYPE:
+    {
+        LOGV("BnDrmManagerService::onTransact :GET_ORIGINAL_MIMETYPE");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const String8 originalMimeType = getOriginalMimeType(data.readInt32(), data.readString8());
+
+        reply->writeString8(originalMimeType);
+        return DRM_NO_ERROR;
+    }
+
+    case GET_DRM_OBJECT_TYPE:
+    {
+        LOGV("BnDrmManagerService::onTransact :GET_DRM_OBJECT_TYPE");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int drmObjectType
+            = getDrmObjectType(data.readInt32(), data.readString8(), data.readString8());
+
+        reply->writeInt32(drmObjectType);
+        return DRM_NO_ERROR;
+    }
+
+    case CHECK_RIGHTS_STATUS:
+    {
+        LOGV("BnDrmManagerService::onTransact :CHECK_RIGHTS_STATUS");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int result
+            = checkRightsStatus(data.readInt32(), data.readString8(), data.readInt32());
+
+        reply->writeInt32(result);
+        return DRM_NO_ERROR;
+    }
+
+    case CONSUME_RIGHTS:
+    {
+        LOGV("BnDrmManagerService::onTransact :CONSUME_RIGHTS");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle handle;
+        handle.decryptId = data.readInt32();
+        handle.mimeType = data.readString8();
+        handle.decryptApiType = data.readInt32();
+        handle.status = data.readInt32();
+        handle.decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle.decryptInfo = new DecryptInfo();
+            handle.decryptInfo->decryptBufferLength = bufferLength;
+        }
+
+        const status_t status
+            = consumeRights(uniqueId, &handle, data.readInt32(),
+                static_cast<bool>(data.readInt32()));
+        reply->writeInt32(status);
+
+        delete handle.decryptInfo; handle.decryptInfo = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case SET_PLAYBACK_STATUS:
+    {
+        LOGV("BnDrmManagerService::onTransact :SET_PLAYBACK_STATUS");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle handle;
+        handle.decryptId = data.readInt32();
+        handle.mimeType = data.readString8();
+        handle.decryptApiType = data.readInt32();
+        handle.status = data.readInt32();
+        handle.decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle.decryptInfo = new DecryptInfo();
+            handle.decryptInfo->decryptBufferLength = bufferLength;
+        }
+
+        const status_t status
+            = setPlaybackStatus(uniqueId, &handle, data.readInt32(), data.readInt32());
+        reply->writeInt32(status);
+
+        delete handle.decryptInfo; handle.decryptInfo = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case VALIDATE_ACTION:
+    {
+        LOGV("BnDrmManagerService::onTransact :VALIDATE_ACTION");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        bool result = validateAction(
+                                data.readInt32(),
+                                data.readString8(),
+                                data.readInt32(),
+                                ActionDescription(data.readInt32(), data.readInt32()));
+
+        reply->writeInt32(result);
+        return DRM_NO_ERROR;
+    }
+
+    case REMOVE_RIGHTS:
+    {
+        LOGV("BnDrmManagerService::onTransact :REMOVE_RIGHTS");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const status_t status = removeRights(data.readInt32(), data.readString8());
+        reply->writeInt32(status);
+
+        return DRM_NO_ERROR;
+    }
+
+    case REMOVE_ALL_RIGHTS:
+    {
+        LOGV("BnDrmManagerService::onTransact :REMOVE_ALL_RIGHTS");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const status_t status = removeAllRights(data.readInt32());
+        reply->writeInt32(status);
+
+        return DRM_NO_ERROR;
+    }
+
+    case OPEN_CONVERT_SESSION:
+    {
+        LOGV("BnDrmManagerService::onTransact :OPEN_CONVERT_SESSION");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int convertId = openConvertSession(data.readInt32(), data.readString8());
+
+        reply->writeInt32(convertId);
+        return DRM_NO_ERROR;
+    }
+
+    case CONVERT_DATA:
+    {
+        LOGV("BnDrmManagerService::onTransact :CONVERT_DATA");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const int convertId = data.readInt32();
+
+        //Filling input data
+        const int bufferSize = data.readInt32();
+        DrmBuffer* inputData = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
+
+        DrmConvertedStatus*    drmConvertedStatus = convertData(uniqueId, convertId, inputData);
+
+        if (NULL != drmConvertedStatus) {
+            //Filling Drm Converted Ststus
+            reply->writeInt32(drmConvertedStatus->statusCode);
+            reply->writeInt32(drmConvertedStatus->offset);
+
+            if (NULL != drmConvertedStatus->convertedData) {
+                const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
+                const int bufferSize = convertedData->length;
+                reply->writeInt32(bufferSize);
+                if (0 < bufferSize) {
+                    reply->write(convertedData->data, bufferSize);
+                }
+                delete [] convertedData->data;
+                delete convertedData; convertedData = NULL;
+            }
+        }
+        delete inputData; inputData = NULL;
+        delete drmConvertedStatus; drmConvertedStatus = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case CLOSE_CONVERT_SESSION:
+    {
+        LOGV("BnDrmManagerService::onTransact :CLOSE_CONVERT_SESSION");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        DrmConvertedStatus*    drmConvertedStatus
+            = closeConvertSession(data.readInt32(), data.readInt32());
+
+        if (NULL != drmConvertedStatus) {
+            //Filling Drm Converted Ststus
+            reply->writeInt32(drmConvertedStatus->statusCode);
+            reply->writeInt32(drmConvertedStatus->offset);
+
+            if (NULL != drmConvertedStatus->convertedData) {
+                const DrmBuffer* convertedData = drmConvertedStatus->convertedData;
+                const int bufferSize = convertedData->length;
+                reply->writeInt32(bufferSize);
+                if (0 < bufferSize) {
+                    reply->write(convertedData->data, bufferSize);
+                }
+                delete [] convertedData->data;
+                delete convertedData; convertedData = NULL;
+            }
+        }
+        delete drmConvertedStatus; drmConvertedStatus = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case GET_ALL_SUPPORT_INFO:
+    {
+        LOGV("BnDrmManagerService::onTransact :GET_ALL_SUPPORT_INFO");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        int length = 0;
+        DrmSupportInfo* drmSupportInfoArray = NULL;
+
+        status_t status = getAllSupportInfo(uniqueId, &length, &drmSupportInfoArray);
+
+        reply->writeInt32(length);
+        for (int i = 0; i < length; ++i) {
+            DrmSupportInfo drmSupportInfo = drmSupportInfoArray[i];
+
+            reply->writeInt32(drmSupportInfo.getFileSuffixCount());
+            DrmSupportInfo::FileSuffixIterator fileSuffixIt
+                = drmSupportInfo.getFileSuffixIterator();
+            while (fileSuffixIt.hasNext()) {
+                reply->writeString8(fileSuffixIt.next());
+            }
+
+            reply->writeInt32(drmSupportInfo.getMimeTypeCount());
+            DrmSupportInfo::MimeTypeIterator mimeTypeIt = drmSupportInfo.getMimeTypeIterator();
+            while (mimeTypeIt.hasNext()) {
+                reply->writeString8(mimeTypeIt.next());
+            }
+            reply->writeString8(drmSupportInfo.getDescription());
+        }
+        delete [] drmSupportInfoArray; drmSupportInfoArray = NULL;
+        reply->writeInt32(status);
+        return DRM_NO_ERROR;
+    }
+
+    case OPEN_DECRYPT_SESSION:
+    {
+        LOGV("BnDrmManagerService::onTransact :OPEN_DECRYPT_SESSION");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const int fd = data.readFileDescriptor();
+
+        DecryptHandle* handle
+            = openDecryptSession(uniqueId, fd, data.readInt32(), data.readInt32());
+
+        if (NULL != handle) {
+            reply->writeInt32(handle->decryptId);
+            reply->writeString8(handle->mimeType);
+            reply->writeInt32(handle->decryptApiType);
+            reply->writeInt32(handle->status);
+            if (NULL != handle->decryptInfo) {
+                reply->writeInt32(handle->decryptInfo->decryptBufferLength);
+                delete handle->decryptInfo; handle->decryptInfo = NULL;
+            }
+        } else {
+            LOGE("NULL decryptHandle is returned");
+        }
+        delete handle; handle = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case OPEN_DECRYPT_SESSION_FROM_URI:
+    {
+        LOGV("BnDrmManagerService::onTransact :OPEN_DECRYPT_SESSION_FROM_URI");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+        const String8 uri = data.readString8();
+
+        DecryptHandle* handle = openDecryptSession(uniqueId, uri.string());
+
+        if (NULL != handle) {
+            reply->writeInt32(handle->decryptId);
+            reply->writeString8(handle->mimeType);
+            reply->writeInt32(handle->decryptApiType);
+            reply->writeInt32(handle->status);
+            if (NULL != handle->decryptInfo) {
+                reply->writeInt32(handle->decryptInfo->decryptBufferLength);
+                delete handle->decryptInfo; handle->decryptInfo = NULL;
+            }
+        } else {
+            LOGE("NULL decryptHandle is returned");
+        }
+        delete handle; handle = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case CLOSE_DECRYPT_SESSION:
+    {
+        LOGV("BnDrmManagerService::onTransact :CLOSE_DECRYPT_SESSION");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle* handle = new DecryptHandle();
+        handle->decryptId = data.readInt32();
+        handle->mimeType = data.readString8();
+        handle->decryptApiType = data.readInt32();
+        handle->status = data.readInt32();
+        handle->decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle->decryptInfo = new DecryptInfo();
+            handle->decryptInfo->decryptBufferLength = bufferLength;
+        }
+
+        const status_t status = closeDecryptSession(uniqueId, handle);
+        reply->writeInt32(status);
+        return DRM_NO_ERROR;
+    }
+
+    case INITIALIZE_DECRYPT_UNIT:
+    {
+        LOGV("BnDrmManagerService::onTransact :INITIALIZE_DECRYPT_UNIT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle handle;
+        handle.decryptId = data.readInt32();
+        handle.mimeType = data.readString8();
+        handle.decryptApiType = data.readInt32();
+        handle.status = data.readInt32();
+        handle.decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle.decryptInfo = new DecryptInfo();
+            handle.decryptInfo->decryptBufferLength = bufferLength;
+        }
+        const int decryptUnitId = data.readInt32();
+
+        //Filling Header info
+        const int bufferSize = data.readInt32();
+        DrmBuffer* headerInfo = NULL;
+        headerInfo = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
+
+        const status_t status
+            = initializeDecryptUnit(uniqueId, &handle, decryptUnitId, headerInfo);
+        reply->writeInt32(status);
+
+        delete handle.decryptInfo; handle.decryptInfo = NULL;
+        delete headerInfo; headerInfo = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case DECRYPT:
+    {
+        LOGV("BnDrmManagerService::onTransact :DECRYPT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle handle;
+        handle.decryptId = data.readInt32();
+        handle.mimeType = data.readString8();
+        handle.decryptApiType = data.readInt32();
+        handle.status = data.readInt32();
+        handle.decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle.decryptInfo = new DecryptInfo();
+            handle.decryptInfo->decryptBufferLength = bufferLength;
+        }
+        const int decryptUnitId = data.readInt32();
+        const int decBufferSize = data.readInt32();
+
+        const int encBufferSize = data.readInt32();
+        DrmBuffer* encBuffer
+            = new DrmBuffer((char *)data.readInplace(encBufferSize), encBufferSize);
+
+        char* buffer = NULL;
+        buffer = new char[decBufferSize];
+        DrmBuffer* decBuffer = new DrmBuffer(buffer, decBufferSize);
+
+        DrmBuffer* IV = NULL;
+        if (0 != data.dataAvail()) {
+            const int ivBufferlength = data.readInt32();
+            IV = new DrmBuffer((char *)data.readInplace(ivBufferlength), ivBufferlength);
+        }
+
+        const status_t status
+            = decrypt(uniqueId, &handle, decryptUnitId, encBuffer, &decBuffer, IV);
+
+        reply->writeInt32(status);
+
+        const int size = decBuffer->length;
+        reply->writeInt32(size);
+        reply->write(decBuffer->data, size);
+
+        delete handle.decryptInfo; handle.decryptInfo = NULL;
+        delete encBuffer; encBuffer = NULL;
+        delete decBuffer; decBuffer = NULL;
+        delete [] buffer; buffer = NULL;
+        delete IV; IV = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case FINALIZE_DECRYPT_UNIT:
+    {
+        LOGV("BnDrmManagerService::onTransact :FINALIZE_DECRYPT_UNIT");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle handle;
+        handle.decryptId = data.readInt32();
+        handle.mimeType = data.readString8();
+        handle.decryptApiType = data.readInt32();
+        handle.status = data.readInt32();
+        handle.decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle.decryptInfo = new DecryptInfo();
+            handle.decryptInfo->decryptBufferLength = bufferLength;
+        }
+
+        const status_t status = finalizeDecryptUnit(uniqueId, &handle, data.readInt32());
+        reply->writeInt32(status);
+
+        delete handle.decryptInfo; handle.decryptInfo = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    case PREAD:
+    {
+        LOGV("BnDrmManagerService::onTransact :READ");
+        CHECK_INTERFACE(IDrmManagerService, data, reply);
+
+        const int uniqueId = data.readInt32();
+
+        DecryptHandle handle;
+        handle.decryptId = data.readInt32();
+        handle.mimeType = data.readString8();
+        handle.decryptApiType = data.readInt32();
+        handle.status = data.readInt32();
+        handle.decryptInfo = NULL;
+
+        const int bufferLength = data.readInt32();
+        if (INVALID_BUFFER_LENGTH != bufferLength) {
+            handle.decryptInfo = new DecryptInfo();
+            handle.decryptInfo->decryptBufferLength = bufferLength;
+        }
+
+        const int numBytes = data.readInt32();
+        char* buffer = new char[numBytes];
+
+        const off_t offset = data.readInt32();
+
+        ssize_t result = pread(uniqueId, &handle, buffer, numBytes, offset);
+        reply->writeInt32(result);
+        if (0 < result) {
+            reply->write(buffer, result);
+        }
+
+        delete handle.decryptInfo; handle.decryptInfo = NULL;
+        delete [] buffer, buffer = NULL;
+        return DRM_NO_ERROR;
+    }
+
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
diff --git a/drm/common/IDrmServiceListener.cpp b/drm/common/IDrmServiceListener.cpp
new file mode 100644
index 0000000..6eeea40
--- /dev/null
+++ b/drm/common/IDrmServiceListener.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <drm/drm_framework_common.h>
+#include <drm/DrmInfoEvent.h>
+#include "IDrmServiceListener.h"
+
+using namespace android;
+
+status_t BpDrmServiceListener::notify(const DrmInfoEvent& event) {
+    Parcel data, reply;
+
+    data.writeInterfaceToken(IDrmServiceListener::getInterfaceDescriptor());
+    data.writeInt32(event.getUniqueId());
+    data.writeInt32(event.getType());
+    data.writeString8(event.getMessage());
+
+    remote()->transact(NOTIFY, data, &reply);
+    return reply.readInt32();
+}
+
+IMPLEMENT_META_INTERFACE(DrmServiceListener, "drm.IDrmServiceListener");
+
+status_t BnDrmServiceListener::onTransact(
+        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+
+    switch (code) {
+    case NOTIFY:
+    {
+        CHECK_INTERFACE(IDrmServiceListener, data, reply);
+        int uniqueId = data.readInt32();
+        int type = data.readInt32();
+        const String8& message = data.readString8();
+
+        status_t status = notify(DrmInfoEvent(uniqueId, type, message));
+        reply->writeInt32(status);
+
+        return DRM_NO_ERROR;
+    }
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
diff --git a/drm/common/ReadWriteUtils.cpp b/drm/common/ReadWriteUtils.cpp
new file mode 100644
index 0000000..7ec4fa2
--- /dev/null
+++ b/drm/common/ReadWriteUtils.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 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 "ReadWriteUtils"
+#include <utils/Log.h>
+
+#include <ReadWriteUtils.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+#define FAILURE -1
+
+String8 ReadWriteUtils::readBytes(const String8& filePath) {
+    FILE* file = NULL;
+    file = fopen(filePath.string(), "r");
+
+    String8 string("");
+    if (NULL != file) {
+        int fd = fileno(file);
+        struct stat sb;
+
+        if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
+            int length = sb.st_size;
+            char* bytes = new char[length];
+            if (length == read(fd, (void*) bytes, length)) {
+                string.append(bytes, length);
+            }
+            delete bytes;
+        }
+        fclose(file);
+    }
+    return string;
+}
+
+int ReadWriteUtils::readBytes(const String8& filePath, char** buffer) {
+    FILE* file = NULL;
+    file = fopen(filePath.string(), "r");
+    int length = 0;
+
+    if (NULL != file) {
+        int fd = fileno(file);
+        struct stat sb;
+
+        if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
+            length = sb.st_size;
+            *buffer = new char[length];
+            if (length != read(fd, (void*) *buffer, length)) {
+                length = FAILURE;
+            }
+        }
+        fclose(file);
+    }
+    return length;
+}
+
+void ReadWriteUtils::writeToFile(const String8& filePath, const String8& data) {
+    FILE* file = NULL;
+    file = fopen(filePath.string(), "w+");
+
+    if (NULL != file) {
+        int fd = fileno(file);
+
+        int size = data.size();
+        if (FAILURE != ftruncate(fd, size)) {
+            if (size != write(fd, data.string(), size)) {
+                LOGE("Failed to write the data to: %s", filePath.string());
+            }
+        }
+        fclose(file);
+    }
+}
+
+void ReadWriteUtils::appendToFile(const String8& filePath, const String8& data) {
+    FILE* file = NULL;
+    file = fopen(filePath.string(), "a+");
+
+    if (NULL != file) {
+        int fd = fileno(file);
+
+        int size = data.size();
+        if (size != write(fd, data.string(), size)) {
+            LOGE("Failed to write the data to: %s", filePath.string());
+        }
+        fclose(file);
+    }
+}
+
diff --git a/drm/drmserver/Android.mk b/drm/drmserver/Android.mk
new file mode 100644
index 0000000..5df2ff8
--- /dev/null
+++ b/drm/drmserver/Android.mk
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2010 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:= \
+    main_drmserver.cpp \
+    DrmManager.cpp \
+    DrmManagerService.cpp \
+    StringTokenizer.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils \
+    libbinder
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon
+
+LOCAL_C_INCLUDES := \
+    $(TOP)/frameworks/base/include \
+    $(TOP)/frameworks/base/drm/libdrmframework/include \
+    $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include
+
+LOCAL_MODULE:= drmserver
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
new file mode 100644
index 0000000..49df1c8
--- /dev/null
+++ b/drm/drmserver/DrmManager.cpp
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2010 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 "DrmManager(Native)"
+#include "utils/Log.h"
+
+#include <utils/String8.h>
+#include <drm/DrmInfo.h>
+#include <drm/DrmInfoEvent.h>
+#include <drm/DrmRights.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+#include <drm/DrmConvertedStatus.h>
+#include <IDrmEngine.h>
+
+#include "DrmManager.h"
+#include "ReadWriteUtils.h"
+
+#define DECRYPT_FILE_ERROR -1
+
+using namespace android;
+
+Vector<int> DrmManager::mUniqueIdVector;
+const String8 DrmManager::EMPTY_STRING("");
+
+DrmManager::DrmManager() :
+    mDecryptSessionId(0),
+    mConvertId(0) {
+
+}
+
+DrmManager::~DrmManager() {
+
+}
+
+int DrmManager::addUniqueId(int uniqueId) {
+    if (0 == uniqueId) {
+        int temp = 0;
+        bool foundUniqueId = false;
+        srand(time(NULL));
+
+        while (!foundUniqueId) {
+            const int size = mUniqueIdVector.size();
+            temp = rand() % 100;
+
+            int index = 0;
+            for (; index < size; ++index) {
+                if (mUniqueIdVector.itemAt(index) == temp) {
+                    foundUniqueId = false;
+                    break;
+                }
+            }
+            if (index == size) {
+                foundUniqueId = true;
+            }
+        }
+        uniqueId = temp;
+    }
+    mUniqueIdVector.push(uniqueId);
+    return uniqueId;
+}
+
+void DrmManager::removeUniqueId(int uniqueId) {
+    for (unsigned int i = 0; i < mUniqueIdVector.size(); i++) {
+        if (uniqueId == mUniqueIdVector.itemAt(i)) {
+            mUniqueIdVector.removeAt(i);
+            break;
+        }
+    }
+}
+
+status_t DrmManager::loadPlugIns() {
+    String8 pluginDirPath("/system/lib/drm/plugins/native");
+    return loadPlugIns(pluginDirPath);
+}
+
+status_t DrmManager::loadPlugIns(const String8& plugInDirPath) {
+    if (mSupportInfoToPlugInIdMap.isEmpty()) {
+        mPlugInManager.loadPlugIns(plugInDirPath);
+        Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+        for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+            String8 plugInPath = plugInPathList[i];
+            DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
+            if (NULL != info) {
+                mSupportInfoToPlugInIdMap.add(*info, plugInPath);
+            }
+        }
+    }
+    return DRM_NO_ERROR;
+}
+
+status_t DrmManager::unloadPlugIns() {
+    mConvertSessionMap.clear();
+    mDecryptSessionMap.clear();
+    mPlugInManager.unloadPlugIns();
+    mSupportInfoToPlugInIdMap.clear();
+    return DRM_NO_ERROR;
+}
+
+status_t DrmManager::setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
+    Mutex::Autolock _l(mLock);
+    if (NULL != drmServiceListener.get()) {
+        mServiceListeners.add(uniqueId, drmServiceListener);
+    } else {
+        mServiceListeners.removeItem(uniqueId);
+    }
+    return DRM_NO_ERROR;
+}
+
+void DrmManager::addClient(int uniqueId) {
+    if (!mSupportInfoToPlugInIdMap.isEmpty()) {
+        Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+        for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
+            rDrmEngine.initialize(uniqueId);
+            rDrmEngine.setOnInfoListener(uniqueId, this);
+        }
+    }
+}
+
+void DrmManager::removeClient(int uniqueId) {
+    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+    for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
+        rDrmEngine.terminate(uniqueId);
+    }
+}
+
+DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.getConstraints(uniqueId, path, action);
+    }
+    return NULL;
+}
+
+DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.getMetadata(uniqueId, path);
+    }
+    return NULL;
+}
+
+status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) {
+    mPlugInManager.loadPlugIn(absolutePath);
+
+    IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(absolutePath);
+    rDrmEngine.initialize(uniqueId);
+    rDrmEngine.setOnInfoListener(uniqueId, this);
+
+    DrmSupportInfo* info = rDrmEngine.getSupportInfo(0);
+    mSupportInfoToPlugInIdMap.add(*info, absolutePath);
+
+    return DRM_NO_ERROR;
+}
+
+bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+    const String8 plugInId = getSupportedPlugInId(mimeType);
+    bool result = (EMPTY_STRING != plugInId) ? true : false;
+
+    if (0 < path.length()) {
+        if (result) {
+            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+            result = rDrmEngine.canHandle(uniqueId, path);
+        } else {
+            result = canHandle(uniqueId, path);
+        }
+    }
+    return result;
+}
+
+DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.processDrmInfo(uniqueId, drmInfo);
+    }
+    return NULL;
+}
+
+bool DrmManager::canHandle(int uniqueId, const String8& path) {
+    bool result = false;
+    Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+
+    for (unsigned int i = 0; i < plugInPathList.size(); ++i) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInPathList[i]);
+        result = rDrmEngine.canHandle(uniqueId, path);
+
+        if (result) {
+            break;
+        }
+    }
+    return result;
+}
+
+DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
+    }
+    return NULL;
+}
+
+status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) {
+    const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath);
+    }
+    return result;
+}
+
+String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.getOriginalMimeType(uniqueId, path);
+    }
+    return EMPTY_STRING;
+}
+
+int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
+    const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
+    }
+    return DrmObjectType::UNKNOWN;
+}
+
+int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.checkRightsStatus(uniqueId, path, action);
+    }
+    return RightsStatus::RIGHTS_INVALID;
+}
+
+status_t DrmManager::consumeRights(
+    int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->consumeRights(uniqueId, decryptHandle, action, reserve);
+    }
+    return result;
+}
+
+status_t DrmManager::setPlaybackStatus(
+    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
+    }
+    return result;
+}
+
+bool DrmManager::validateAction(
+    int uniqueId, const String8& path, int action, const ActionDescription& description) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        return rDrmEngine.validateAction(uniqueId, path, action, description);
+    }
+    return false;
+}
+
+status_t DrmManager::removeRights(int uniqueId, const String8& path) {
+    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+        result = rDrmEngine.removeRights(uniqueId, path);
+    }
+    return result;
+}
+
+status_t DrmManager::removeAllRights(int uniqueId) {
+    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+    status_t result = DRM_ERROR_UNKNOWN;
+    for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
+        result = rDrmEngine.removeAllRights(uniqueId);
+        if (DRM_NO_ERROR != result) {
+            break;
+        }
+    }
+    return result;
+}
+
+int DrmManager::openConvertSession(int uniqueId, const String8& mimeType) {
+    int convertId = -1;
+
+    const String8 plugInId = getSupportedPlugInId(mimeType);
+    if (EMPTY_STRING != plugInId) {
+        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+
+        if (DRM_NO_ERROR == rDrmEngine.openConvertSession(uniqueId, mConvertId + 1)) {
+            Mutex::Autolock _l(mConvertLock);
+            ++mConvertId;
+            convertId = mConvertId;
+            mConvertSessionMap.add(convertId, &rDrmEngine);
+        }
+    }
+    return convertId;
+}
+
+DrmConvertedStatus* DrmManager::convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) {
+    DrmConvertedStatus *drmConvertedStatus = NULL;
+
+    if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
+        drmConvertedStatus = drmEngine->convertData(uniqueId, convertId, inputData);
+    }
+    return drmConvertedStatus;
+}
+
+DrmConvertedStatus* DrmManager::closeConvertSession(int uniqueId, int convertId) {
+    DrmConvertedStatus *drmConvertedStatus = NULL;
+
+    if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
+        drmConvertedStatus = drmEngine->closeConvertSession(uniqueId, convertId);
+        mConvertSessionMap.removeItem(convertId);
+    }
+    return drmConvertedStatus;
+}
+
+status_t DrmManager::getAllSupportInfo(
+                    int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+    Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
+    int size = plugInPathList.size();
+    int validPlugins = 0;
+
+    if (0 < size) {
+        Vector<DrmSupportInfo> drmSupportInfoList;
+
+        for (int i = 0; i < size; ++i) {
+            String8 plugInPath = plugInPathList[i];
+            DrmSupportInfo* drmSupportInfo
+                = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
+            if (NULL != drmSupportInfo) {
+                drmSupportInfoList.add(*drmSupportInfo);
+                delete drmSupportInfo; drmSupportInfo = NULL;
+            }
+        }
+
+        validPlugins = drmSupportInfoList.size();
+        if (0 < validPlugins) {
+            *drmSupportInfoArray = new DrmSupportInfo[validPlugins];
+            for (int i = 0; i < validPlugins; ++i) {
+                (*drmSupportInfoArray)[i] = drmSupportInfoList[i];
+            }
+        }
+    }
+    *length = validPlugins;
+    return DRM_NO_ERROR;
+}
+
+DecryptHandle* DrmManager::openDecryptSession(int uniqueId, int fd, int offset, int length) {
+    Mutex::Autolock _l(mDecryptLock);
+    status_t result = DRM_ERROR_CANNOT_HANDLE;
+    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+
+    DecryptHandle* handle = new DecryptHandle();
+    if (NULL != handle) {
+        handle->decryptId = mDecryptSessionId + 1;
+
+        for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+            String8 plugInId = plugInIdList.itemAt(index);
+            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+            result = rDrmEngine.openDecryptSession(uniqueId, handle, fd, offset, length);
+
+            if (DRM_NO_ERROR == result) {
+                ++mDecryptSessionId;
+                mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+                break;
+            }
+        }
+    }
+    if (DRM_NO_ERROR != result) {
+        delete handle; handle = NULL;
+        LOGE("DrmManager::openDecryptSession: no capable plug-in found");
+    }
+    return handle;
+}
+
+DecryptHandle* DrmManager::openDecryptSession(int uniqueId, const char* uri) {
+    Mutex::Autolock _l(mDecryptLock);
+    status_t result = DRM_ERROR_CANNOT_HANDLE;
+    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
+
+    DecryptHandle* handle = new DecryptHandle();
+    if (NULL != handle) {
+        handle->decryptId = mDecryptSessionId + 1;
+
+        for (unsigned int index = 0; index < plugInIdList.size(); index++) {
+            String8 plugInId = plugInIdList.itemAt(index);
+            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
+            result = rDrmEngine.openDecryptSession(uniqueId, handle, uri);
+
+            if (DRM_NO_ERROR == result) {
+                ++mDecryptSessionId;
+                mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+                break;
+            }
+        }
+    }
+    if (DRM_NO_ERROR != result) {
+        delete handle; handle = NULL;
+        LOGE("DrmManager::openDecryptSession: no capable plug-in found");
+    }
+    return handle;
+}
+
+status_t DrmManager::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+    Mutex::Autolock _l(mDecryptLock);
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->closeDecryptSession(uniqueId, decryptHandle);
+        if (DRM_NO_ERROR == result) {
+            mDecryptSessionMap.removeItem(decryptHandle->decryptId);
+        }
+    }
+    return result;
+}
+
+status_t DrmManager::initializeDecryptUnit(
+    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->initializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
+    }
+    return result;
+}
+
+status_t DrmManager::decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->decrypt(
+                uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+    }
+    return result;
+}
+
+status_t DrmManager::finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+    }
+    return result;
+}
+
+ssize_t DrmManager::pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset) {
+    ssize_t result = DECRYPT_FILE_ERROR;
+
+    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
+        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
+        result = drmEngine->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+    }
+    return result;
+}
+
+String8 DrmManager::getSupportedPlugInId(
+            int uniqueId, const String8& path, const String8& mimeType) {
+    String8 plugInId("");
+
+    if (EMPTY_STRING != mimeType) {
+        plugInId = getSupportedPlugInId(mimeType);
+    } else {
+        plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
+    }
+    return plugInId;
+}
+
+String8 DrmManager::getSupportedPlugInId(const String8& mimeType) {
+    String8 plugInId("");
+
+    if (EMPTY_STRING != mimeType) {
+        for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
+            const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);
+
+            if (drmSupportInfo.isSupportedMimeType(mimeType)) {
+                plugInId = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo);
+                break;
+            }
+        }
+    }
+    return plugInId;
+}
+
+String8 DrmManager::getSupportedPlugInIdFromPath(int uniqueId, const String8& path) {
+    String8 plugInId("");
+    const String8 fileSuffix = path.getPathExtension();
+
+    for (unsigned int index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
+        const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);
+
+        if (drmSupportInfo.isSupportedFileSuffix(fileSuffix)) {
+            String8 key = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo);
+            IDrmEngine& drmEngine = mPlugInManager.getPlugIn(key);
+
+            if (drmEngine.canHandle(uniqueId, path)) {
+                plugInId = key;
+                break;
+            }
+        }
+    }
+    return plugInId;
+}
+
+void DrmManager::onInfo(const DrmInfoEvent& event) {
+    Mutex::Autolock _l(mLock);
+    for (unsigned int index = 0; index < mServiceListeners.size(); index++) {
+        int uniqueId = mServiceListeners.keyAt(index);
+
+        if (uniqueId == event.getUniqueId()) {
+            sp<IDrmServiceListener> serviceListener = mServiceListeners.valueFor(uniqueId);
+            serviceListener->notify(event);
+        }
+    }
+}
+
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
new file mode 100644
index 0000000..4dcfa72
--- /dev/null
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2010 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 "DrmManagerService(Native)"
+#include <utils/Log.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <errno.h>
+#include <utils/threads.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <sys/stat.h>
+#include "DrmManagerService.h"
+#include "DrmManager.h"
+
+using namespace android;
+
+static Vector<uid_t> trustedUids;
+
+static bool isProtectedCallAllowed() {
+    // TODO
+    // Following implementation is just for reference.
+    // Each OEM manufacturer should implement/replace with their own solutions.
+    bool result = false;
+
+    IPCThreadState* ipcState = IPCThreadState::self();
+    uid_t uid = ipcState->getCallingUid();
+
+    for (unsigned int i = 0; i < trustedUids.size(); ++i) {
+        if (trustedUids[i] == uid) {
+            result = true;
+            break;
+        }
+    }
+    return result;
+}
+
+void DrmManagerService::instantiate() {
+    LOGV("instantiate");
+    defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService());
+
+    if (0 >= trustedUids.size()) {
+        // TODO
+        // Following implementation is just for reference.
+        // Each OEM manufacturer should implement/replace with their own solutions.
+
+        // Add trusted uids here
+        trustedUids.push(AID_MEDIA);
+    }
+}
+
+DrmManagerService::DrmManagerService() :
+        mDrmManager(NULL) {
+    LOGV("created");
+    mDrmManager = new DrmManager();
+    mDrmManager->loadPlugIns();
+}
+
+DrmManagerService::~DrmManagerService() {
+    LOGV("Destroyed");
+    mDrmManager->unloadPlugIns();
+    delete mDrmManager; mDrmManager = NULL;
+}
+
+int DrmManagerService::addUniqueId(int uniqueId) {
+    return mDrmManager->addUniqueId(uniqueId);
+}
+
+void DrmManagerService::removeUniqueId(int uniqueId) {
+    mDrmManager->removeUniqueId(uniqueId);
+}
+
+void DrmManagerService::addClient(int uniqueId) {
+    mDrmManager->addClient(uniqueId);
+}
+
+void DrmManagerService::removeClient(int uniqueId) {
+    mDrmManager->removeClient(uniqueId);
+}
+
+status_t DrmManagerService::setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
+    LOGV("Entering setDrmServiceListener");
+    mDrmManager->setDrmServiceListener(uniqueId, drmServiceListener);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmManagerService::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+    LOGV("Entering installDrmEngine");
+    return mDrmManager->installDrmEngine(uniqueId, drmEngineFile);
+}
+
+DrmConstraints* DrmManagerService::getConstraints(
+            int uniqueId, const String8* path, const int action) {
+    LOGV("Entering getConstraints from content");
+    return mDrmManager->getConstraints(uniqueId, path, action);
+}
+
+DrmMetadata* DrmManagerService::getMetadata(int uniqueId, const String8* path) {
+    LOGV("Entering getMetadata from content");
+    return mDrmManager->getMetadata(uniqueId, path);
+}
+
+bool DrmManagerService::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+    LOGV("Entering canHandle");
+    return mDrmManager->canHandle(uniqueId, path, mimeType);
+}
+
+DrmInfoStatus* DrmManagerService::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    LOGV("Entering processDrmInfo");
+    return mDrmManager->processDrmInfo(uniqueId, drmInfo);
+}
+
+DrmInfo* DrmManagerService::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    LOGV("Entering acquireDrmInfo");
+    return mDrmManager->acquireDrmInfo(uniqueId, drmInfoRequest);
+}
+
+status_t DrmManagerService::saveRights(
+            int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) {
+    LOGV("Entering saveRights");
+    return mDrmManager->saveRights(uniqueId, drmRights, rightsPath, contentPath);
+}
+
+String8 DrmManagerService::getOriginalMimeType(int uniqueId, const String8& path) {
+    LOGV("Entering getOriginalMimeType");
+    return mDrmManager->getOriginalMimeType(uniqueId, path);
+}
+
+int DrmManagerService::getDrmObjectType(
+           int uniqueId, const String8& path, const String8& mimeType) {
+    LOGV("Entering getDrmObjectType");
+    return mDrmManager->getDrmObjectType(uniqueId, path, mimeType);
+}
+
+int DrmManagerService::checkRightsStatus(
+            int uniqueId, const String8& path, int action) {
+    LOGV("Entering checkRightsStatus");
+    return mDrmManager->checkRightsStatus(uniqueId, path, action);
+}
+
+status_t DrmManagerService::consumeRights(
+            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    LOGV("Entering consumeRights");
+    return mDrmManager->consumeRights(uniqueId, decryptHandle, action, reserve);
+}
+
+status_t DrmManagerService::setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    LOGV("Entering setPlaybackStatus");
+    return mDrmManager->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
+}
+
+bool DrmManagerService::validateAction(
+            int uniqueId, const String8& path,
+            int action, const ActionDescription& description) {
+    LOGV("Entering validateAction");
+    return mDrmManager->validateAction(uniqueId, path, action, description);
+}
+
+status_t DrmManagerService::removeRights(int uniqueId, const String8& path) {
+    LOGV("Entering removeRights");
+    return mDrmManager->removeRights(uniqueId, path);
+}
+
+status_t DrmManagerService::removeAllRights(int uniqueId) {
+    LOGV("Entering removeAllRights");
+    return mDrmManager->removeAllRights(uniqueId);
+}
+
+int DrmManagerService::openConvertSession(int uniqueId, const String8& mimeType) {
+    LOGV("Entering openConvertSession");
+    return mDrmManager->openConvertSession(uniqueId, mimeType);
+}
+
+DrmConvertedStatus* DrmManagerService::convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) {
+    LOGV("Entering convertData");
+    return mDrmManager->convertData(uniqueId, convertId, inputData);
+}
+
+DrmConvertedStatus* DrmManagerService::closeConvertSession(int uniqueId, int convertId) {
+    LOGV("Entering closeConvertSession");
+    return mDrmManager->closeConvertSession(uniqueId, convertId);
+}
+
+status_t DrmManagerService::getAllSupportInfo(
+            int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+    LOGV("Entering getAllSupportInfo");
+    return mDrmManager->getAllSupportInfo(uniqueId, length, drmSupportInfoArray);
+}
+
+DecryptHandle* DrmManagerService::openDecryptSession(
+            int uniqueId, int fd, int offset, int length) {
+    LOGV("Entering DrmManagerService::openDecryptSession");
+    if (isProtectedCallAllowed()) {
+        return mDrmManager->openDecryptSession(uniqueId, fd, offset, length);
+    }
+
+    return NULL;
+}
+
+DecryptHandle* DrmManagerService::openDecryptSession(
+            int uniqueId, const char* uri) {
+    LOGV("Entering DrmManagerService::openDecryptSession with uri");
+    if (isProtectedCallAllowed()) {
+        return mDrmManager->openDecryptSession(uniqueId, uri);
+    }
+
+    return NULL;
+}
+
+status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+    LOGV("Entering closeDecryptSession");
+    return mDrmManager->closeDecryptSession(uniqueId, decryptHandle);
+}
+
+status_t DrmManagerService::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) {
+    LOGV("Entering initializeDecryptUnit");
+    return mDrmManager->initializeDecryptUnit(uniqueId,decryptHandle, decryptUnitId, headerInfo);
+}
+
+status_t DrmManagerService::decrypt(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    LOGV("Entering decrypt");
+    return mDrmManager->decrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+}
+
+status_t DrmManagerService::finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    LOGV("Entering finalizeDecryptUnit");
+    return mDrmManager->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+}
+
+ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset) {
+    LOGV("Entering pread");
+    return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+}
+
diff --git a/drm/drmserver/StringTokenizer.cpp b/drm/drmserver/StringTokenizer.cpp
new file mode 100644
index 0000000..2130a00
--- /dev/null
+++ b/drm/drmserver/StringTokenizer.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 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 "StringTokenizer.h"
+
+using namespace android;
+
+StringTokenizer::StringTokenizer(const String8& string, const String8& delimiter) {
+    splitString(string, delimiter);
+}
+
+void StringTokenizer::splitString(const String8& string, const String8& delimiter) {
+    for (unsigned int i = 0; i < string.length(); i++) {
+        unsigned int position = string.find(delimiter.string(), i);
+        if (string.length() != position) {
+            String8 token(string.string()+i, position-i);
+            if (token.length()) {
+                mStringTokenizerVector.push(token);
+                i = position + delimiter.length() - 1;
+            }
+        } else {
+            mStringTokenizerVector.push(String8(string.string()+i, string.length()-i));
+            break;
+        }
+    }
+}
+
+StringTokenizer::Iterator StringTokenizer::iterator() {
+    return Iterator(this);
+}
+
+StringTokenizer::Iterator::Iterator(const StringTokenizer::Iterator& iterator) :
+    mStringTokenizer(iterator.mStringTokenizer),
+    mIndex(iterator.mIndex) {
+}
+
+StringTokenizer::Iterator& StringTokenizer::Iterator::operator=(
+            const StringTokenizer::Iterator& iterator) {
+    mStringTokenizer = iterator.mStringTokenizer;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool StringTokenizer::Iterator::hasNext() {
+    return mIndex < mStringTokenizer->mStringTokenizerVector.size();
+}
+
+String8& StringTokenizer::Iterator::next() {
+    String8& value = mStringTokenizer->mStringTokenizerVector.editItemAt(mIndex);
+    mIndex++;
+    return value;
+}
+
diff --git a/drm/drmserver/main_drmserver.cpp b/drm/drmserver/main_drmserver.cpp
new file mode 100644
index 0000000..6d10646
--- /dev/null
+++ b/drm/drmserver/main_drmserver.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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 <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <private/android_filesystem_config.h>
+
+#include <DrmManagerService.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+    sp<ProcessState> proc(ProcessState::self());
+    sp<IServiceManager> sm = defaultServiceManager();
+    LOGI("ServiceManager: %p", sm.get());
+    DrmManagerService::instantiate();
+    ProcessState::self()->startThreadPool();
+    IPCThreadState::self()->joinThreadPool();
+}
+
diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk
new file mode 100644
index 0000000..99133ba
--- /dev/null
+++ b/drm/libdrmframework/Android.mk
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2010 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:= \
+    DrmManagerClientImpl.cpp \
+    DrmManagerClient.cpp
+
+LOCAL_MODULE:= libdrmframework
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils \
+    libbinder
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+    libdrmframeworkcommon
+
+LOCAL_C_INCLUDES += \
+    $(TOP)/frameworks/base/drm/libdrmframework/include \
+    $(TOP)/frameworks/base/include
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
new file mode 100644
index 0000000..8bb00c3
--- /dev/null
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2010 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/String8.h>
+#include <binder/IServiceManager.h>
+#include <drm/DrmManagerClient.h>
+
+#include "DrmManagerClientImpl.h"
+
+using namespace android;
+
+DrmManagerClient::DrmManagerClient():
+        mUniqueId(0), mDrmManagerClientImpl(NULL) {
+    mDrmManagerClientImpl = DrmManagerClientImpl::create(&mUniqueId);
+    mDrmManagerClientImpl->addClient(mUniqueId);
+}
+
+DrmManagerClient::~DrmManagerClient() {
+    DrmManagerClientImpl::remove(mUniqueId);
+    mDrmManagerClientImpl->removeClient(mUniqueId);
+    mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL);
+    delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL;
+}
+
+status_t DrmManagerClient::setOnInfoListener(
+                    const sp<DrmManagerClient::OnInfoListener>& infoListener) {
+    return mDrmManagerClientImpl->setOnInfoListener(mUniqueId, infoListener);
+}
+
+DrmConstraints* DrmManagerClient::getConstraints(const String8* path, const int action) {
+    return mDrmManagerClientImpl->getConstraints(mUniqueId, path, action);
+}
+
+DrmMetadata* DrmManagerClient::getMetadata(const String8* path) {
+    return mDrmManagerClientImpl->getMetadata(mUniqueId, path);
+}
+
+bool DrmManagerClient::canHandle(const String8& path, const String8& mimeType) {
+    return mDrmManagerClientImpl->canHandle(mUniqueId, path, mimeType);
+}
+
+DrmInfoStatus* DrmManagerClient::processDrmInfo(const DrmInfo* drmInfo) {
+    return mDrmManagerClientImpl->processDrmInfo(mUniqueId, drmInfo);
+}
+
+DrmInfo* DrmManagerClient::acquireDrmInfo(const DrmInfoRequest* drmInfoRequest) {
+    return mDrmManagerClientImpl->acquireDrmInfo(mUniqueId, drmInfoRequest);
+}
+
+status_t DrmManagerClient::saveRights(
+        const DrmRights& drmRights, const String8& rightsPath, const String8& contentPath) {
+    return mDrmManagerClientImpl->saveRights(mUniqueId, drmRights, rightsPath, contentPath);
+}
+
+String8 DrmManagerClient::getOriginalMimeType(const String8& path) {
+    return mDrmManagerClientImpl->getOriginalMimeType(mUniqueId, path);
+}
+
+int DrmManagerClient::getDrmObjectType(const String8& path, const String8& mimeType) {
+    return mDrmManagerClientImpl->getDrmObjectType( mUniqueId, path, mimeType);
+}
+
+int DrmManagerClient::checkRightsStatus(const String8& path, int action) {
+    return mDrmManagerClientImpl->checkRightsStatus(mUniqueId, path, action);
+}
+
+status_t DrmManagerClient::consumeRights(DecryptHandle* decryptHandle, int action, bool reserve) {
+    Mutex::Autolock _l(mDecryptLock);
+    return mDrmManagerClientImpl->consumeRights(mUniqueId, decryptHandle, action, reserve);
+}
+
+status_t DrmManagerClient::setPlaybackStatus(
+            DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    return mDrmManagerClientImpl
+            ->setPlaybackStatus(mUniqueId, decryptHandle, playbackStatus, position);
+}
+
+bool DrmManagerClient::validateAction(
+            const String8& path, int action, const ActionDescription& description) {
+    return mDrmManagerClientImpl->validateAction(mUniqueId, path, action, description);
+}
+
+status_t DrmManagerClient::removeRights(const String8& path) {
+    return mDrmManagerClientImpl->removeRights(mUniqueId, path);
+}
+
+status_t DrmManagerClient::removeAllRights() {
+    return mDrmManagerClientImpl->removeAllRights(mUniqueId);
+}
+
+int DrmManagerClient::openConvertSession(const String8& mimeType) {
+    return mDrmManagerClientImpl->openConvertSession(mUniqueId, mimeType);
+}
+
+DrmConvertedStatus* DrmManagerClient::convertData(int convertId, const DrmBuffer* inputData) {
+    return mDrmManagerClientImpl->convertData(mUniqueId, convertId, inputData);
+}
+
+DrmConvertedStatus* DrmManagerClient::closeConvertSession(int convertId) {
+    return mDrmManagerClientImpl->closeConvertSession(mUniqueId, convertId);
+}
+
+status_t DrmManagerClient::getAllSupportInfo(int* length, DrmSupportInfo** drmSupportInfoArray) {
+    return mDrmManagerClientImpl->getAllSupportInfo(mUniqueId, length, drmSupportInfoArray);
+}
+
+DecryptHandle* DrmManagerClient::openDecryptSession(int fd, int offset, int length) {
+    return mDrmManagerClientImpl->openDecryptSession(mUniqueId, fd, offset, length);
+}
+
+DecryptHandle* DrmManagerClient::openDecryptSession(const char* uri) {
+    return mDrmManagerClientImpl->openDecryptSession(mUniqueId, uri);
+}
+
+status_t DrmManagerClient::closeDecryptSession(DecryptHandle* decryptHandle) {
+    return mDrmManagerClientImpl->closeDecryptSession(mUniqueId, decryptHandle);
+}
+
+status_t DrmManagerClient::initializeDecryptUnit(
+            DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+    Mutex::Autolock _l(mDecryptLock);
+    return mDrmManagerClientImpl->initializeDecryptUnit(
+            mUniqueId, decryptHandle, decryptUnitId, headerInfo);
+}
+
+status_t DrmManagerClient::decrypt(
+    DecryptHandle* decryptHandle, int decryptUnitId,
+    const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    Mutex::Autolock _l(mDecryptLock);
+    return mDrmManagerClientImpl->decrypt(
+            mUniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+}
+
+status_t DrmManagerClient::finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId) {
+    Mutex::Autolock _l(mDecryptLock);
+    return mDrmManagerClientImpl->finalizeDecryptUnit(mUniqueId, decryptHandle, decryptUnitId);
+}
+
+ssize_t DrmManagerClient::pread(
+            DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset) {
+    Mutex::Autolock _l(mDecryptLock);
+    return mDrmManagerClientImpl->pread(mUniqueId, decryptHandle, buffer, numBytes, offset);
+}
+
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
new file mode 100644
index 0000000..eea312b
--- /dev/null
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2010 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 "DrmManagerClientImpl(Native)"
+#include <utils/Log.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <binder/IServiceManager.h>
+
+#include "DrmManagerClientImpl.h"
+
+using namespace android;
+
+#define INVALID_VALUE -1
+
+Mutex DrmManagerClientImpl::mMutex;
+sp<IDrmManagerService> DrmManagerClientImpl::mDrmManagerService;
+const String8 DrmManagerClientImpl::EMPTY_STRING("");
+
+DrmManagerClientImpl* DrmManagerClientImpl::create(int* pUniqueId) {
+    if (0 == *pUniqueId) {
+        int uniqueId = getDrmManagerService()->addUniqueId(*pUniqueId);
+        *pUniqueId = uniqueId;
+    } else {
+        getDrmManagerService()->addUniqueId(*pUniqueId);
+    }
+    return new DrmManagerClientImpl();
+}
+
+void DrmManagerClientImpl::remove(int uniqueId) {
+    getDrmManagerService()->removeUniqueId(uniqueId);
+}
+
+const sp<IDrmManagerService>& DrmManagerClientImpl::getDrmManagerService() {
+    mMutex.lock();
+    if (NULL == mDrmManagerService.get()) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder;
+        do {
+            binder = sm->getService(String16("drm.drmManager"));
+            if (binder != 0) {
+                break;
+            }
+            LOGW("DrmManagerService not published, waiting...");
+            struct timespec reqt;
+            reqt.tv_sec  = 0;
+            reqt.tv_nsec = 500000000; //0.5 sec
+            nanosleep(&reqt, NULL);
+        } while (true);
+
+        mDrmManagerService = interface_cast<IDrmManagerService>(binder);
+    }
+    mMutex.unlock();
+    return mDrmManagerService;
+}
+
+void DrmManagerClientImpl::addClient(int uniqueId) {
+    getDrmManagerService()->addClient(uniqueId);
+}
+
+void DrmManagerClientImpl::removeClient(int uniqueId) {
+    getDrmManagerService()->removeClient(uniqueId);
+}
+
+status_t DrmManagerClientImpl::setOnInfoListener(
+            int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) {
+    Mutex::Autolock _l(mLock);
+    mOnInfoListener = infoListener;
+    return getDrmManagerService()->setDrmServiceListener(uniqueId,
+            (NULL != infoListener.get()) ? this : NULL);
+}
+
+status_t DrmManagerClientImpl::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (EMPTY_STRING != drmEngineFile) {
+        status = getDrmManagerService()->installDrmEngine(uniqueId, drmEngineFile);
+    }
+    return status;
+}
+
+DrmConstraints* DrmManagerClientImpl::getConstraints(
+        int uniqueId, const String8* path, const int action) {
+    DrmConstraints *drmConstraints = NULL;
+    if ((NULL != path) && (EMPTY_STRING != *path)) {
+        drmConstraints = getDrmManagerService()->getConstraints(uniqueId, path, action);
+    }
+    return drmConstraints;
+}
+
+DrmMetadata* DrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) {
+    DrmMetadata *drmMetadata = NULL;
+    if ((NULL != path) && (EMPTY_STRING != *path)) {
+        drmMetadata = getDrmManagerService()->getMetadata(uniqueId, path);
+    }
+    return drmMetadata;
+}
+
+bool DrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+    bool retCode = false;
+    if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
+        retCode = getDrmManagerService()->canHandle(uniqueId, path, mimeType);
+    }
+    return retCode;
+}
+
+DrmInfoStatus* DrmManagerClientImpl::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    DrmInfoStatus *drmInfoStatus = NULL;
+    if (NULL != drmInfo) {
+        drmInfoStatus = getDrmManagerService()->processDrmInfo(uniqueId, drmInfo);
+    }
+    return drmInfoStatus;
+}
+
+DrmInfo* DrmManagerClientImpl::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    DrmInfo* drmInfo = NULL;
+    if (NULL != drmInfoRequest) {
+        drmInfo = getDrmManagerService()->acquireDrmInfo(uniqueId, drmInfoRequest);
+    }
+    return drmInfo;
+}
+
+status_t DrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (EMPTY_STRING != contentPath) {
+        status = getDrmManagerService()->saveRights(uniqueId, drmRights, rightsPath, contentPath);
+    }
+    return status;
+}
+
+String8 DrmManagerClientImpl::getOriginalMimeType(int uniqueId, const String8& path) {
+    String8 mimeType = EMPTY_STRING;
+    if (EMPTY_STRING != path) {
+        mimeType = getDrmManagerService()->getOriginalMimeType(uniqueId, path);
+    }
+    return mimeType;
+}
+
+int DrmManagerClientImpl::getDrmObjectType(
+            int uniqueId, const String8& path, const String8& mimeType) {
+    int drmOjectType = DrmObjectType::UNKNOWN;
+    if ((EMPTY_STRING != path) || (EMPTY_STRING != mimeType)) {
+         drmOjectType = getDrmManagerService()->getDrmObjectType(uniqueId, path, mimeType);
+    }
+    return drmOjectType;
+}
+
+int DrmManagerClientImpl::checkRightsStatus(
+            int uniqueId, const String8& path, int action) {
+    int rightsStatus = RightsStatus::RIGHTS_INVALID;
+    if (EMPTY_STRING != path) {
+        rightsStatus = getDrmManagerService()->checkRightsStatus(uniqueId, path, action);
+    }
+    return rightsStatus;
+}
+
+status_t DrmManagerClientImpl::consumeRights(
+            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (NULL != decryptHandle) {
+        status = getDrmManagerService()->consumeRights(uniqueId, decryptHandle, action, reserve);
+    }
+    return status;
+}
+
+status_t DrmManagerClientImpl::setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (NULL != decryptHandle) {
+        status = getDrmManagerService()->setPlaybackStatus(
+                uniqueId, decryptHandle, playbackStatus, position);
+    }
+    return status;
+}
+
+bool DrmManagerClientImpl::validateAction(
+            int uniqueId, const String8& path, int action, const ActionDescription& description) {
+    bool retCode = false;
+    if (EMPTY_STRING != path) {
+        retCode = getDrmManagerService()->validateAction(uniqueId, path, action, description);
+    }
+    return retCode;
+}
+
+status_t DrmManagerClientImpl::removeRights(int uniqueId, const String8& path) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (EMPTY_STRING != path) {
+        status = getDrmManagerService()->removeRights(uniqueId, path);
+    }
+    return status;
+}
+
+status_t DrmManagerClientImpl::removeAllRights(int uniqueId) {
+    return getDrmManagerService()->removeAllRights(uniqueId);
+}
+
+int DrmManagerClientImpl::openConvertSession(int uniqueId, const String8& mimeType) {
+    int retCode = INVALID_VALUE;
+    if (EMPTY_STRING != mimeType) {
+        retCode = getDrmManagerService()->openConvertSession(uniqueId, mimeType);
+    }
+    return retCode;
+}
+
+DrmConvertedStatus* DrmManagerClientImpl::convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) {
+    DrmConvertedStatus* drmConvertedStatus = NULL;
+    if (NULL != inputData) {
+         drmConvertedStatus = getDrmManagerService()->convertData(uniqueId, convertId, inputData);
+    }
+    return drmConvertedStatus;
+}
+
+DrmConvertedStatus* DrmManagerClientImpl::closeConvertSession(int uniqueId, int convertId) {
+    return getDrmManagerService()->closeConvertSession(uniqueId, convertId);
+}
+
+status_t DrmManagerClientImpl::getAllSupportInfo(
+            int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if ((NULL != drmSupportInfoArray) && (NULL != length)) {
+        status = getDrmManagerService()->getAllSupportInfo(uniqueId, length, drmSupportInfoArray);
+    }
+    return status;
+}
+
+DecryptHandle* DrmManagerClientImpl::openDecryptSession(
+            int uniqueId, int fd, int offset, int length) {
+    return getDrmManagerService()->openDecryptSession(uniqueId, fd, offset, length);
+}
+
+DecryptHandle* DrmManagerClientImpl::openDecryptSession(int uniqueId, const char* uri) {
+    DecryptHandle* handle = NULL;
+    if (NULL != uri) {
+        handle = getDrmManagerService()->openDecryptSession(uniqueId, uri);
+    }
+    return handle;
+}
+
+status_t DrmManagerClientImpl::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (NULL != decryptHandle) {
+        status = getDrmManagerService()->closeDecryptSession( uniqueId, decryptHandle);
+    }
+    return status;
+}
+
+status_t DrmManagerClientImpl::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if ((NULL != decryptHandle) && (NULL != headerInfo)) {
+        status = getDrmManagerService()->initializeDecryptUnit(
+                uniqueId, decryptHandle, decryptUnitId, headerInfo);
+    }
+    return status;
+}
+
+status_t DrmManagerClientImpl::decrypt(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if ((NULL != decryptHandle) && (NULL != encBuffer)
+        && (NULL != decBuffer) && (NULL != *decBuffer)) {
+        status = getDrmManagerService()->decrypt(
+                uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
+    }
+    return status;
+}
+
+status_t DrmManagerClientImpl::finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    status_t status = DRM_ERROR_UNKNOWN;
+    if (NULL != decryptHandle) {
+        status
+            = getDrmManagerService()->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
+    }
+    return status;
+}
+
+ssize_t DrmManagerClientImpl::pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset) {
+    ssize_t retCode = INVALID_VALUE;
+    if ((NULL != decryptHandle) && (NULL != buffer) && (0 < numBytes)) {
+        retCode = getDrmManagerService()->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
+    }
+    return retCode;
+}
+
+status_t DrmManagerClientImpl::notify(const DrmInfoEvent& event) {
+    if (NULL != mOnInfoListener.get()) {
+        Mutex::Autolock _l(mLock);
+        sp<DrmManagerClient::OnInfoListener> listener = mOnInfoListener;
+        listener->onInfo(event);
+    }
+    return DRM_NO_ERROR;
+}
+
diff --git a/drm/libdrmframework/include/DrmIOService.h b/drm/libdrmframework/include/DrmIOService.h
new file mode 100644
index 0000000..244124e
--- /dev/null
+++ b/drm/libdrmframework/include/DrmIOService.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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_IO_SERVICE_H__
+#define __DRM_IO_SERVICE_H__
+
+#include "IDrmIOService.h"
+
+namespace android {
+
+/**
+ * This is the implementation class for DRM IO service.
+ *
+ * The instance of this class is created while starting the DRM IO service.
+ *
+ */
+class DrmIOService : public BnDrmIOService {
+public:
+    static void instantiate();
+
+private:
+    DrmIOService();
+    virtual ~DrmIOService();
+
+public:
+    void writeToFile(const String8& filePath, const String8& dataBuffer);
+    String8 readFromFile(const String8& filePath);
+};
+
+};
+
+#endif /* __DRM_IO_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/DrmManager.h b/drm/libdrmframework/include/DrmManager.h
new file mode 100644
index 0000000..bc462c2
--- /dev/null
+++ b/drm/libdrmframework/include/DrmManager.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010 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_MANAGER_H__
+#define __DRM_MANAGER_H__
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <drm/drm_framework_common.h>
+#include "IDrmEngine.h"
+#include "PlugInManager.h"
+#include "IDrmServiceListener.h"
+
+namespace android {
+
+class IDrmManager;
+class DrmRegistrationInfo;
+class DrmUnregistrationInfo;
+class DrmRightsAcquisitionInfo;
+class DrmContentIds;
+class DrmConstraints;
+class DrmMetadata;
+class DrmRights;
+class DrmInfo;
+class DrmInfoStatus;
+class DrmConvertedStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class ActionDescription;
+
+/**
+ * This is implementation class for DRM Manager. This class delegates the
+ * functionality to corresponding DRM Engine.
+ *
+ * The DrmManagerService class creates an instance of this class.
+ *
+ */
+class DrmManager : public IDrmEngine::OnInfoListener {
+public:
+    DrmManager();
+    virtual ~DrmManager();
+
+public:
+    int addUniqueId(int uniqueId);
+
+    void removeUniqueId(int uniqueId);
+
+    void addClient(int uniqueId);
+
+    void removeClient(int uniqueId);
+
+    status_t loadPlugIns();
+
+    status_t loadPlugIns(const String8& plugInDirPath);
+
+    status_t unloadPlugIns();
+
+    status_t setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& drmServiceListener);
+
+    status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+    DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+    bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+    DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+    DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+    status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath);
+
+    String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+    int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+    int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+    status_t setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    bool validateAction(
+            int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+    status_t removeRights(int uniqueId, const String8& path);
+
+    status_t removeAllRights(int uniqueId);
+
+    int openConvertSession(int uniqueId, const String8& mimeType);
+
+    DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+    DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+    status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+    DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+    DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo);
+
+    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset);
+
+    void onInfo(const DrmInfoEvent& event);
+
+private:
+    String8 getSupportedPlugInId(int uniqueId, const String8& path, const String8& mimeType);
+
+    String8 getSupportedPlugInId(const String8& mimeType);
+
+    String8 getSupportedPlugInIdFromPath(int uniqueId, const String8& path);
+
+    bool canHandle(int uniqueId, const String8& path);
+
+private:
+    static Vector<int> mUniqueIdVector;
+    static const String8 EMPTY_STRING;
+
+    int mDecryptSessionId;
+    int mConvertId;
+    Mutex mLock;
+    Mutex mDecryptLock;
+    Mutex mConvertLock;
+    TPlugInManager<IDrmEngine> mPlugInManager;
+    KeyedVector< DrmSupportInfo, String8 > mSupportInfoToPlugInIdMap;
+    KeyedVector< int, IDrmEngine*> mConvertSessionMap;
+    KeyedVector< int, sp<IDrmServiceListener> > mServiceListeners;
+    KeyedVector< int, IDrmEngine*> mDecryptSessionMap;
+};
+
+};
+
+#endif /* __DRM_MANAGER_H__ */
+
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
new file mode 100644
index 0000000..ff84fc7
--- /dev/null
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2010 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_MANAGER_CLIENT_IMPL_H__
+#define __DRM_MANAGER_CLIENT_IMPL_H__
+
+#include <binder/IMemory.h>
+#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
+
+#include "IDrmManagerService.h"
+
+namespace android {
+
+class DrmInfoEvent;
+/**
+ * This is implementation class for DrmManagerClient class.
+ *
+ * Only the JNI layer creates an instance of this class to delegate
+ * functionality to Native later.
+ *
+ */
+class DrmManagerClientImpl : public BnDrmServiceListener {
+private:
+    DrmManagerClientImpl() { }
+
+public:
+    static DrmManagerClientImpl* create(int* pUniqueId);
+
+    static void remove(int uniqueId);
+
+    virtual ~DrmManagerClientImpl() { }
+
+public:
+    /**
+     * Adds the client respective to given unique id.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     */
+    void addClient(int uniqueId);
+
+    /**
+     * Removes the client respective to given unique id.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     */
+    void removeClient(int uniqueId);
+
+    /**
+     * Register a callback to be invoked when the caller required to
+     * receive necessary information
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] infoListener Listener
+     * @return status_t
+     *            Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t setOnInfoListener(
+            int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener);
+
+    /**
+     * Get constraint information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Actions defined such as,
+     *     Action::DEFAULT, Action::PLAY, etc
+     * @return DrmConstraints
+     *     key-value pairs of constraint are embedded in it
+     * @note
+     *     In case of error, return NULL
+     */
+    DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+    /**
+     * Get metadata information associated with input content.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata are embedded in it
+     * @note
+     *    In case of error, return NULL
+     */
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+    /**
+     * Check whether the given mimetype or path can be handled
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the content needs to be handled
+     * @param[in] mimetype Mimetype of the content needs to be handled
+     * @return
+     *     True if DrmManager can handle given path or mime type.
+     */
+    bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+    /**
+     * Executes given drm information based on its type
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmInfo Information needs to be processed
+     * @return DrmInfoStatus
+     *     instance as a result of processing given input
+     */
+    DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+    /**
+     * Retrieves necessary information for registration, unregistration or rights
+     * acquisition information.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmInfoRequest Request information to retrieve drmInfo
+     * @return DrmInfo
+     *     instance as a result of processing given input
+     */
+    DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+    /**
+     * Save DRM rights to specified rights path
+     * and make association with content path
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmRights DrmRights to be saved
+     * @param[in] rightsPath File path where rights to be saved
+     * @param[in] contentPath File path where content was saved
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath);
+
+    /**
+     * Retrieves the mime type embedded inside the original content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path the path of the protected content
+     * @return String8
+     *     Returns mime-type of the original content, such as "video/mpeg"
+     */
+    String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+    /**
+     * Retrieves the type of the protected object (content, rights, etc..)
+     * using specified path or mimetype. At least one parameter should be non null
+     * to retrieve DRM object type
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the content or null.
+     * @param[in] mimeType Mime type of the content or null.
+     * @return type of the DRM content,
+     *     such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+     */
+    int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+    /**
+     * Check whether the given content has valid rights or not
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+     * @return the status of the rights for the protected content,
+     *     such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+     */
+    int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+    /**
+     * Consumes the rights for a content.
+     * If the reserve parameter is true the rights is reserved until the same
+     * application calls this api again with the reserve parameter set to false.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+     * @param[in] reserve True if the rights should be reserved.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+    /**
+     * Informs the DRM engine about the playback actions performed on the DRM files.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+     * @param[in] position Position in the file (in milliseconds) where the start occurs.
+     *     Only valid together with Playback::START.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    /**
+     * Validates whether an action on the DRM content is allowed or not.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+     * @param[in] description Detailed description of the action
+     * @return true if the action is allowed.
+     */
+    bool validateAction(
+        int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+    /**
+     * Removes the rights associated with the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t removeRights(int uniqueId, const String8& path);
+
+    /**
+     * Removes all the rights information of each plug-in associated with
+     * DRM framework. Will be used in master reset
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t removeAllRights(int uniqueId);
+
+    /**
+     * This API is for Forward Lock based DRM scheme.
+     * Each time the application tries to download a new DRM file
+     * which needs to be converted, then the application has to
+     * begin with calling this API.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] mimeType Description/MIME type of the input data packet
+     * @return Return handle for the convert session
+     */
+    int openConvertSession(int uniqueId, const String8& mimeType);
+
+    /**
+     * Accepts and converts the input data which is part of DRM file.
+     * The resultant converted data and the status is returned in the DrmConvertedInfo
+     * object. This method will be called each time there are new block
+     * of data received by the application.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @param[in] inputData Input Data which need to be converted
+     * @return Return object contains the status of the data conversion,
+     *     the output converted data and offset. In this case the
+     *     application will ignore the offset information.
+     */
+    DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+    /**
+     * Informs the Drm Agent when there is no more data which need to be converted
+     * or when an error occurs. Upon successful conversion of the complete data,
+     * the agent will inform that where the header and body signature
+     * should be added. This signature appending is needed to integrity
+     * protect the converted file.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @return Return object contains the status of the data conversion,
+     *     the header and body signature data. It also informs
+     *     the application on which offset these signature data
+     *     should be appended.
+     */
+    DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+    /**
+     * Retrieves all DrmSupportInfo instance that native DRM framework can handle.
+     * This interface is meant to be used by JNI layer
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[out] length Number of elements in drmSupportInfoArray
+     * @param[out] drmSupportInfoArray Array contains all DrmSupportInfo
+     *             that native DRM framework can handle
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] fd File descriptor of the protected content to be decrypted
+     * @param[in] offset Start position of the content
+     * @param[in] length The length of the protected content
+     * @return
+     *     Handle for the decryption session
+     */
+    DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] uri Path of the protected content to be decrypted
+     * @return
+     *     Handle for the decryption session
+     */
+    DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+    /**
+     * Close the decrypt session for the given handle
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+    /**
+     * Initialize decryption for the given unit of the protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo);
+
+    /**
+     * Decrypt the protected content buffers for the given unit
+     * This method will be called any number of times, based on number of
+     * encrypted streams received from application.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @param[in] encBuffer Encrypted data block
+     * @param[out] decBuffer Decrypted data block
+     * @param[in] IV Optional buffer
+     * @return status_t
+     *     Returns the error code for this API
+     *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+     *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+     *     DRM_ERROR_DECRYPT for failure.
+     */
+    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+    /**
+     * Finalize decryption for the given unit of the protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+    /**
+     * Reads the specified number of bytes from an open DRM file.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[out] buffer Reference to the buffer that should receive the read data.
+     * @param[in] numBytes Number of bytes to read.
+     * @param[in] offset Offset with which to update the file position.
+     *
+     * @return Number of bytes read. Returns -1 for Failure.
+     */
+    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset);
+
+    /**
+     * Notify the event to the registered listener
+     *
+     * @param[in] event The event to be notified
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t notify(const DrmInfoEvent& event);
+
+private:
+    /**
+     * Install new DRM Engine Plug-in at the runtime
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmEngine Shared Object(so) File in which DRM Engine defined
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+private:
+    Mutex mLock;
+    sp<DrmManagerClient::OnInfoListener> mOnInfoListener;
+
+private:
+    static Mutex mMutex;
+    static sp<IDrmManagerService> mDrmManagerService;
+    static const sp<IDrmManagerService>& getDrmManagerService();
+    static const String8 EMPTY_STRING;
+};
+
+};
+
+#endif /* __DRM_MANAGER_CLIENT_IMPL_H__ */
+
diff --git a/drm/libdrmframework/include/DrmManagerService.h b/drm/libdrmframework/include/DrmManagerService.h
new file mode 100644
index 0000000..f346356
--- /dev/null
+++ b/drm/libdrmframework/include/DrmManagerService.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 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_MANAGER_SERVICE_H__
+#define __DRM_MANAGER_SERVICE_H__
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include "IDrmManagerService.h"
+#include "IDrmServiceListener.h"
+
+namespace android {
+
+class DrmManager;
+class String8;
+class Mutex;
+
+/**
+ * This is the implementation class for DRM manager service. This delegates
+ * the responsibility to DrmManager.
+ *
+ * The instance of this class is created while starting the DRM manager service.
+ *
+ */
+class DrmManagerService : public BnDrmManagerService {
+public:
+    static void instantiate();
+
+private:
+    DrmManagerService();
+    virtual ~DrmManagerService();
+
+public:
+    int addUniqueId(int uniqueId);
+
+    void removeUniqueId(int uniqueId);
+
+    void addClient(int uniqueId);
+
+    void removeClient(int uniqueId);
+
+    status_t setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& drmServiceListener);
+
+    status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+    DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+    bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+    DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+    DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest);
+
+    status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath);
+
+    String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+    int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+    int checkRightsStatus(int uniqueId, const String8& path,int action);
+
+    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+    status_t setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    bool validateAction(int uniqueId, const String8& path,
+            int action, const ActionDescription& description);
+
+    status_t removeRights(int uniqueId, const String8& path);
+
+    status_t removeAllRights(int uniqueId);
+
+    int openConvertSession(int uniqueId, const String8& mimeType);
+
+    DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+    DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+    status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+    DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+    DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo);
+
+    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset);
+
+private:
+    DrmManager* mDrmManager;
+};
+
+};
+
+#endif /* __DRM_MANAGER_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/IDrmIOService.h b/drm/libdrmframework/include/IDrmIOService.h
new file mode 100644
index 0000000..5e0d907
--- /dev/null
+++ b/drm/libdrmframework/include/IDrmIOService.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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 __IDRM_IO_SERVICE_H__
+#define __IDRM_IO_SERVICE_H__
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+/**
+ * This is the interface class for DRM IO service.
+ *
+ */
+class IDrmIOService : public IInterface
+{
+public:
+    enum {
+        WRITE_TO_FILE = IBinder::FIRST_CALL_TRANSACTION,
+        READ_FROM_FILE
+    };
+
+public:
+    DECLARE_META_INTERFACE(DrmIOService);
+
+public:
+    /**
+     * Writes the data into the file path provided
+     *
+     * @param[in] filePath Path of the file
+     * @param[in] dataBuffer Data to write
+     */
+    virtual void writeToFile(const String8& filePath, const String8& dataBuffer) = 0;
+
+    /**
+     * Reads the data from the file path provided
+     *
+     * @param[in] filePath Path of the file
+     * @return Data read from the file
+     */
+    virtual String8 readFromFile(const String8& filePath) = 0;
+};
+
+/**
+ * This is the Binder implementation class for DRM IO service.
+ */
+class BpDrmIOService: public BpInterface<IDrmIOService>
+{
+public:
+    BpDrmIOService(const sp<IBinder>& impl)
+            : BpInterface<IDrmIOService>(impl) {}
+
+    virtual void writeToFile(const String8& filePath, const String8& dataBuffer);
+
+    virtual String8 readFromFile(const String8& filePath);
+};
+
+/**
+ * This is the Binder implementation class for DRM IO service.
+ */
+class BnDrmIOService: public BnInterface<IDrmIOService>
+{
+public:
+    virtual status_t onTransact(
+            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};
+
+#endif /* __IDRM_IO_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/IDrmManagerService.h b/drm/libdrmframework/include/IDrmManagerService.h
new file mode 100644
index 0000000..f1dabd3
--- /dev/null
+++ b/drm/libdrmframework/include/IDrmManagerService.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2010 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 __IDRM_MANAGER_SERVICE_H__
+#define __IDRM_MANAGER_SERVICE_H__
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <drm/drm_framework_common.h>
+#include "IDrmServiceListener.h"
+
+namespace android {
+
+class DrmContentIds;
+class DrmConstraints;
+class DrmMetadata;
+class DrmRights;
+class DrmInfo;
+class DrmInfoStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class DrmConvertedStatus;
+class String8;
+class ActionDescription;
+
+/**
+ * This is the interface class for DRM Manager service.
+ *
+ */
+class IDrmManagerService : public IInterface
+{
+public:
+    enum {
+        ADD_UNIQUEID = IBinder::FIRST_CALL_TRANSACTION,
+        REMOVE_UNIQUEID,
+        ADD_CLIENT,
+        REMOVE_CLIENT,
+        SET_DRM_SERVICE_LISTENER,
+        INSTALL_DRM_ENGINE,
+        GET_CONSTRAINTS_FROM_CONTENT,
+        GET_METADATA_FROM_CONTENT,
+        CAN_HANDLE,
+        PROCESS_DRM_INFO,
+        ACQUIRE_DRM_INFO,
+        SAVE_RIGHTS,
+        GET_ORIGINAL_MIMETYPE,
+        GET_DRM_OBJECT_TYPE,
+        CHECK_RIGHTS_STATUS,
+        CONSUME_RIGHTS,
+        SET_PLAYBACK_STATUS,
+        VALIDATE_ACTION,
+        REMOVE_RIGHTS,
+        REMOVE_ALL_RIGHTS,
+        OPEN_CONVERT_SESSION,
+        CONVERT_DATA,
+        CLOSE_CONVERT_SESSION,
+        GET_ALL_SUPPORT_INFO,
+        OPEN_DECRYPT_SESSION,
+        OPEN_DECRYPT_SESSION_FROM_URI,
+        CLOSE_DECRYPT_SESSION,
+        INITIALIZE_DECRYPT_UNIT,
+        DECRYPT,
+        FINALIZE_DECRYPT_UNIT,
+        PREAD
+    };
+
+public:
+    DECLARE_META_INTERFACE(DrmManagerService);
+
+public:
+    virtual int addUniqueId(int uniqueId) = 0;
+
+    virtual void removeUniqueId(int uniqueId) = 0;
+
+    virtual void addClient(int uniqueId) = 0;
+
+    virtual void removeClient(int uniqueId) = 0;
+
+    virtual status_t setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& infoListener) = 0;
+
+    virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile) = 0;
+
+    virtual DrmConstraints* getConstraints(
+            int uniqueId, const String8* path, const int action) = 0;
+
+    virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
+    virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+    virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
+
+    virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) = 0;
+
+    virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) = 0;
+
+    virtual String8 getOriginalMimeType(int uniqueId, const String8& path) = 0;
+
+    virtual int getDrmObjectType(
+            int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+    virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0;
+
+    virtual status_t consumeRights(
+            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
+
+    virtual status_t setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+
+    virtual bool validateAction(
+            int uniqueId, const String8& path,
+            int action, const ActionDescription& description) = 0;
+
+    virtual status_t removeRights(int uniqueId, const String8& path) = 0;
+
+    virtual status_t removeAllRights(int uniqueId) = 0;
+
+    virtual int openConvertSession(int uniqueId, const String8& mimeType) = 0;
+
+    virtual DrmConvertedStatus* convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) = 0;
+
+    virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId) = 0;
+
+    virtual status_t getAllSupportInfo(
+            int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) = 0;
+
+    virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length) = 0;
+
+    virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri) = 0;
+
+    virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+
+    virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) = 0;
+
+    virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
+
+    virtual status_t finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+
+    virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes,off_t offset) = 0;
+};
+
+/**
+ * This is the Binder implementation class for DRM Manager service.
+ */
+class BpDrmManagerService: public BpInterface<IDrmManagerService>
+{
+public:
+    BpDrmManagerService(const sp<IBinder>& impl)
+            : BpInterface<IDrmManagerService>(impl) {}
+
+    virtual int addUniqueId(int uniqueId);
+
+    virtual void removeUniqueId(int uniqueId);
+
+    virtual void addClient(int uniqueId);
+
+    virtual void removeClient(int uniqueId);
+
+    virtual status_t setDrmServiceListener(
+            int uniqueId, const sp<IDrmServiceListener>& infoListener);
+
+    virtual status_t installDrmEngine(int uniqueId, const String8& drmEngineFile);
+
+    virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+    virtual DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+    virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+
+    virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+    virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest);
+
+    virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath);
+
+    virtual String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+    virtual int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+    virtual int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+    virtual status_t consumeRights(
+            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+    virtual status_t setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    virtual bool validateAction(
+            int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+    virtual status_t removeRights(int uniqueId, const String8& path);
+
+    virtual status_t removeAllRights(int uniqueId);
+
+    virtual int openConvertSession(int uniqueId, const String8& mimeType);
+
+    virtual DrmConvertedStatus* convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData);
+
+    virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+    virtual status_t getAllSupportInfo(
+            int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+
+    virtual DecryptHandle* openDecryptSession(int uniqueId, int fd, int offset, int length);
+
+    virtual DecryptHandle* openDecryptSession(int uniqueId, const char* uri);
+
+    virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+    virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo);
+
+    virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+    virtual status_t finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+    virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset);
+};
+
+/**
+ * This is the Binder implementation class for DRM Manager service.
+ */
+class BnDrmManagerService: public BnInterface<IDrmManagerService>
+{
+public:
+    virtual status_t onTransact(
+            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};
+
+#endif /* __IDRM_MANAGER_SERVICE_H__ */
+
diff --git a/drm/libdrmframework/include/IDrmServiceListener.h b/drm/libdrmframework/include/IDrmServiceListener.h
new file mode 100644
index 0000000..7f7109f
--- /dev/null
+++ b/drm/libdrmframework/include/IDrmServiceListener.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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 __IDRM_SERVICE_LISTENER_H__
+#define __IDRM_SERVICE_LISTENER_H__
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class DrmInfoEvent;
+
+/**
+ * This is the interface class for DRM service listener.
+ *
+ */
+class IDrmServiceListener : public IInterface
+{
+public:
+    enum {
+        NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+    };
+
+public:
+    DECLARE_META_INTERFACE(DrmServiceListener);
+
+public:
+    virtual status_t notify(const DrmInfoEvent& event) = 0;
+};
+
+/**
+ * This is the Binder implementation class for DRM service listener.
+ */
+class BpDrmServiceListener: public BpInterface<IDrmServiceListener>
+{
+public:
+    BpDrmServiceListener(const sp<IBinder>& impl)
+            : BpInterface<IDrmServiceListener>(impl) {}
+
+    virtual status_t notify(const DrmInfoEvent& event);
+};
+
+/**
+ * This is the Binder implementation class for DRM service listener.
+ */
+class BnDrmServiceListener: public BnInterface<IDrmServiceListener>
+{
+public:
+    virtual status_t onTransact(
+            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+};
+
+#endif /* __IDRM_SERVICE_LISTENER_H__ */
+
diff --git a/drm/libdrmframework/include/PlugInManager.h b/drm/libdrmframework/include/PlugInManager.h
new file mode 100644
index 0000000..9ad195f
--- /dev/null
+++ b/drm/libdrmframework/include/PlugInManager.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2010 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 __PLUGIN_MANAGER_H__
+#define __PLUGIN_MANAGER_H__
+
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+const char* const PLUGIN_MANAGER_CREATE = "create";
+const char* const PLUGIN_MANAGER_DESTROY = "destroy";
+const char* const PLUGIN_EXTENSION = ".so";
+
+/**
+ * This is the template class for Plugin manager.
+ *
+ * The DrmManager uses this class to handle the plugins.
+ *
+ */
+template<typename Type>
+class TPlugInManager {
+private:
+    typedef void*      HANDLE;
+    typedef Type*      create_t(void);
+    typedef void       destroy_t(Type*);
+    typedef create_t*  FPCREATE;
+    typedef destroy_t* FPDESTORY;
+
+    typedef struct _PlugInContainer {
+        String8   sPath;
+        HANDLE    hHandle;
+        FPCREATE  fpCreate;
+        FPDESTORY fpDestory;
+        Type*     pInstance;
+
+        _PlugInContainer():
+            sPath("")
+            ,hHandle(NULL)
+            ,fpCreate(NULL)
+            ,fpDestory(NULL)
+            ,pInstance(NULL)
+            {}
+    } PlugInContainer;
+
+    typedef KeyedVector<String8, PlugInContainer*> PlugInMap;
+    PlugInMap m_plugInMap;
+
+    typedef Vector<String8> PlugInIdList;
+    PlugInIdList m_plugInIdList;
+
+public:
+    /**
+     * Load all the plug-ins in the specified directory
+     *
+     * @param[in] rsPlugInDirPath
+     *     Directory path which plug-ins (dynamic library) are stored
+     * @note Plug-ins should be implemented according to the specification
+     */
+    void loadPlugIns(const String8& rsPlugInDirPath) {
+        Vector<String8> plugInFileList = getPlugInPathList(rsPlugInDirPath);
+
+        if (!plugInFileList.isEmpty()) {
+            for (unsigned int i = 0; i < plugInFileList.size(); ++i) {
+                loadPlugIn(plugInFileList[i]);
+            }
+        }
+    }
+
+    /**
+     * Unload all the plug-ins
+     *
+     */
+    void unloadPlugIns() {
+        for (unsigned int i = 0; i < m_plugInIdList.size(); ++i) {
+            unloadPlugIn(m_plugInIdList[i]);
+        }
+        m_plugInIdList.clear();
+    }
+
+    /**
+     * Get all the IDs of available plug-ins
+     *
+     * @return[in] plugInIdList
+     *     String type Vector in which all plug-in IDs are stored
+     */
+    Vector<String8> getPlugInIdList() const {
+        return m_plugInIdList;
+    }
+
+    /**
+     * Get a plug-in reference of specified ID
+     *
+     * @param[in] rsPlugInId
+     *     Plug-in ID to be used
+     * @return plugIn
+     *     Reference of specified plug-in instance
+     */
+    Type& getPlugIn(const String8& rsPlugInId) {
+        if (!contains(rsPlugInId)) {
+            // This error case never happens
+        }
+        return *(m_plugInMap.valueFor(rsPlugInId)->pInstance);
+    }
+
+public:
+    /**
+     * Load a plug-in stored in the specified path
+     *
+     * @param[in] rsPlugInPath
+     *     Plug-in (dynamic library) file path
+     * @note Plug-in should be implemented according to the specification
+     */
+    void loadPlugIn(const String8& rsPlugInPath) {
+        if (contains(rsPlugInPath)) {
+            return;
+        }
+
+        PlugInContainer* pPlugInContainer = new PlugInContainer();
+
+        pPlugInContainer->hHandle = dlopen(rsPlugInPath.string(), RTLD_LAZY);
+
+        if (NULL == pPlugInContainer->hHandle) {
+            delete pPlugInContainer;
+            pPlugInContainer = NULL;
+            return;
+        }
+
+        pPlugInContainer->sPath = rsPlugInPath;
+        pPlugInContainer->fpCreate
+                = (FPCREATE)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_CREATE);
+        pPlugInContainer->fpDestory
+                = (FPDESTORY)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_DESTROY);
+
+        if (NULL != pPlugInContainer->fpCreate && NULL != pPlugInContainer->fpDestory) {
+            pPlugInContainer->pInstance = (Type*)pPlugInContainer->fpCreate();
+            m_plugInIdList.add(rsPlugInPath);
+            m_plugInMap.add(rsPlugInPath, pPlugInContainer);
+        } else {
+            dlclose(pPlugInContainer->hHandle);
+            delete pPlugInContainer;
+            pPlugInContainer = NULL;
+            return;
+        }
+    }
+
+    /**
+     * Unload a plug-in stored in the specified path
+     *
+     * @param[in] rsPlugInPath
+     *     Plug-in (dynamic library) file path
+     */
+    void unloadPlugIn(const String8& rsPlugInPath) {
+        if (!contains(rsPlugInPath)) {
+            return;
+        }
+
+        PlugInContainer* pPlugInContainer = m_plugInMap.valueFor(rsPlugInPath);
+        pPlugInContainer->fpDestory(pPlugInContainer->pInstance);
+        dlclose(pPlugInContainer->hHandle);
+
+        m_plugInMap.removeItem(rsPlugInPath);
+        delete pPlugInContainer;
+        pPlugInContainer = NULL;
+    }
+
+private:
+    /**
+     * True if TPlugInManager contains rsPlugInId
+     */
+    bool contains(const String8& rsPlugInId) {
+        return m_plugInMap.indexOfKey(rsPlugInId) != NAME_NOT_FOUND;
+    }
+
+    /**
+     * Return file path list of plug-ins stored in the specified directory
+     *
+     * @param[in] rsDirPath
+     *     Directory path in which plug-ins are stored
+     * @return plugInFileList
+     *     String type Vector in which file path of plug-ins are stored
+     */
+    Vector<String8> getPlugInPathList(const String8& rsDirPath) {
+        Vector<String8> fileList;
+        DIR* pDir = opendir(rsDirPath.string());
+        struct dirent* pEntry = new dirent();
+
+        while (NULL != pDir && NULL != (pEntry = readdir(pDir))) {
+            if (!isPlugIn(pEntry)) {
+                continue;
+            }
+            String8 plugInPath;
+            plugInPath += rsDirPath;
+            plugInPath += "/";
+            plugInPath += pEntry->d_name;
+
+            fileList.add(plugInPath);
+        }
+
+        if (NULL != pDir) {
+            closedir(pDir);
+        }
+        delete pEntry;
+        pEntry = NULL;
+
+        return fileList;
+    }
+
+    /**
+     * True if the input name denotes plug-in
+     */
+    bool isPlugIn(const struct dirent* pEntry) const {
+        String8 sName(pEntry->d_name);
+        int extentionPos = sName.size() - String8(PLUGIN_EXTENSION).size();
+        if (extentionPos < 0) {
+            return false;
+        }
+        return extentionPos == (int)sName.find(PLUGIN_EXTENSION);
+    }
+
+    /**
+     * True if the input entry is "." or ".."
+     */
+    bool isDotOrDDot(const struct dirent* pEntry) const {
+        String8 sName(pEntry->d_name);
+        return "." == sName || ".." == sName;
+    }
+
+    /**
+     * True if input entry is directory
+     */
+    bool isDirectory(const struct dirent* pEntry) const {
+        return DT_DIR == pEntry->d_type;
+    }
+
+    /**
+     * True if input entry is regular file
+     */
+    bool isRegularFile(const struct dirent* pEntry) const {
+        return DT_REG == pEntry->d_type;
+    }
+
+    /**
+     * True if input entry is link
+     */
+    bool isLink(const struct dirent* pEntry) const {
+        return DT_LNK == pEntry->d_type;
+    }
+};
+
+};
+
+#endif /* __PLUGIN_MANAGER_H__ */
+
diff --git a/drm/libdrmframework/include/ReadWriteUtils.h b/drm/libdrmframework/include/ReadWriteUtils.h
new file mode 100644
index 0000000..529b342
--- /dev/null
+++ b/drm/libdrmframework/include/ReadWriteUtils.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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 __READ_WRITE_UTILS_H__
+#define __READ_WRITE_UTILS_H__
+
+#include <utils/FileMap.h>
+#include <drm/drm_framework_common.h>
+
+namespace android {
+
+/**
+ * This is an utility class which performs IO operations.
+ *
+ */
+class ReadWriteUtils {
+public:
+    /**
+     * Constructor for ReadWriteUtils
+     */
+    ReadWriteUtils() {}
+
+    /**
+     * Destructor for ReadWriteUtils
+     */
+    virtual ~ReadWriteUtils();
+
+public:
+    /**
+     * Reads the data from the file path provided
+     *
+     * @param[in] filePath Path of the file
+     * @return Data read from the file
+     */
+    static String8 readBytes(const String8& filePath);
+    /**
+     * Reads the data into the given buffer from the file path provided
+     *
+     * @param[in] filePath Path of the file
+     * @param[out] buffer Data read from the file
+     * @return Length of the data read from the file
+     */
+    static int readBytes(const String8& filePath, char** buffer);
+    /**
+     * Writes the data into the file path provided
+     *
+     * @param[in] filePath Path of the file
+     * @param[in] dataBuffer Data to write
+     */
+    static void writeToFile(const String8& filePath, const String8& data);
+    /**
+     * Appends the data into the file path provided
+     *
+     * @param[in] filePath Path of the file
+     * @param[in] dataBuffer Data to append
+     */
+    static void appendToFile(const String8& filePath, const String8& data);
+
+private:
+    FileMap* mFileMap;
+};
+
+};
+
+#endif /* __READ_WRITE_UTILS_H__ */
+
diff --git a/drm/libdrmframework/include/StringTokenizer.h b/drm/libdrmframework/include/StringTokenizer.h
new file mode 100644
index 0000000..70e7558
--- /dev/null
+++ b/drm/libdrmframework/include/StringTokenizer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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 __STRING_TOKENIZER_H__
+#define __STRING_TOKENIZER_H__
+
+#include <drm/drm_framework_common.h>
+
+namespace android {
+
+/**
+ * This is an utility class for String manipulation.
+ *
+ */
+class StringTokenizer {
+public:
+    /**
+     * Iterator for string tokens
+     */
+    class Iterator {
+        friend class StringTokenizer;
+    private:
+        Iterator(StringTokenizer* StringTokenizer)
+         : mStringTokenizer(StringTokenizer), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        String8& next();
+
+    private:
+        StringTokenizer* mStringTokenizer;
+        unsigned int mIndex;
+    };
+
+public:
+    /**
+     * Constructor for StringTokenizer
+     *
+     * @param[in] string Complete string data
+     * @param[in] delimeter Delimeter used to split the string
+     */
+    StringTokenizer(const String8& string, const String8& delimeter);
+
+    /**
+     * Destructor for StringTokenizer
+     */
+    ~StringTokenizer() {}
+
+private:
+    /**
+     * Splits the string according to the delimeter
+     */
+    void splitString(const String8& string, const String8& delimeter);
+
+public:
+    /**
+     * Returns Iterator object to walk through the split string values
+     *
+     * @return Iterator object
+     */
+    Iterator iterator();
+
+private:
+    Vector<String8> mStringTokenizerVector;
+};
+
+};
+#endif /* __STRING_TOKENIZER_H__ */
+
diff --git a/drm/libdrmframework/plugins/Android.mk b/drm/libdrmframework/plugins/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/common/Android.mk b/drm/libdrmframework/plugins/common/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/common/include/DrmEngineBase.h b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
new file mode 100644
index 0000000..67b6355
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/include/DrmEngineBase.h
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2010 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_ENGINE_BASE_H__
+#define __DRM_ENGINE_BASE_H__
+
+#include <drm/drm_framework_common.h>
+#include "IDrmEngine.h"
+
+namespace android {
+
+/**
+ * This class is an interface for plug-in developers
+ *
+ * Responsibility of this class is control the sequence of actual plug-in.
+ * All each plug-in developer has to do is implement onXXX() type virtual interfaces.
+ */
+class DrmEngineBase : public IDrmEngine {
+public:
+    DrmEngineBase();
+    virtual ~DrmEngineBase();
+
+public:
+    DrmConstraints* getConstraints(int uniqueId, const String8* path, int action);
+
+    DrmMetadata* getMetadata(int uniqueId, const String8* path);
+
+    status_t initialize(int uniqueId);
+
+    status_t setOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+    status_t terminate(int uniqueId);
+
+    bool canHandle(int uniqueId, const String8& path);
+
+    DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+    status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath);
+
+    DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+    String8 getOriginalMimeType(int uniqueId, const String8& path);
+
+    int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+    int checkRightsStatus(int uniqueId, const String8& path, int action);
+
+    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+    status_t setPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    bool validateAction(
+            int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+    status_t removeRights(int uniqueId, const String8& path);
+
+    status_t removeAllRights(int uniqueId);
+
+    status_t openConvertSession(int uniqueId, int convertId);
+
+    DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+    DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+
+    DrmSupportInfo* getSupportInfo(int uniqueId);
+
+    status_t openDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+
+    status_t openDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, const char* uri);
+
+    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo);
+
+    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset);
+
+protected:
+    /////////////////////////////////////////////////////
+    // Interface for plug-in developers                //
+    // each plug-in has to implement following method  //
+    /////////////////////////////////////////////////////
+    /**
+     * Get constraint information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Actions defined such as,
+     *     Action::DEFAULT, Action::PLAY, etc
+     * @return DrmConstraints
+     *     key-value pairs of constraint are embedded in it
+     * @note
+     *     In case of error, return NULL
+     */
+    virtual DrmConstraints* onGetConstraints(
+            int uniqueId, const String8* path, int action) = 0;
+
+    /**
+     * Get metadata information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata
+     * @note
+     *     In case of error, return NULL
+     */
+    virtual DrmMetadata* onGetMetadata(int uniqueId, const String8* path) = 0;
+
+    /**
+     * Initialize plug-in
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onInitialize(int uniqueId) = 0;
+
+    /**
+     * Register a callback to be invoked when the caller required to
+     * receive necessary information
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] infoListener Listener
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onSetOnInfoListener(
+            int uniqueId, const IDrmEngine::OnInfoListener* infoListener) = 0;
+
+    /**
+     * Terminate the plug-in
+     * and release resource bound to plug-in
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onTerminate(int uniqueId) = 0;
+
+    /**
+     * Get whether the given content can be handled by this plugin or not
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path the protected object
+     * @return bool
+     *     Returns true if this plugin can handle , false in case of not able to handle
+     */
+    virtual bool onCanHandle(int uniqueId, const String8& path) = 0;
+
+    /**
+     * Executes given drm information based on its type
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmInfo Information needs to be processed
+     * @return DrmInfoStatus
+     *     instance as a result of processing given input
+     */
+    virtual DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
+
+    /**
+     * Save DRM rights to specified rights path
+     * and make association with content path
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmRights DrmRights to be saved
+     * @param[in] rightsPath File path where rights to be saved
+     * @param[in] contentPath File path where content was saved
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onSaveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightspath, const String8& contentPath) = 0;
+
+    /**
+     * Retrieves necessary information for registration, unregistration or rights
+     * acquisition information.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmInfoRequest Request information to retrieve drmInfo
+     * @return DrmInfo
+     *     instance as a result of processing given input
+     */
+    virtual DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInforequest) = 0;
+
+    /**
+     * Retrieves the mime type embedded inside the original content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return String8
+     *     Returns mime-type of the original content, such as "video/mpeg"
+     */
+    virtual String8 onGetOriginalMimeType(int uniqueId, const String8& path) = 0;
+
+    /**
+     * Retrieves the type of the protected object (content, rights, etc..)
+     * using specified path or mimetype. At least one parameter should be non null
+     * to retrieve DRM object type
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the content or null.
+     * @param[in] mimeType Mime type of the content or null.
+     * @return type of the DRM content,
+     *     such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+     */
+    virtual int onGetDrmObjectType(
+            int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+    /**
+     * Check whether the given content has valid rights or not
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+     * @return the status of the rights for the protected content,
+     *     such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+     */
+    virtual int onCheckRightsStatus(int uniqueId, const String8& path, int action) = 0;
+
+    /**
+     * Consumes the rights for a content.
+     * If the reserve parameter is true the rights is reserved until the same
+     * application calls this api again with the reserve parameter set to false.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+     * @param[in] reserve True if the rights should be reserved.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle,
+            int action, bool reserve) = 0;
+
+    /**
+     * Informs the DRM Engine about the playback actions performed on the DRM files.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+     * @param[in] position Position in the file (in milliseconds) where the start occurs.
+     *     Only valid together with Playback::START.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onSetPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position) = 0;
+
+    /**
+     * Validates whether an action on the DRM content is allowed or not.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+     * @param[in] description Detailed description of the action
+     * @return true if the action is allowed.
+     */
+    virtual bool onValidateAction(int uniqueId, const String8& path,
+            int action, const ActionDescription& description) = 0;
+
+    /**
+     * Removes the rights associated with the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onRemoveRights(int uniqueId, const String8& path) = 0;
+
+    /**
+     * Removes all the rights information of each plug-in associated with
+     * DRM framework. Will be used in master reset
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onRemoveAllRights(int uniqueId) = 0;
+
+    /**
+     * This API is for Forward Lock based DRM scheme.
+     * Each time the application tries to download a new DRM file
+     * which needs to be converted, then the application has to
+     * begin with calling this API.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onOpenConvertSession(int uniqueId, int convertId) = 0;
+
+    /**
+     * Accepts and converts the input data which is part of DRM file.
+     * The resultant converted data and the status is returned in the DrmConvertedInfo
+     * object. This method will be called each time there are new block
+     * of data received by the application.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @param[in] inputData Input Data which need to be converted
+     * @return Return object contains the status of the data conversion,
+     *     the output converted data and offset. In this case the
+     *     application will ignore the offset information.
+     */
+    virtual DrmConvertedStatus* onConvertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) = 0;
+
+    /**
+     * Informs the Drm Agent when there is no more data which need to be converted
+     * or when an error occurs. Upon successful conversion of the complete data,
+     * the agent will inform that where the header and body signature
+     * should be added. This signature appending is needed to integrity
+     * protect the converted file.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @return Return object contains the status of the data conversion,
+     *     the header and body signature data. It also informs
+     *     the application on which offset these signature data
+     *     should be appended.
+     */
+    virtual DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId) = 0;
+
+    /**
+     * Returns the information about the Drm Engine capabilities which includes
+     * supported MimeTypes and file suffixes.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return DrmSupportInfo
+     *     instance which holds the capabilities of a plug-in
+     */
+    virtual DrmSupportInfo* onGetSupportInfo(int uniqueId) = 0;
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the current decryption session
+     * @param[in] fd File descriptor of the protected content to be decrypted
+     * @param[in] offset Start position of the content
+     * @param[in] length The length of the protected content
+     * @return
+     *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+     */
+    virtual status_t onOpenDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the current decryption session
+     * @param[in] uri Path of the protected content to be decrypted
+     * @return
+     *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+     */
+    virtual status_t onOpenDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, const char* uri) = 0;
+
+    /**
+     * Close the decrypt session for the given handle
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+
+    /**
+     * Initialize decryption for the given unit of the protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptId Handle for the decryption session
+     * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+     * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) = 0;
+
+    /**
+     * Decrypt the protected content buffers for the given unit
+     * This method will be called any number of times, based on number of
+     * encrypted streams received from application.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptId Handle for the decryption session
+     * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+     * @param[in] encBuffer Encrypted data block
+     * @param[out] decBuffer Decrypted data block
+     * @param[in] IV Optional buffer
+     * @return status_t
+     *     Returns the error code for this API
+     *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+     *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+     *     DRM_ERROR_DECRYPT for failure.
+     */
+    virtual status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
+
+    /**
+     * Finalize decryption for the given unit of the protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t onFinalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+
+    /**
+     * Reads the specified number of bytes from an open DRM file.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[out] buffer Reference to the buffer that should receive the read data.
+     * @param[in] numBytes Number of bytes to read.
+     * @param[in] offset Offset with which to update the file position.
+     *
+     * @return Number of bytes read. Returns -1 for Failure.
+     */
+    virtual ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset) = 0;
+};
+
+};
+
+#endif /* __DRM_ENGINE_BASE_H__ */
+
diff --git a/drm/libdrmframework/plugins/common/include/IDrmEngine.h b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
new file mode 100644
index 0000000..f839070
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/include/IDrmEngine.h
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2010 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 __IDRM_ENGINE_H__
+#define __IDRM_ENGINE_H__
+
+#include <drm/drm_framework_common.h>
+
+namespace android {
+
+class DrmContentIds;
+class DrmConstraints;
+class DrmMetadata;
+class DrmRights;
+class DrmInfo;
+class DrmInfoStatus;
+class DrmConvertedStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class DrmInfoEvent;
+
+/**
+ * This class is an interface for plug-in user
+ *
+ * Responsibility of this class is provide generic interface to DRM Engine Manager.
+ * Each interface need to be as abstract as possible.
+ */
+class IDrmEngine {
+public:
+    virtual ~IDrmEngine() {
+    }
+
+public:
+    class OnInfoListener {
+
+    public:
+        virtual void onInfo(const DrmInfoEvent& event) = 0;
+
+        virtual ~OnInfoListener() { }
+    };
+
+public:
+
+    //////////////////////////////////
+    // Implementation of IDrmEngine //
+    //////////////////////////////////
+
+    /**
+     * Initialize plug-in
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t initialize(int uniqueId) = 0;
+
+    /**
+     * Register a callback to be invoked when the caller required to
+     * receive necessary information
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] infoListener Listener
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t setOnInfoListener(
+            int uniqueId, const IDrmEngine::OnInfoListener* infoListener) = 0;
+
+    /**
+     * Terminate the plug-in
+     * and release resource bound to plug-in
+     * e.g.) release native resource
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t terminate(int uniqueId) = 0;
+
+    /**
+     * Get constraint information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Actions defined such as,
+     *     Action::DEFAULT, Action::PLAY, etc
+     * @return DrmConstraints
+     *     key-value pairs of constraint are embedded in it
+     * @note
+     *     In case of error, return NULL
+     */
+    virtual DrmConstraints* getConstraints(
+            int uniqueId, const String8* path, int action) = 0;
+
+    /**
+     * Get metadata information associated with input content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata
+     * @note
+     *      In case of error, return NULL
+     */
+    virtual DrmMetadata* getMetadata(int uniqueId, const String8* path) = 0;
+
+    /**
+     * Get whether the given content can be handled by this plugin or not
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path the protected object
+     * @return bool
+     *     true if this plugin can handle , false in case of not able to handle
+     */
+    virtual bool canHandle(int uniqueId, const String8& path) = 0;
+
+    /**
+     * Executes given drm information based on its type
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmInfo Information needs to be processed
+     * @return DrmInfoStatus
+     *     instance as a result of processing given input
+     */
+    virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo) = 0;
+
+    /**
+     * Retrieves necessary information for registration, unregistration or rights
+     * acquisition information.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmInfoRequest Request information to retrieve drmInfo
+     * @return DrmInfo
+     *     instance as a result of processing given input
+     */
+    virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) = 0;
+
+    /**
+     * Save DRM rights to specified rights path
+     * and make association with content path
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] drmRights DrmRights to be saved
+     * @param[in] rightsPath File path where rights to be saved
+     * @param[in] contentPath File path where content was saved
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) = 0;
+
+    /**
+     * Retrieves the mime type embedded inside the original content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return String8
+     *     Returns mime-type of the original content, such as "video/mpeg"
+     */
+    virtual String8 getOriginalMimeType(int uniqueId, const String8& path) = 0;
+
+    /**
+     * Retrieves the type of the protected object (content, rights, etc..)
+     * using specified path or mimetype. At least one parameter should be non null
+     * to retrieve DRM object type
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the content or null.
+     * @param[in] mimeType Mime type of the content or null.
+     * @return type of the DRM content,
+     *     such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+     */
+    virtual int getDrmObjectType(
+            int uniqueId, const String8& path, const String8& mimeType) = 0;
+
+    /**
+     * Check whether the given content has valid rights or not
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+     * @return the status of the rights for the protected content,
+     *     such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+     */
+    virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0;
+
+    /**
+     * Consumes the rights for a content.
+     * If the reserve parameter is true the rights is reserved until the same
+     * application calls this api again with the reserve parameter set to false.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+     * @param[in] reserve True if the rights should be reserved.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t consumeRights(
+            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
+
+    /**
+     * Informs the DRM Engine about the playback actions performed on the DRM files.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+     * @param[in] position Position in the file (in milliseconds) where the start occurs.
+     *     Only valid together with Playback::START.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t setPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+            int playbackStatus, int position) = 0;
+
+    /**
+     * Validates whether an action on the DRM content is allowed or not.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+     * @param[in] description Detailed description of the action
+     * @return true if the action is allowed.
+     */
+    virtual bool validateAction(int uniqueId, const String8& path,
+            int action, const ActionDescription& description) = 0;
+
+    /**
+     * Removes the rights associated with the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] path Path of the protected content
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t removeRights(int uniqueId, const String8& path) = 0;
+
+    /**
+     * Removes all the rights information of each plug-in associated with
+     * DRM framework. Will be used in master reset
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t removeAllRights(int uniqueId) = 0;
+
+    /**
+     * This API is for Forward Lock based DRM scheme.
+     * Each time the application tries to download a new DRM file
+     * which needs to be converted, then the application has to
+     * begin with calling this API.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t openConvertSession(int uniqueId, int convertId) = 0;
+
+    /**
+     * Accepts and converts the input data which is part of DRM file.
+     * The resultant converted data and the status is returned in the DrmConvertedInfo
+     * object. This method will be called each time there are new block
+     * of data received by the application.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @param[in] inputData Input Data which need to be converted
+     * @return Return object contains the status of the data conversion,
+     *     the output converted data and offset. In this case the
+     *     application will ignore the offset information.
+     */
+    virtual DrmConvertedStatus* convertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) = 0;
+
+    /**
+     * Informs the Drm Agent when there is no more data which need to be converted
+     * or when an error occurs. Upon successful conversion of the complete data,
+     * the agent will inform that where the header and body signature
+     * should be added. This signature appending is needed to integrity
+     * protect the converted file.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] convertId Handle for the convert session
+     * @return Return object contains the status of the data conversion,
+     *     the header and body signature data. It also informs
+     *     the application on which offset these signature data
+     *     should be appended.
+     */
+    virtual DrmConvertedStatus* closeConvertSession( int uniqueId, int convertId) = 0;
+
+    /**
+     * Returns the information about the Drm Engine capabilities which includes
+     * supported MimeTypes and file suffixes.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @return DrmSupportInfo
+     *     instance which holds the capabilities of a plug-in
+     */
+    virtual DrmSupportInfo* getSupportInfo(int uniqueId) = 0;
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the current decryption session
+     * @param[in] fd File descriptor of the protected content to be decrypted
+     * @param[in] offset Start position of the content
+     * @param[in] length The length of the protected content
+     * @return
+     *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+     */
+    virtual status_t openDecryptSession(
+        int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) = 0;
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the current decryption session
+     * @param[in] uri Path of the protected content to be decrypted
+     * @return
+     *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+     */
+    virtual status_t openDecryptSession(
+        int uniqueId, DecryptHandle* decryptHandle, const char* uri) = 0;
+
+    /**
+     * Close the decrypt session for the given handle
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+
+    /**
+     * Initialize decryption for the given unit of the protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) = 0;
+
+    /**
+     * Decrypt the protected content buffers for the given unit
+     * This method will be called any number of times, based on number of
+     * encrypted streams received from application.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @param[in] encBuffer Encrypted data block
+     * @param[out] decBuffer Decrypted data block
+     * @param[in] IV Optional buffer
+     * @return status_t
+     *     Returns the error code for this API
+     *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+     *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+     *     DRM_ERROR_DECRYPT for failure.
+     */
+    virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
+
+    /**
+     * Finalize decryption for the given unit of the protected content
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    virtual status_t finalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+
+    /**
+     * Reads the specified number of bytes from an open DRM file.
+     *
+     * @param[in] uniqueId Unique identifier for a session
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[out] buffer Reference to the buffer that should receive the read data.
+     * @param[in] numBytes Number of bytes to read.
+     * @param[in] offset Offset with which to update the file position.
+     *
+     * @return Number of bytes read. Returns -1 for Failure.
+     */
+    virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset) = 0;
+};
+
+};
+
+#endif /* __IDRM_ENGINE_H__ */
+
diff --git a/drm/libdrmframework/plugins/common/util/Android.mk b/drm/libdrmframework/plugins/common/util/Android.mk
new file mode 100644
index 0000000..15dda80
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2010 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 := \
+    src/MimeTypeUtil.cpp
+
+LOCAL_MODULE := libdrmutility
+
+LOCAL_SHARED_LIBRARIES :=  \
+    libutils \
+    libdl \
+    libdvm \
+    libandroid_runtime \
+    libnativehelper \
+    liblog
+
+
+base := frameworks/base
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE) \
+    $(base)/include \
+    $(base)/include/drm \
+    $(base)/include/drm/plugins \
+    $(LOCAL_PATH)/include
+
+
+ifneq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_C_INCLUDES += \
+    $(LOCAL_PATH)/tools
+
+endif
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
new file mode 100644
index 0000000..4d12a61
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/MimeTypeUtil.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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 __MIMETYPEUTIL_H__
+#define __MIMETYPEUTIL_H__
+
+#include <utils/String8.h>
+
+namespace android {
+
+class MimeTypeUtil {
+
+public:
+
+    MimeTypeUtil() {}
+
+    virtual ~MimeTypeUtil() {}
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+static String8 convertMimeType(String8& mimeType);
+
+};
+};
+
+#endif /* __MIMETYPEUTIL_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
new file mode 100644
index 0000000..3dff58c
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 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 __SESSIONMAP_H__
+#define __SESSIONMAP_H__
+
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+/**
+ * A wrapper template class for handling DRM Engine sessions.
+ */
+template <typename NODE>
+class SessionMap {
+
+public:
+    KeyedVector<int, NODE> map;
+
+    SessionMap() {}
+
+    virtual ~SessionMap() {
+        destroyMap();
+    }
+
+/**
+ * Adds a new value in the session map table. It expects memory to be allocated already
+ * for the session object
+ *
+ * @param key - key or Session ID
+ * @param value - session object to add
+ *
+ * @return boolean result of adding value. returns false if key is already exist.
+ */
+bool addValue(int key, NODE value) {
+    bool result = false;
+
+    if (!isCreated(key)) {
+        map.add(key, value);
+        result = true;
+    }
+
+    return result;
+}
+
+
+/**
+ * returns the session object by the key
+ *
+ * @param key - key or Session ID
+ *
+ * @return session object as per the key
+ */
+NODE getValue(int key) {
+    NODE value = NULL;
+
+    if (isCreated(key)) {
+        value = (NODE) map.valueFor(key);
+    }
+
+    return value;
+}
+
+/**
+ * returns the number of objects in the session map table
+ *
+ * @return count of number of session objects.
+ */
+int getSize() {
+    return map.size();
+}
+
+/**
+ * returns the session object by the index in the session map table
+ *
+ * @param index - index of the value required
+ *
+ * @return session object as per the index
+ */
+NODE getValueAt(unsigned int index) {
+    NODE value = NULL;
+
+    if (map.size() > index) {
+      value = map.valueAt(index);
+    }
+
+    return value;
+}
+
+/**
+ * deletes the object from session map. It also frees up memory for the session object.
+ *
+ * @param key - key of the value to be deleted
+ *
+ */
+void removeValue(int key) {
+    deleteValue(getValue(key));
+    map.removeItem(key);
+}
+
+/**
+ * decides if session is already created.
+ *
+ * @param key - key of the value for the session
+ *
+ * @return boolean result of whether session is created
+ */
+bool isCreated(int key) {
+    return (0 <= map.indexOfKey(key));
+}
+
+/**
+ * empty the entire session table. It releases all the memory for session objects.
+ */
+void destroyMap() {
+    int size = map.size();
+    int i = 0;
+
+    for (i = 0; i < size; i++) {
+        deleteValue(map.valueAt(i));
+    }
+
+    map.clear();
+}
+
+/**
+ * free up the memory for the session object.
+ * Make sure if any reference to the session object anywhere, otherwise it will be a
+ * dangle pointer after this call.
+ *
+ * @param value - session object to free
+ *
+ */
+void deleteValue(NODE value) {
+    delete value;
+}
+
+};
+
+};
+
+#endif /* __SESSIONMAP_H__ */
diff --git a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
new file mode 100644
index 0000000..4ee903e
--- /dev/null
+++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 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 <MimeTypeUtil.h>
+#include <utils/Log.h>
+
+namespace android {
+
+#undef LOG_TAG
+#define LOG_TAG "MimeTypeUtil"
+
+enum {
+    MIMETYPE_AUDIO       = 0,
+    MIMETYPE_APPLICATION = 1,
+    MIMETYPE_IMAGE       = 2,
+    MIMETYPE_VIDEO       = 3,
+    MIMETYPE_LAST        = -1,
+};
+
+struct MimeGroup{
+    int         type;     // Audio, video,.. use the enum values
+    const char* pGroup;   // "audio/", "video/",.. should contain the last "/"
+    int         size;     // Number of bytes. e.g. "audio/" = 6 bytes
+};
+
+struct MimeTypeList{
+    int         type;
+    const char* pMimeExt;  // Everything after the '/' e.g. audio/x-mpeg -> "x-mpeg"
+    int         size;      // Number of bytes. e.g. "x-mpeg" = 6 bytes
+    const char* pMimeType; // Mimetype that should be returned
+};
+
+
+// Known mimetypes by android
+static const char mime_type_audio_mpeg[]  = "audio/mpeg";
+static const char mime_type_audio_3gpp[]  = "audio/3gpp";
+static const char mime_type_audio_amr[]   = "audio/amr-wb";
+static const char mime_type_audio_aac[]   = "audio/mp4a-latm";
+static const char mime_type_audio_wav[]   = "audio/wav";
+
+static const char mime_type_video_mpeg4[] = "video/mpeg4";
+static const char mime_type_video_3gpp[]  = "video/3gpp";
+
+// Known mimetype groups
+static const char mime_group_audio[]       = "audio/";
+static const char mime_group_application[] = "application/";
+static const char mime_group_image[]       = "image/";
+static const char mime_group_video[]       = "video/";
+
+static struct MimeGroup mimeGroup[] = {
+    {MIMETYPE_AUDIO,       mime_group_audio,        sizeof(mime_group_audio)-1},
+    {MIMETYPE_APPLICATION, mime_group_application,  sizeof(mime_group_application)-1},
+    {MIMETYPE_IMAGE,       mime_group_image,        sizeof(mime_group_image)-1},
+    {MIMETYPE_VIDEO,       mime_group_video,        sizeof(mime_group_video)-1},
+    {MIMETYPE_LAST,        NULL,                    0} // Must be last entry
+};
+
+// List of all mimetypes that should be converted.
+static struct MimeTypeList mimeTypeList[] = {
+    // Mp3 mime types
+    {MIMETYPE_AUDIO, "mp3",          sizeof("mp3")-1,         mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mpeg",       sizeof("x-mpeg")-1,      mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mp3",        sizeof("x-mp3")-1,       mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "mpg",          sizeof("mpg")-1,         mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "mpg3",         sizeof("mpg")-1,         mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mpg",        sizeof("x-mpg")-1,       mime_type_audio_mpeg},
+    {MIMETYPE_AUDIO, "x-mpegaudio",  sizeof("x-mpegaudio")-1, mime_type_audio_mpeg},
+
+    // 3gpp audio mime types
+    {MIMETYPE_AUDIO, "3gp",          sizeof("3gp")-1,         mime_type_audio_3gpp},
+
+    // Amr audio mime types
+    {MIMETYPE_AUDIO, "amr",          sizeof("amr")-1,         mime_type_audio_amr},
+
+    // Aac audio mime types
+    {MIMETYPE_AUDIO, "aac",          sizeof("aac")-1,         mime_type_audio_aac},
+
+    // Wav audio mime types
+    {MIMETYPE_AUDIO, "x-wav",        sizeof("x-wav")-1,       mime_type_audio_wav},
+
+    // Mpeg4 video mime types
+    {MIMETYPE_VIDEO, "mpg4",         sizeof("mpg4")-1,        mime_type_video_mpeg4},
+    {MIMETYPE_VIDEO, "mp4v-es",      sizeof("mp4v-es")-1,     mime_type_video_mpeg4},
+
+    // 3gpp video mime types
+    {MIMETYPE_VIDEO, "3gp",          sizeof("3gp")-1,         mime_type_video_3gpp},
+
+    // Must be last entry
+    {MIMETYPE_LAST,  NULL,           0,                       NULL}
+};
+
+/**
+ * May convert the mimetype if there is a well known
+ * replacement mimetype otherwise the original mimetype
+ * is returned.
+ *
+ * @param mimeType - mimetype in lower case to convert.
+ *
+ * @return mimetype or null.
+ */
+String8 MimeTypeUtil::convertMimeType(String8& mimeType) {
+    String8 result = mimeType;
+    const char* pTmp;
+    const char* pMimeType;
+    struct MimeGroup* pGroup;
+    struct MimeTypeList* pMimeItem;
+    int len;
+
+    pMimeType = mimeType.string();
+    if (NULL != pMimeType) {
+        /* Check which group the mimetype is */
+        pGroup = mimeGroup;
+
+        while (MIMETYPE_LAST != pGroup->type) {
+            if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) {
+                break;
+            }
+            pGroup++;
+        }
+
+        /* Go through the mimetype list. Only check items of the correct group */
+        if (MIMETYPE_LAST != pGroup->type) {
+            pMimeItem = mimeTypeList;
+            len = strlen (pMimeType+pGroup->size);
+
+            while (MIMETYPE_LAST != pMimeItem->type) {
+                if ((len == pMimeItem->size) &&
+                    (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) {
+                    result = String8(pMimeItem->pMimeType);
+                    break;
+                }
+                pMimeItem++;
+            }
+        }
+        LOGI("convertMimeType got mimetype %s, converted into mimetype %s",
+             pMimeType, result.string());
+    }
+
+    return result;
+}
+};
diff --git a/drm/libdrmframework/plugins/forward-lock/Android.mk b/drm/libdrmframework/plugins/forward-lock/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
new file mode 100644
index 0000000..d4a6f18
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2010 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)
+
+base := frameworks/base
+
+# Determine whether the DRM framework uses 64-bit data types for file offsets and do the same.
+ifneq ($(shell grep -c 'off64_t offset' $(base)/drm/libdrmframework/plugins/common/include/IDrmEngine.h), 0)
+LOCAL_CFLAGS += -DUSE_64BIT_DRM_API
+endif
+
+LOCAL_SRC_FILES:= \
+    src/FwdLockEngine.cpp
+
+LOCAL_MODULE := libfwdlockengine
+
+LOCAL_SHARED_LIBRARIES := \
+    libicui18n \
+    libicuuc \
+    libutils \
+    libdl \
+    libandroid_runtime \
+    libnativehelper \
+    libcrypto \
+    libssl \
+    libdrmframework
+
+LOCAL_STATIC_LIBRARIES := \
+    libdrmutility \
+    libdrmframeworkcommon \
+    libfwdlock-common \
+    libfwdlock-converter \
+    libfwdlock-decoder
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE) \
+    $(base)/include/drm \
+    $(base)/drm/libdrmframework/plugins/common/include \
+    $(base)/drm/libdrmframework/plugins/common/util/include \
+    $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+    $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/converter \
+    $(base)/drm/libdrmframework/plugins/forward-lock/internal-format/decoder \
+    $(LOCAL_PATH)/include \
+    external/openssl/include
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
new file mode 100644
index 0000000..34804cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKENGINE_H__
+#define __FWDLOCKENGINE_H__
+
+#include <DrmEngineBase.h>
+#include <DrmConstraints.h>
+#include <DrmRights.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmConvertedStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmInfoEvent.h>
+
+#include "SessionMap.h"
+#include "FwdLockConv.h"
+
+namespace android {
+
+/**
+ * Forward Lock Engine class.
+ */
+class FwdLockEngine : public android::DrmEngineBase {
+
+public:
+    FwdLockEngine();
+    virtual ~FwdLockEngine();
+
+protected:
+/**
+ * Get constraint information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Actions defined such as,
+ *     Action::DEFAULT, Action::PLAY, etc
+ * @return DrmConstraints
+ *     key-value pairs of constraint are embedded in it
+ * @note
+ *     In case of error, return NULL
+ */
+DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+/**
+ * Get metadata information associated with input content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return DrmMetadata
+ *      For Forward Lock engine, it returns an empty object
+ * @note
+ *     In case of error, returns NULL
+ */
+DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+/**
+ * Initialize plug-in.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onInitialize(int uniqueId);
+
+/**
+ * Register a callback to be invoked when the caller required to
+ * receive necessary information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param infoListener Listener
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+/**
+ * Terminate the plug-in and release resources bound to it.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onTerminate(int uniqueId);
+
+/**
+ * Get whether the given content can be handled by this plugin or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path to the protected object
+ * @return bool
+ *      Returns true if this plugin can handle , false in case of not able to handle
+ */
+bool onCanHandle(int uniqueId, const String8& path);
+
+/**
+ * Processes the given DRM information as appropriate for its type.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfo Information that needs to be processed
+ * @return DrmInfoStatus
+ *      instance as a result of processing given input
+ */
+DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+/**
+ * Save DRM rights to specified rights path
+ * and make association with content path.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmRights DrmRights to be saved
+ * @param rightsPath File path where rights to be saved
+ * @param contentPath File path where content was saved
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onSaveRights(int uniqueId,
+                      const DrmRights& drmRights,
+                      const String8& rightsPath,
+                      const String8& contentPath);
+
+/**
+ * Retrieves necessary information for registration, unregistration or rights
+ * acquisition information.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param drmInfoRequest Request information to retrieve drmInfo
+ * @return DrmInfo
+ *      instance as a result of processing given input
+ */
+DrmInfo* onAcquireDrmInfo(int uniqueId,
+                          const DrmInfoRequest* drmInfoRequest);
+
+/**
+ * Retrieves the mime type embedded inside the original content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return String8
+ *       Returns mime-type of the original content, such as "video/mpeg"
+ */
+String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+/**
+ * Retrieves the type of the protected object (content, rights, etc..)
+ * using specified path or mimetype. At least one parameter should be non null
+ * to retrieve DRM object type.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the content or null.
+ * @param mimeType Mime type of the content or null.
+ * @return type of the DRM content,
+ *     such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+ */
+int onGetDrmObjectType(int uniqueId,
+                       const String8& path,
+                       const String8& mimeType);
+
+/**
+ * Check whether the given content has valid rights or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to perform (Action::DEFAULT, Action::PLAY, etc)
+ * @return the status of the rights for the protected content,
+ *     such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+ */
+int onCheckRightsStatus(int uniqueId,
+                        const String8& path,
+                        int action);
+
+/**
+ * Consumes the rights for a content.
+ * If the reserve parameter is true the rights are reserved until the same
+ * application calls this api again with the reserve parameter set to false.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+ * @param reserve True if the rights should be reserved.
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onConsumeRights(int uniqueId,
+                         DecryptHandle* decryptHandle,
+                         int action,
+                         bool reserve);
+
+/**
+ * Informs the DRM Engine about the playback actions performed on the DRM files.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+ * @param position Position in the file (in milliseconds) where the start occurs.
+ *     Only valid together with Playback::START.
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onSetPlaybackStatus(int uniqueId,
+                             DecryptHandle* decryptHandle,
+                             int playbackStatus,
+                             int64_t position);
+#else
+status_t onSetPlaybackStatus(int uniqueId,
+                             DecryptHandle* decryptHandle,
+                             int playbackStatus,
+                             int position);
+#endif
+
+/**
+ *  Validates whether an action on the DRM content is allowed or not.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @param action Action to validate (Action::PLAY, Action::TRANSFER, etc)
+ * @param description Detailed description of the action
+ * @return true if the action is allowed.
+ */
+bool onValidateAction(int uniqueId,
+                      const String8& path,
+                      int action,
+                      const ActionDescription& description);
+
+/**
+ * Removes the rights associated with the given protected content.
+ * Not used for Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param path Path of the protected content
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveRights(int uniqueId, const String8& path);
+
+/**
+ * Removes all the rights information of each plug-in associated with
+ * DRM framework. Will be used in master reset but does nothing for
+ * Forward Lock Engine.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onRemoveAllRights(int uniqueId);
+
+/**
+ * Starts the Forward Lock file conversion session.
+ * Each time the application tries to download a new DRM file
+ * which needs to be converted, then the application has to
+ * begin with calling this API. The convertId is used as the conversion session key
+ * and must not be the same for different convert sessions.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onOpenConvertSession(int uniqueId, int convertId);
+
+/**
+ * Accepts and converts the input data which is part of DRM file.
+ * The resultant converted data and the status is returned in the DrmConvertedInfo
+ * object. This method will be called each time there is a new block
+ * of data received by the application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @param inputData Input Data which need to be converted
+ * @return Return object contains the status of the data conversion,
+ *       the output converted data and offset. In this case the
+ *      application will ignore the offset information.
+ */
+DrmConvertedStatus* onConvertData(int uniqueId,
+                                  int convertId,
+                                  const DrmBuffer* inputData);
+
+/**
+ * Closes the convert session in case of data supply completed or error occurred.
+ * Upon successful conversion of the complete data, it returns signature calculated over
+ * the entire data used over a conversion session. This signature must be copied to the offset
+ * mentioned in the DrmConvertedStatus. Signature is used for data integrity protection.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param convertId Handle for the convert session
+ * @return Return object contains the status of the data conversion,
+ *      the header and body signature data. It also informs
+ *      the application about the file offset at which this
+ *      signature data should be written.
+ */
+DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+/**
+ * Returns the information about the Drm Engine capabilities which includes
+ * supported MimeTypes and file suffixes.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @return DrmSupportInfo
+ *      instance which holds the capabilities of a plug-in
+ */
+DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param fd File descriptor of the protected content to be decrypted
+ * @param offset Start position of the content
+ * @param length The length of the protected content
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+#ifdef USE_64BIT_DRM_API
+status_t onOpenDecryptSession(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              int fd, off64_t offset, off64_t length);
+#else
+status_t onOpenDecryptSession(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              int fd, int offset, int length);
+#endif
+
+/**
+ * Open the decrypt session to decrypt the given protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the current decryption session
+ * @param uri Path of the protected content to be decrypted
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onOpenDecryptSession(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              const char* uri);
+
+/**
+ * Close the decrypt session for the given handle.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @return status_t
+ *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+ */
+status_t onCloseDecryptSession(int uniqueId,
+                               DecryptHandle* decryptHandle);
+
+/**
+ * Initialize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param headerInfo Information for initializing decryption of this decrypUnit
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onInitializeDecryptUnit(int uniqueId,
+                                 DecryptHandle* decryptHandle,
+                                 int decryptUnitId,
+                                 const DrmBuffer* headerInfo);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID which specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @return status_t
+ *     Returns the error code for this API
+ *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ *     DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId,
+                   DecryptHandle* decryptHandle,
+                   int decryptUnitId,
+                   const DrmBuffer* encBuffer,
+                   DrmBuffer** decBuffer);
+
+/**
+ * Decrypt the protected content buffers for the given unit.
+ * This method will be called any number of times, based on number of
+ * encrypted streams received from application.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptId Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @param encBuffer Encrypted data block
+ * @param decBuffer Decrypted data block
+ * @param IV Optional buffer
+ * @return status_t
+ *     Returns the error code for this API
+ *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+ *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+ *     DRM_ERROR_DECRYPT for failure.
+ */
+status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+                   int decryptUnitId, const DrmBuffer* encBuffer,
+                   DrmBuffer** decBuffer, DrmBuffer* IV);
+
+/**
+ * Finalize decryption for the given unit of the protected content.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param decryptUnitId ID Specifies decryption unit, such as track ID
+ * @return
+ *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
+ */
+status_t onFinalizeDecryptUnit(int uniqueId,
+                               DecryptHandle* decryptHandle,
+                               int decryptUnitId);
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ *
+ * @return Number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t onRead(int uniqueId,
+               DecryptHandle* decryptHandle,
+               void* pBuffer,
+               int numBytes);
+
+/**
+ * Updates the file position within an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param offset Offset with which to update the file position.
+ * @param whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *           These constants are defined in unistd.h.
+ *
+ * @return New file position.
+ * @retval ((off_t)-1) Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+off64_t onLseek(int uniqueId,
+                DecryptHandle* decryptHandle,
+                off64_t offset,
+                int whence);
+#else
+off_t onLseek(int uniqueId,
+              DecryptHandle* decryptHandle,
+              off_t offset,
+              int whence);
+#endif
+
+/**
+ * Reads the specified number of bytes from an open DRM file.
+ *
+ * @param uniqueId Unique identifier for a session
+ * @param decryptHandle Handle for the decryption session
+ * @param buffer Reference to the buffer that should receive the read data.
+ * @param numBytes Number of bytes to read.
+ * @param offset Offset with which to update the file position.
+ *
+ * @return Number of bytes read. Returns -1 for Failure.
+ */
+#ifdef USE_64BIT_DRM_API
+ssize_t onPread(int uniqueId,
+                DecryptHandle* decryptHandle,
+                void* buffer,
+                ssize_t numBytes,
+                off64_t offset);
+#else
+ssize_t onPread(int uniqueId,
+                DecryptHandle* decryptHandle,
+                void* buffer,
+                ssize_t numBytes,
+                off_t offset);
+#endif
+
+private:
+
+/**
+ * Session Class for Forward Lock Conversion. An object of this class is created
+ * for every conversion.
+ */
+class ConvertSession {
+    public :
+        int uniqueId;
+        FwdLockConv_Output_t output;
+
+        ConvertSession() {
+            uniqueId = 0;
+            memset(&output, 0, sizeof(FwdLockConv_Output_t));
+        }
+
+        virtual ~ConvertSession() {}
+};
+
+/**
+ * Session Class for Forward Lock decoder. An object of this class is created
+ * for every decoding session.
+ */
+class DecodeSession {
+    public :
+        int fileDesc;
+        off_t offset;
+
+        DecodeSession() {
+            fileDesc = -1;
+            offset = 0;
+        }
+
+        DecodeSession(int fd) {
+            fileDesc = fd;
+            offset = 0;
+        }
+
+        virtual ~DecodeSession() {}
+};
+
+/**
+ * Session Map Tables for Conversion and Decoding of forward lock files.
+ */
+SessionMap<ConvertSession*> convertSessionMap;
+SessionMap<DecodeSession*> decodeSessionMap;
+
+/**
+ * Converts the error code from Forward Lock Converter to DrmConvertStatus error code.
+ *
+ * @param Forward Lock Converter error code
+ *
+ * @return Status code from DrmConvertStatus.
+ */
+static int getConvertedStatus(FwdLockConv_Status_t status);
+};
+
+};
+
+#endif /* __FWDLOCKENGINE_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
new file mode 100644
index 0000000..da95d60
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngineConst.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKENGINECONST_H__
+#define __FWDLOCKENGINECONST_H__
+
+namespace android {
+
+/**
+ * Constants for forward Lock Engine used for exposing engine's capabilities.
+ */
+#define FWDLOCK_EXTENSION_FL           ("FL")
+#define FWDLOCK_DOTEXTENSION_FL        (".fl")
+#define FWDLOCK_MIMETYPE_FL            ("application/x-android-drm-fl")
+
+#define FWDLOCK_EXTENSION_DM           ("DM")
+#define FWDLOCK_DOTEXTENSION_DM        (".dm")
+#define FWDLOCK_MIMETYPE_DM            ("application/vnd.oma.drm.message")
+
+#define FWDLOCK_DESCRIPTION            ("OMA V1 Forward Lock")
+
+};
+
+#endif /* __FWDLOCKENGINECONST_H__ */
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
new file mode 100644
index 0000000..d430f72
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2010 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 "SessionMap.h"
+#include "FwdLockEngine.h"
+#include <utils/Log.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "drm_framework_common.h"
+#include <fcntl.h>
+#include <limits.h>
+#include <DrmRights.h>
+#include <DrmConstraints.h>
+#include <DrmMetadata.h>
+#include <DrmInfo.h>
+#include <DrmInfoStatus.h>
+#include <DrmInfoRequest.h>
+#include <DrmSupportInfo.h>
+#include <DrmConvertedStatus.h>
+#include <utils/String8.h>
+#include "FwdLockConv.h"
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+#include "FwdLockEngineConst.h"
+#include "MimeTypeUtil.h"
+
+#undef LOG_TAG
+#define LOG_TAG "FwdLockEngine"
+
+using namespace android;
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+    return new FwdLockEngine();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* plugIn) {
+    delete plugIn;
+}
+
+FwdLockEngine::FwdLockEngine() {
+    LOGD("FwdLockEngine Construction");
+}
+
+FwdLockEngine::~FwdLockEngine() {
+    LOGD("FwdLockEngine Destruction");
+
+    convertSessionMap.destroyMap();
+    decodeSessionMap.destroyMap();
+}
+
+int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) {
+    int retStatus = DrmConvertedStatus::STATUS_ERROR;
+
+    switch(status) {
+        case FwdLockConv_Status_OK:
+            retStatus = DrmConvertedStatus::STATUS_OK;
+            break;
+        case FwdLockConv_Status_SyntaxError:
+        case FwdLockConv_Status_InvalidArgument:
+        case FwdLockConv_Status_UnsupportedFileFormat:
+        case FwdLockConv_Status_UnsupportedContentTransferEncoding:
+            LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+                  "Returning STATUS_INPUTDATA_ERROR", status);
+            retStatus = DrmConvertedStatus::STATUS_INPUTDATA_ERROR;
+            break;
+        default:
+            LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+                  "Returning STATUS_ERROR", status);
+            retStatus = DrmConvertedStatus::STATUS_ERROR;
+            break;
+    }
+
+    return retStatus;
+}
+
+DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) {
+    DrmConstraints* drmConstraints = NULL;
+
+    LOGD("FwdLockEngine::onGetConstraints");
+
+    if (NULL != path &&
+        (RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) {
+        // Return the empty constraints to show no error condition.
+        drmConstraints = new DrmConstraints();
+    }
+
+    return drmConstraints;
+}
+
+DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) {
+    DrmMetadata* drmMetadata = NULL;
+
+    LOGD("FwdLockEngine::onGetMetadata");
+
+    if (NULL != path) {
+        // Returns empty metadata to show no error condition.
+        drmMetadata = new DrmMetadata();
+    }
+
+    return drmMetadata;
+}
+
+android::status_t FwdLockEngine::onInitialize(int uniqueId) {
+    LOGD("FwdLockEngine::onInitialize");
+
+
+    if (FwdLockGlue_InitializeKeyEncryption()) {
+        LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded");
+    } else {
+        LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:"
+             "errno = %d", errno);
+    }
+
+    return DRM_NO_ERROR;
+}
+
+android::status_t
+FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+    // Not used
+    LOGD("FwdLockEngine::onSetOnInfoListener");
+
+    return DRM_NO_ERROR;
+}
+
+android::status_t FwdLockEngine::onTerminate(int uniqueId) {
+    LOGD("FwdLockEngine::onTerminate");
+
+    return DRM_NO_ERROR;
+}
+
+DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) {
+    DrmSupportInfo* pSupportInfo = new DrmSupportInfo();
+
+    LOGD("FwdLockEngine::onGetSupportInfo");
+
+    // fill all Forward Lock mimetypes and extensions
+    if (NULL != pSupportInfo) {
+        pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_FL));
+        pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_FL));
+        pSupportInfo->addMimeType(String8(FWDLOCK_MIMETYPE_DM));
+        pSupportInfo->addFileSuffix(String8(FWDLOCK_DOTEXTENSION_DM));
+
+        pSupportInfo->setDescription(String8(FWDLOCK_DESCRIPTION));
+    }
+
+    return pSupportInfo;
+}
+
+bool FwdLockEngine::onCanHandle(int uniqueId, const String8& path) {
+    bool result = false;
+
+    String8 extString = path.getPathExtension();
+
+    extString.toLower();
+
+    if ((extString == String8(FWDLOCK_DOTEXTENSION_FL)) ||
+        (extString == String8(FWDLOCK_DOTEXTENSION_DM))) {
+        result = true;
+    }
+    return result;
+}
+
+DrmInfoStatus* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    DrmInfoStatus *drmInfoStatus = NULL;
+
+    // Nothing to process
+
+    drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8(""));
+
+    LOGD("FwdLockEngine::onProcessDrmInfo");
+
+    return drmInfoStatus;
+}
+
+status_t FwdLockEngine::onSaveRights(
+            int uniqueId,
+            const DrmRights& drmRights,
+            const String8& rightsPath,
+            const String8& contentPath) {
+    // No rights to save. Return
+    LOGD("FwdLockEngine::onSaveRights");
+    return DRM_ERROR_UNKNOWN;
+}
+
+DrmInfo* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    DrmInfo* drmInfo = NULL;
+
+    // Nothing to be done for Forward Lock file
+    LOGD("FwdLockEngine::onAcquireDrmInfo");
+
+    return drmInfo;
+}
+
+int FwdLockEngine::onCheckRightsStatus(int uniqueId,
+                                       const String8& path,
+                                       int action) {
+    int result = RightsStatus::RIGHTS_INVALID;
+
+    LOGD("FwdLockEngine::onCheckRightsStatus");
+
+    // Only Transfer action is not allowed for forward Lock files.
+    if (onCanHandle(uniqueId, path)) {
+        switch(action) {
+            case Action::DEFAULT:
+            case Action::PLAY:
+            case Action::RINGTONE:
+            case Action::OUTPUT:
+            case Action::PREVIEW:
+            case Action::EXECUTE:
+            case Action::DISPLAY:
+                result = RightsStatus::RIGHTS_VALID;
+                break;
+
+            case Action::TRANSFER:
+            default:
+                result = RightsStatus::RIGHTS_INVALID;
+                break;
+        }
+    }
+
+    return result;
+}
+
+status_t FwdLockEngine::onConsumeRights(int uniqueId,
+                                        DecryptHandle* decryptHandle,
+                                        int action,
+                                        bool reserve) {
+    // No rights consumption
+    LOGD("FwdLockEngine::onConsumeRights");
+    return DRM_NO_ERROR;
+}
+
+bool FwdLockEngine::onValidateAction(int uniqueId,
+                                     const String8& path,
+                                     int action,
+                                     const ActionDescription& description) {
+    LOGD("FwdLockEngine::onValidateAction");
+
+    // For the forwardlock engine checkRights and ValidateAction are the same.
+    return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID);
+}
+
+String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) {
+    LOGD("FwdLockEngine::onGetOriginalMimeType");
+    String8 mimeString = String8("");
+    int fileDesc = FwdLockFile_open(path.string());
+
+    if (-1 < fileDesc) {
+        const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
+
+        if (NULL != pMimeType) {
+            String8 contentType = String8(pMimeType);
+            contentType.toLower();
+            mimeString = MimeTypeUtil::convertMimeType(contentType);
+        }
+
+        FwdLockFile_close(fileDesc);
+    }
+
+    return mimeString;
+}
+
+int FwdLockEngine::onGetDrmObjectType(int uniqueId,
+                                      const String8& path,
+                                      const String8& mimeType) {
+    String8 mimeStr = String8(mimeType);
+
+    LOGD("FwdLockEngine::onGetDrmObjectType");
+
+    mimeStr.toLower();
+
+    /* Checks whether
+    * 1. path and mime type both are not empty strings (meaning unavailable) else content is unknown
+    * 2. if one of them is empty string and if other is known then its a DRM Content Object.
+    * 3. if both of them are available, then both may be of known type
+    *    (regardless of the relation between them to make it compatible with other DRM Engines)
+    */
+    if (((0 == path.length()) || onCanHandle(uniqueId, path)) &&
+        ((0 == mimeType.length()) || ((mimeStr == String8(FWDLOCK_MIMETYPE_FL)) ||
+        (mimeStr == String8(FWDLOCK_MIMETYPE_DM)))) && (mimeType != path) ) {
+            return DrmObjectType::CONTENT;
+    }
+
+    return DrmObjectType::UNKNOWN;
+}
+
+status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) {
+    // No Rights to remove
+    LOGD("FwdLockEngine::onRemoveRights");
+    return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onRemoveAllRights(int uniqueId) {
+    // No rights to remove
+    LOGD("FwdLockEngine::onRemoveAllRights");
+    return DRM_NO_ERROR;
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+                                            int playbackStatus, int64_t position) {
+#else
+status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+                                            int playbackStatus, int position) {
+#endif
+    // Not used
+    LOGD("FwdLockEngine::onSetPlaybackStatus");
+    return DRM_NO_ERROR;
+}
+
+status_t FwdLockEngine::onOpenConvertSession(int uniqueId,
+                                         int convertId) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    LOGD("FwdLockEngine::onOpenConvertSession");
+    if (!convertSessionMap.isCreated(convertId)) {
+        ConvertSession *newSession = new ConvertSession();
+        if (FwdLockConv_Status_OK ==
+            FwdLockConv_OpenSession(&(newSession->uniqueId), &(newSession->output))) {
+            convertSessionMap.addValue(convertId, newSession);
+            result = DRM_NO_ERROR;
+        } else {
+            LOGD("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed.");
+            delete newSession;
+        }
+    }
+    return result;
+}
+
+DrmConvertedStatus* FwdLockEngine::onConvertData(int uniqueId,
+                                                 int convertId,
+                                                 const DrmBuffer* inputData) {
+    FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+    DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+    int offset = -1;
+
+    if (NULL != inputData && convertSessionMap.isCreated(convertId)) {
+        ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+        if (NULL != convSession) {
+            retStatus = FwdLockConv_ConvertData(convSession->uniqueId,
+                                                inputData->data,
+                                                inputData->length,
+                                                &(convSession->output));
+
+            if (FwdLockConv_Status_OK == retStatus) {
+                // return bytes from conversion if available
+                if (convSession->output.fromConvertData.numBytes > 0) {
+                    convResult->data = new char[convSession->output.fromConvertData.numBytes];
+
+                    if (NULL != convResult->data) {
+                        convResult->length = convSession->output.fromConvertData.numBytes;
+                        memcpy(convResult->data,
+                               (char *)convSession->output.fromConvertData.pBuffer,
+                               convResult->length);
+                    }
+                }
+            } else {
+                offset = convSession->output.fromConvertData.errorPos;
+            }
+        }
+    }
+    return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId,
+                                                         int convertId) {
+    FwdLockConv_Status_t retStatus = FwdLockConv_Status_InvalidArgument;
+    DrmBuffer *convResult = new DrmBuffer(NULL, 0);
+    int offset = -1;
+
+    LOGD("FwdLockEngine::onCloseConvertSession");
+
+    if (convertSessionMap.isCreated(convertId)) {
+        ConvertSession *convSession = convertSessionMap.getValue(convertId);
+
+        if (NULL != convSession) {
+            retStatus = FwdLockConv_CloseSession(convSession->uniqueId, &(convSession->output));
+
+            if (FwdLockConv_Status_OK == retStatus) {
+                offset = convSession->output.fromCloseSession.fileOffset;
+                convResult->data = new char[FWD_LOCK_SIGNATURES_SIZE];
+
+                if (NULL != convResult->data) {
+                      convResult->length = FWD_LOCK_SIGNATURES_SIZE;
+                      memcpy(convResult->data,
+                             (char *)convSession->output.fromCloseSession.signatures,
+                             convResult->length);
+                }
+            }
+        }
+        convertSessionMap.removeValue(convertId);
+    }
+    return new DrmConvertedStatus(getConvertedStatus(retStatus), convResult, offset);
+}
+
+#ifdef USE_64BIT_DRM_API
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+                                             DecryptHandle* decryptHandle,
+                                             int fd,
+                                             off64_t offset,
+                                             off64_t length) {
+#else
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+                                             DecryptHandle* decryptHandle,
+                                             int fd,
+                                             int offset,
+                                             int length) {
+#endif
+    status_t result = DRM_ERROR_CANNOT_HANDLE;
+    int fileDesc = -1;
+
+    LOGD("FwdLockEngine::onOpenDecryptSession");
+
+    if ((-1 < fd) &&
+        (NULL != decryptHandle) &&
+        (!decodeSessionMap.isCreated(decryptHandle->decryptId))) {
+        fileDesc = dup(fd);
+    } else {
+        LOGD("FwdLockEngine::onOpenDecryptSession parameter error");
+        return result;
+    }
+
+    if (-1 < fileDesc &&
+        -1 < ::lseek(fileDesc, offset, SEEK_SET) &&
+        -1 < FwdLockFile_attach(fileDesc)) {
+        // check for file integrity. This must be done to protect the content mangling.
+        int retVal = FwdLockFile_CheckHeaderIntegrity(fileDesc);
+        DecodeSession* decodeSession = new DecodeSession(fileDesc);
+
+        if (retVal && NULL != decodeSession) {
+            decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession);
+            const char *pmime= FwdLockFile_GetContentType(fileDesc);
+            String8 contentType = String8(pmime == NULL ? "" : pmime);
+            contentType.toLower();
+            decryptHandle->mimeType = MimeTypeUtil::convertMimeType(contentType);
+            decryptHandle->decryptApiType = DecryptApiType::CONTAINER_BASED;
+            decryptHandle->status = RightsStatus::RIGHTS_VALID;
+            decryptHandle->decryptInfo = NULL;
+            result = DRM_NO_ERROR;
+        } else {
+            LOGD("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd");
+            FwdLockFile_detach(fileDesc);
+            ::close(fileDesc);
+            delete decodeSession;
+        }
+    }
+
+    LOGD("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result);
+
+    return result;
+}
+
+status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
+                                             DecryptHandle* decryptHandle,
+                                             const char* uri) {
+    status_t result = DRM_ERROR_CANNOT_HANDLE;
+    const char fileTag [] = "file://";
+
+    if (NULL != decryptHandle && NULL != uri && strlen(uri) > sizeof(fileTag)) {
+        String8 uriTag = String8(uri);
+        uriTag.toLower();
+
+        if (0 == strncmp(uriTag.string(), fileTag, sizeof(fileTag) - 1)) {
+            const char *filePath = strchr(uri + sizeof(fileTag) - 1, '/');
+            if (NULL != filePath && onCanHandle(uniqueId, String8(filePath))) {
+                int fd = open(filePath, O_RDONLY);
+
+                if (-1 < fd) {
+                    // offset is always 0 and length is not used. so any positive size.
+                    result = onOpenDecryptSession(uniqueId, decryptHandle, fd, 0, 1);
+
+                    // fd is duplicated already if success. closing the file
+                    close(fd);
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+status_t FwdLockEngine::onCloseDecryptSession(int uniqueId,
+                                              DecryptHandle* decryptHandle) {
+    status_t result = DRM_ERROR_UNKNOWN;
+    LOGD("FwdLockEngine::onCloseDecryptSession");
+
+    if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+        DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+        if (NULL != session && session->fileDesc > -1) {
+            FwdLockFile_detach(session->fileDesc);
+            ::close(session->fileDesc);
+            decodeSessionMap.removeValue(decryptHandle->decryptId);
+            result = DRM_NO_ERROR;
+        }
+    }
+
+    LOGD("FwdLockEngine::onCloseDecryptSession Exit");
+    return result;
+}
+
+status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId,
+                                                DecryptHandle* decryptHandle,
+                                                int decryptUnitId,
+                                                const DrmBuffer* headerInfo) {
+    LOGD("FwdLockEngine::onInitializeDecryptUnit");
+    return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    LOGD("FwdLockEngine::onDecrypt");
+    return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onDecrypt(int uniqueId,
+                                  DecryptHandle* decryptHandle,
+                                  int decryptUnitId,
+                                  const DrmBuffer* encBuffer,
+                                  DrmBuffer** decBuffer) {
+    LOGD("FwdLockEngine::onDecrypt");
+    return DRM_ERROR_UNKNOWN;
+}
+
+status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId,
+                                              DecryptHandle* decryptHandle,
+                                              int decryptUnitId) {
+    LOGD("FwdLockEngine::onFinalizeDecryptUnit");
+    return DRM_ERROR_UNKNOWN;
+}
+
+ssize_t FwdLockEngine::onRead(int uniqueId,
+                              DecryptHandle* decryptHandle,
+                              void* buffer,
+                              int numBytes) {
+    ssize_t size = -1;
+
+    if (NULL != decryptHandle &&
+       decodeSessionMap.isCreated(decryptHandle->decryptId) &&
+        NULL != buffer &&
+        numBytes > -1) {
+        DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+        if (NULL != session && session->fileDesc > -1) {
+            size = FwdLockFile_read(session->fileDesc, buffer, numBytes);
+
+            if (0 > size) {
+                session->offset = ((off_t)-1);
+            } else {
+                session->offset += size;
+            }
+        }
+    }
+
+    return size;
+}
+
+#ifdef USE_64BIT_DRM_API
+off64_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+                               off64_t offset, int whence) {
+#else
+off_t FwdLockEngine::onLseek(int uniqueId, DecryptHandle* decryptHandle,
+                             off_t offset, int whence) {
+#endif
+    off_t offval = -1;
+
+    if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+        DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
+        if (NULL != session && session->fileDesc > -1) {
+            offval = FwdLockFile_lseek(session->fileDesc, offset, whence);
+            session->offset = offval;
+        }
+    }
+
+    return offval;
+}
+
+#ifdef USE_64BIT_DRM_API
+ssize_t FwdLockEngine::onPread(int uniqueId,
+                               DecryptHandle* decryptHandle,
+                               void* buffer,
+                               ssize_t numBytes,
+                               off64_t offset) {
+#else
+ssize_t FwdLockEngine::onPread(int uniqueId,
+                               DecryptHandle* decryptHandle,
+                               void* buffer,
+                               ssize_t numBytes,
+                               off_t offset) {
+#endif
+    ssize_t bytesRead = -1;
+
+    DecodeSession* decoderSession = NULL;
+
+    if ((NULL != decryptHandle) &&
+        (NULL != (decoderSession = decodeSessionMap.getValue(decryptHandle->decryptId))) &&
+        (NULL != buffer) &&
+        (numBytes > -1) &&
+        (offset > -1)) {
+        if (offset != decoderSession->offset) {
+            decoderSession->offset = onLseek(uniqueId, decryptHandle, offset, SEEK_SET);
+        }
+
+        if (((off_t)-1) != decoderSession->offset) {
+            bytesRead = onRead(uniqueId, decryptHandle, buffer, numBytes);
+            if (bytesRead < 0) {
+                LOGD("FwdLockEngine::onPread error reading");
+            }
+        }
+    } else {
+        LOGD("FwdLockEngine::onPread decryptId not found");
+    }
+
+    return bytesRead;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
new file mode 100644
index 0000000..9ee7961
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.mk
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2010 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 $(call all-subdir-makefiles)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
new file mode 100644
index 0000000..6c5d3cf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2010 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 := \
+    FwdLockGlue.c
+
+LOCAL_C_INCLUDES := \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_MODULE := libfwdlock-common
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
new file mode 100644
index 0000000..92bda8f
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define KEY_SIZE 16
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+static int isInitialized = FALSE;
+
+static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";
+
+static AES_KEY encryptionRoundKeys;
+static AES_KEY decryptionRoundKeys;
+
+/**
+ * Creates all directories along the fully qualified path of the given file.
+ *
+ * @param[in] path A reference to the fully qualified path of a file.
+ * @param[in] mode The access mode to use for the directories being created.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) {
+    int result = TRUE;
+    size_t partialPathLength = strlen(path);
+    char *partialPath = malloc(partialPathLength + 1);
+    if (partialPath == NULL) {
+        result = FALSE;
+    } else {
+        size_t i;
+        for (i = 0; i < partialPathLength; ++i) {
+            if (path[i] == '/' && i > 0) {
+                partialPath[i] = '\0';
+                if (mkdir(partialPath, mode) != 0 && errno != EEXIST) {
+                    result = FALSE;
+                    break;
+                }
+            }
+            partialPath[i] = path[i];
+        }
+        free(partialPath);
+    }
+    return result;
+}
+
+/**
+ * Initializes the round keys used for encryption and decryption of session keys. First creates a
+ * device-unique key-encryption key if none exists yet.
+ */
+static void FwdLockGlue_InitializeRoundKeys() {
+    unsigned char keyEncryptionKey[KEY_SIZE];
+    int fileDesc = open(strKeyFilename, O_RDONLY);
+    if (fileDesc >= 0) {
+        if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+            isInitialized = TRUE;
+        }
+        (void)close(fileDesc);
+    } else if (errno == ENOENT &&
+               FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) &&
+               FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) {
+        fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR);
+        if (fileDesc >= 0) {
+            if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
+                isInitialized = TRUE;
+            }
+            (void)close(fileDesc);
+        }
+    }
+    if (isInitialized) {
+        if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 ||
+            AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) {
+            isInitialized = FALSE;
+        }
+    }
+    memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
+}
+
+/**
+ * Validates the padding of a decrypted key.
+ *
+ * @param[in] pData A reference to the buffer containing the decrypted key and padding.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the padding was valid.
+ */
+static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) {
+    size_t i;
+    size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE);
+    pData += decryptedKeyLength;
+    for (i = 0; i < padding; ++i) {
+        if ((size_t)*pData != padding) {
+            return FALSE;
+        }
+        ++pData;
+    }
+    return TRUE;
+}
+
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) {
+    // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the
+    // non-blocking version of "/dev/random").
+    ssize_t numBytesRead = 0;
+    int fileDesc = open("/dev/urandom", O_RDONLY);
+    if (fileDesc >= 0) {
+        numBytesRead = read(fileDesc, pBuffer, numBytes);
+        (void)close(fileDesc);
+    }
+    return numBytesRead >= 0 && (size_t)numBytesRead == numBytes;
+}
+
+int FwdLockGlue_InitializeKeyEncryption() {
+    static pthread_once_t once = PTHREAD_ONCE_INIT;
+    pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
+    return isInitialized;
+}
+
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
+    return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
+}
+
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+                           size_t plaintextKeyLength,
+                           void *pEncryptedKey,
+                           size_t encryptedKeyLength) {
+    int result = FALSE;
+    assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength));
+    if (FwdLockGlue_InitializeKeyEncryption()) {
+        unsigned char initVector[AES_BLOCK_SIZE];
+        if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) {
+            size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE);
+            size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+            memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength);
+            memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding);
+            memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE);
+            AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys,
+                            initVector, AES_ENCRYPT);
+            result = TRUE;
+        }
+    }
+    return result;
+}
+
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+                           size_t encryptedKeyLength,
+                           void *pDecryptedKey,
+                           size_t decryptedKeyLength) {
+    int result = FALSE;
+    assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength));
+    if (FwdLockGlue_InitializeKeyEncryption()) {
+        size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
+        unsigned char *pData = malloc(dataLength);
+        if (pData != NULL) {
+            unsigned char initVector[AES_BLOCK_SIZE];
+            memcpy(pData, pEncryptedKey, dataLength);
+            memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE);
+            AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector,
+                            AES_DECRYPT);
+            memcpy(pDecryptedKey, pData, decryptedKeyLength);
+            result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength);
+            free(pData);
+        }
+    }
+    return result;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
new file mode 100644
index 0000000..f36f6ea
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKGLUE_H__
+#define __FWDLOCKGLUE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generates the specified number of cryptographically secure random bytes.
+ *
+ * @param[out] pBuffer A reference to the buffer that should receive the random data.
+ * @param[in] numBytes The number of random bytes to generate.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes);
+
+/**
+ * Performs initialization of the key-encryption key. Should be called once during startup to
+ * facilitate encryption and decryption of session keys.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_InitializeKeyEncryption();
+
+/**
+ * Returns the length of the encrypted key, given the length of the plaintext key.
+ *
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ *
+ * @return The length in bytes of the encrypted key.
+ */
+size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength);
+
+/**
+ * Encrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pPlaintextKey A reference to the buffer containing the plaintext key.
+ * @param[in] plaintextKeyLength The length in bytes of the plaintext key.
+ * @param[out] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
+                           size_t plaintextKeyLength,
+                           void *pEncryptedKey,
+                           size_t encryptedKeyLength);
+
+/**
+ * Decrypts the given session key using a key-encryption key unique to this device.
+ *
+ * @param[in] pEncryptedKey A reference to the buffer containing the encrypted key.
+ * @param[in] encryptedKeyLength The length in bytes of the encrypted key.
+ * @param[out] pDecryptedKey A reference to the buffer containing the decrypted key.
+ * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
+ *
+ * @return A Boolean value indicating whether the operation was successful.
+ */
+int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
+                           size_t encryptedKeyLength,
+                           void *pDecryptedKey,
+                           size_t decryptedKeyLength);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKGLUE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
new file mode 100644
index 0000000..00bb788
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2010 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 := \
+    FwdLockConv.c
+
+LOCAL_C_INCLUDES := \
+    frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-converter
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
new file mode 100644
index 0000000..14ea9e9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockConv.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define MAX_NUM_SESSIONS 32
+
+#define OUTPUT_BUFFER_SIZE_INCREMENT 1024
+#define READ_BUFFER_SIZE 1024
+
+#define MAX_BOUNDARY_LENGTH 70
+#define MAX_DELIMITER_LENGTH (MAX_BOUNDARY_LENGTH + 4)
+
+#define STRING_LENGTH_INCREMENT 25
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+/**
+ * Data type for the parser states of the converter.
+ */
+typedef enum FwdLockConv_ParserState {
+    FwdLockConv_ParserState_WantsOpenDelimiter,
+    FwdLockConv_ParserState_WantsMimeHeaders,
+    FwdLockConv_ParserState_WantsBinaryEncodedData,
+    FwdLockConv_ParserState_WantsBase64EncodedData,
+    FwdLockConv_ParserState_Done
+} FwdLockConv_ParserState_t;
+
+/**
+ * Data type for the scanner states of the converter.
+ */
+typedef enum FwdLockConv_ScannerState {
+    FwdLockConv_ScannerState_WantsFirstDash,
+    FwdLockConv_ScannerState_WantsSecondDash,
+    FwdLockConv_ScannerState_WantsCR,
+    FwdLockConv_ScannerState_WantsLF,
+    FwdLockConv_ScannerState_WantsBoundary,
+    FwdLockConv_ScannerState_WantsBoundaryEnd,
+    FwdLockConv_ScannerState_WantsMimeHeaderNameStart,
+    FwdLockConv_ScannerState_WantsMimeHeaderName,
+    FwdLockConv_ScannerState_WantsMimeHeaderNameEnd,
+    FwdLockConv_ScannerState_WantsContentTypeStart,
+    FwdLockConv_ScannerState_WantsContentType,
+    FwdLockConv_ScannerState_WantsContentTransferEncodingStart,
+    FwdLockConv_ScannerState_Wants_A_OR_I,
+    FwdLockConv_ScannerState_Wants_N,
+    FwdLockConv_ScannerState_Wants_A,
+    FwdLockConv_ScannerState_Wants_R,
+    FwdLockConv_ScannerState_Wants_Y,
+    FwdLockConv_ScannerState_Wants_S,
+    FwdLockConv_ScannerState_Wants_E,
+    FwdLockConv_ScannerState_Wants_6,
+    FwdLockConv_ScannerState_Wants_4,
+    FwdLockConv_ScannerState_Wants_B,
+    FwdLockConv_ScannerState_Wants_I,
+    FwdLockConv_ScannerState_Wants_T,
+    FwdLockConv_ScannerState_WantsContentTransferEncodingEnd,
+    FwdLockConv_ScannerState_WantsMimeHeaderValueEnd,
+    FwdLockConv_ScannerState_WantsMimeHeadersEnd,
+    FwdLockConv_ScannerState_WantsByte1,
+    FwdLockConv_ScannerState_WantsByte1_AfterCRLF,
+    FwdLockConv_ScannerState_WantsByte2,
+    FwdLockConv_ScannerState_WantsByte3,
+    FwdLockConv_ScannerState_WantsByte4,
+    FwdLockConv_ScannerState_WantsPadding,
+    FwdLockConv_ScannerState_WantsWhitespace,
+    FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF,
+    FwdLockConv_ScannerState_WantsDelimiter
+} FwdLockConv_ScannerState_t;
+
+/**
+ * Data type for the content transfer encoding.
+ */
+typedef enum FwdLockConv_ContentTransferEncoding {
+    FwdLockConv_ContentTransferEncoding_Undefined,
+    FwdLockConv_ContentTransferEncoding_Binary,
+    FwdLockConv_ContentTransferEncoding_Base64
+} FwdLockConv_ContentTransferEncoding_t;
+
+/**
+ * Data type for a dynamically growing string.
+ */
+typedef struct FwdLockConv_String {
+    char *ptr;
+    size_t length;
+    size_t maxLength;
+    size_t lengthIncrement;
+} FwdLockConv_String_t;
+
+/**
+ * Data type for the per-file state information needed by the converter.
+ */
+typedef struct FwdLockConv_Session {
+    FwdLockConv_ParserState_t parserState;
+    FwdLockConv_ScannerState_t scannerState;
+    FwdLockConv_ScannerState_t savedScannerState;
+    off64_t numCharsConsumed;
+    char delimiter[MAX_DELIMITER_LENGTH];
+    size_t delimiterLength;
+    size_t delimiterMatchPos;
+    FwdLockConv_String_t mimeHeaderName;
+    FwdLockConv_String_t contentType;
+    FwdLockConv_ContentTransferEncoding_t contentTransferEncoding;
+    unsigned char sessionKey[KEY_SIZE];
+    void *pEncryptedSessionKey;
+    size_t encryptedSessionKeyLength;
+    AES_KEY encryptionRoundKeys;
+    HMAC_CTX signingContext;
+    unsigned char topHeader[TOP_HEADER_SIZE];
+    unsigned char counter[AES_BLOCK_SIZE];
+    unsigned char keyStream[AES_BLOCK_SIZE];
+    int keyStreamIndex;
+    unsigned char ch;
+    size_t outputBufferSize;
+    size_t dataOffset;
+    size_t numDataBytes;
+} FwdLockConv_Session_t;
+
+static FwdLockConv_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const FwdLockConv_String_t nullString = { NULL, 0, 0, STRING_LENGTH_INCREMENT };
+
+static const unsigned char topHeaderTemplate[] =
+    { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+static const char strContent[] = "content-";
+static const char strType[] = "type";
+static const char strTransferEncoding[] = "transfer-encoding";
+static const char strTextPlain[] = "text/plain";
+static const char strApplicationVndOmaDrmRightsXml[] = "application/vnd.oma.drm.rights+xml";
+static const char strApplicationVndOmaDrmContent[] = "application/vnd.oma.drm.content";
+
+static const size_t strlenContent = sizeof strContent - 1;
+static const size_t strlenTextPlain = sizeof strTextPlain - 1;
+
+static const signed char base64Values[] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * Acquires an unused converter session.
+ *
+ * @return A session ID.
+ */
+static int FwdLockConv_AcquireSession() {
+    int sessionId = -1;
+    int i;
+    pthread_mutex_lock(&sessionAcquisitionMutex);
+    for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+        if (sessionPtrs[i] == NULL) {
+            sessionPtrs[i] = malloc(sizeof *sessionPtrs[i]);
+            if (sessionPtrs[i] != NULL) {
+                sessionId = i;
+            }
+            break;
+        }
+    }
+    pthread_mutex_unlock(&sessionAcquisitionMutex);
+    return sessionId;
+}
+
+/**
+ * Checks whether a session ID is in range and currently in use.
+ *
+ * @param[in] sessionID A session ID.
+ *
+ * @return A Boolean value indicating whether the session ID is in range and currently in use.
+ */
+static int FwdLockConv_IsValidSession(int sessionId) {
+    return 0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL;
+}
+
+/**
+ * Releases a converter session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockConv_ReleaseSession(int sessionId) {
+    pthread_mutex_lock(&sessionAcquisitionMutex);
+    assert(FwdLockConv_IsValidSession(sessionId));
+    memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+    free(sessionPtrs[sessionId]);
+    sessionPtrs[sessionId] = NULL;
+    pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives cryptographically independent keys for encryption and signing from the session key.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) {
+    FwdLockConv_Status_t status;
+    struct FwdLockConv_DeriveKeys_Data {
+        AES_KEY sessionRoundKeys;
+        unsigned char value[KEY_SIZE];
+        unsigned char key[KEY_SIZE];
+    } *pData = malloc(sizeof *pData);
+    if (pData == NULL) {
+        status = FwdLockConv_Status_OutOfMemory;
+    } else {
+        if (AES_set_encrypt_key(pSession->sessionKey, KEY_SIZE_IN_BITS,
+                                &pData->sessionRoundKeys) != 0) {
+            status = FwdLockConv_Status_ProgramError;
+        } else {
+            // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+            memset(pData->value, 0, KEY_SIZE);
+            AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+            if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+                                    &pSession->encryptionRoundKeys) != 0) {
+                status = FwdLockConv_Status_ProgramError;
+            } else {
+                // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+                ++pData->value[0];
+                AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+                HMAC_CTX_init(&pSession->signingContext);
+                HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+                status = FwdLockConv_Status_OK;
+            }
+        }
+        memset(pData, 0, sizeof pData); // Zero out key data.
+        free(pData);
+    }
+    return status;
+}
+
+/**
+ * Checks whether a given character is valid in a boundary. Note that the boundary may contain
+ * leading and internal spaces.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a boundary.
+ */
+static int FwdLockConv_IsBoundaryChar(int ch) {
+    return isalnum(ch) || ch == '\'' ||
+            ch == '(' || ch == ')' || ch == '+' || ch == '_' || ch == ',' || ch == '-' ||
+            ch == '.' || ch == '/' || ch == ':' || ch == '=' || ch == '?' || ch == ' ';
+}
+
+/**
+ * Checks whether a given character should be considered whitespace, using a narrower definition
+ * than the standard-library isspace() function.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character should be considered whitespace.
+ */
+static int FwdLockConv_IsWhitespace(int ch) {
+    return ch == ' ' || ch == '\t';
+}
+
+/**
+ * Removes trailing spaces from the delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t *pSession) {
+    while (pSession->delimiterLength > 4 &&
+           pSession->delimiter[pSession->delimiterLength - 1] == ' ') {
+        --pSession->delimiterLength;
+    }
+    if (pSession->delimiterLength > 4) {
+        return FwdLockConv_Status_OK;
+    }
+    return FwdLockConv_Status_SyntaxError;
+}
+
+/**
+ * Matches the open delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t *pSession,
+                                                           int ch) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsFirstDash:
+        if (ch == '-') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsSecondDash:
+        if (ch == '-') {
+            // The delimiter starts with "\r\n--" (the open delimiter may omit the initial "\r\n").
+            // The rest is the user-defined boundary that should come next.
+            pSession->delimiter[0] = '\r';
+            pSession->delimiter[1] = '\n';
+            pSession->delimiter[2] = '-';
+            pSession->delimiter[3] = '-';
+            pSession->delimiterLength = 4;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsBoundary;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsCR:
+        if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsLF:
+        if (ch == '\n') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+        } else if (ch != '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsBoundary:
+        if (FwdLockConv_IsBoundaryChar(ch)) {
+            // The boundary may contain leading and internal spaces, so trailing spaces will also be
+            // matched here. These will be removed later.
+            if (pSession->delimiterLength < MAX_DELIMITER_LENGTH) {
+                pSession->delimiter[pSession->delimiterLength++] = ch;
+            } else if (ch != ' ') {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (ch == '\r') {
+            status = FwdLockConv_RightTrimDelimiter(pSession);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+            }
+        } else if (ch == '\t') {
+            status = FwdLockConv_RightTrimDelimiter(pSession);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+            }
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsWhitespace:
+        if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsBoundaryEnd:
+        if (ch == '\n') {
+            pSession->parserState = FwdLockConv_ParserState_WantsMimeHeaders;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header name.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header name.
+ */
+static int FwdLockConv_IsMimeHeaderNameChar(int ch) {
+    return isgraph(ch) && ch != ':';
+}
+
+/**
+ * Checks whether a given character is valid in a MIME header value.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in a MIME header value.
+ */
+static int FwdLockConv_IsMimeHeaderValueChar(int ch) {
+    return isgraph(ch) && ch != ';';
+}
+
+/**
+ * Appends a character to the specified dynamically growing string.
+ *
+ * @param[in,out] pString A reference to a dynamically growing string.
+ * @param[in] ch The character to append.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_StringAppend(FwdLockConv_String_t *pString, int ch) {
+    if (pString->length == pString->maxLength) {
+        size_t newMaxLength = pString->maxLength + pString->lengthIncrement;
+        char *newPtr = realloc(pString->ptr, newMaxLength + 1);
+        if (newPtr == NULL) {
+            return FwdLockConv_Status_OutOfMemory;
+        }
+        pString->ptr = newPtr;
+        pString->maxLength = newMaxLength;
+    }
+    pString->ptr[pString->length++] = ch;
+    pString->ptr[pString->length] = '\0';
+    return FwdLockConv_Status_OK;
+}
+
+/**
+ * Attempts to recognize the MIME header name and changes the scanner state accordingly.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t *pSession) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    if (strncmp(pSession->mimeHeaderName.ptr, strContent, strlenContent) == 0) {
+        if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strType) == 0) {
+            if (pSession->contentType.ptr == NULL) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsContentTypeStart;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strTransferEncoding) == 0) {
+            if (pSession->contentTransferEncoding ==
+                    FwdLockConv_ContentTransferEncoding_Undefined) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingStart;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        }
+    } else {
+        pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+    }
+    return status;
+}
+
+/**
+ * Applies defaults to missing MIME header values.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_ApplyDefaults(FwdLockConv_Session_t *pSession) {
+    if (pSession->contentType.ptr == NULL) {
+        // Content type is missing: default to "text/plain".
+        pSession->contentType.ptr = malloc(sizeof strTextPlain);
+        if (pSession->contentType.ptr == NULL) {
+            return FwdLockConv_Status_OutOfMemory;
+        }
+        memcpy(pSession->contentType.ptr, strTextPlain, sizeof strTextPlain);
+        pSession->contentType.length = strlenTextPlain;
+        pSession->contentType.maxLength = strlenTextPlain;
+    }
+    if (pSession->contentTransferEncoding == FwdLockConv_ContentTransferEncoding_Undefined) {
+        // Content transfer encoding is missing: default to binary.
+        pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+    }
+    return FwdLockConv_Status_OK;
+}
+
+/**
+ * Verifies that the content type is supported.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_VerifyContentType(FwdLockConv_Session_t *pSession) {
+    FwdLockConv_Status_t status;
+    if (pSession->contentType.ptr == NULL) {
+        status = FwdLockConv_Status_ProgramError;
+    } else if (strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmRightsXml) == 0 ||
+               strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmContent) == 0) {
+        status = FwdLockConv_Status_UnsupportedFileFormat;
+    } else {
+        status = FwdLockConv_Status_OK;
+    }
+    return status;
+}
+
+/**
+ * Writes the header of the output file.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteHeader(FwdLockConv_Session_t *pSession,
+                                                    FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (pSession->contentType.length > UCHAR_MAX) {
+        status = FwdLockConv_Status_SyntaxError;
+    } else {
+        pSession->outputBufferSize = OUTPUT_BUFFER_SIZE_INCREMENT;
+        pOutput->fromConvertData.pBuffer = malloc(pSession->outputBufferSize);
+        if (pOutput->fromConvertData.pBuffer == NULL) {
+            status = FwdLockConv_Status_OutOfMemory;
+        } else {
+            size_t encryptedSessionKeyPos = TOP_HEADER_SIZE + pSession->contentType.length;
+            size_t dataSignaturePos = encryptedSessionKeyPos + pSession->encryptedSessionKeyLength;
+            size_t headerSignaturePos = dataSignaturePos + SHA1_HASH_SIZE;
+            pSession->dataOffset = headerSignaturePos + SHA1_HASH_SIZE;
+            memcpy(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate);
+            pSession->topHeader[CONTENT_TYPE_LENGTH_POS] =
+                    (unsigned char)pSession->contentType.length;
+            memcpy(pOutput->fromConvertData.pBuffer, pSession->topHeader, TOP_HEADER_SIZE);
+            memcpy((char *)pOutput->fromConvertData.pBuffer + TOP_HEADER_SIZE,
+                   pSession->contentType.ptr, pSession->contentType.length);
+            memcpy((char *)pOutput->fromConvertData.pBuffer + encryptedSessionKeyPos,
+                   pSession->pEncryptedSessionKey, pSession->encryptedSessionKeyLength);
+
+            // Set the signatures to all zeros for now; they will have to be updated later.
+            memset((char *)pOutput->fromConvertData.pBuffer + dataSignaturePos, 0,
+                   SHA1_HASH_SIZE);
+            memset((char *)pOutput->fromConvertData.pBuffer + headerSignaturePos, 0,
+                   SHA1_HASH_SIZE);
+
+            pOutput->fromConvertData.numBytes = pSession->dataOffset;
+            status = FwdLockConv_Status_OK;
+        }
+    }
+    return status;
+}
+
+/**
+ * Matches the MIME headers.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t *pSession,
+                                                         int ch,
+                                                         FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsMimeHeaderNameStart:
+        if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+            pSession->mimeHeaderName.length = 0;
+            status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderName;
+            }
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeadersEnd;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeaderName:
+        if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
+            status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
+        } else if (ch == ':') {
+            status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameEnd;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeaderNameEnd:
+        if (ch == ':') {
+            status = FwdLockConv_RecognizeMimeHeaderName(pSession);
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentTypeStart:
+        if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+            status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsContentType;
+            }
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentType:
+        if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
+            status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
+        } else if (ch == ';') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentTransferEncodingStart:
+        if (ch == 'b' || ch == 'B') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_A_OR_I;
+        } else if (ch == '7' || ch == '8') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_B;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_A_OR_I:
+        if (ch == 'i' || ch == 'I') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_N;
+        } else if (ch == 'a' || ch == 'A') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_S;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_N:
+        if (ch == 'n' || ch == 'N') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_A;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_A:
+        if (ch == 'a' || ch == 'A') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_R;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_R:
+        if (ch == 'r' || ch == 'R') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_Y;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_Y:
+        if (ch == 'y' || ch == 'Y') {
+            pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_S:
+        if (ch == 's' || ch == 'S') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_E;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_E:
+        if (ch == 'e' || ch == 'E') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_6;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_6:
+        if (ch == '6') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_4;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_4:
+        if (ch == '4') {
+            pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Base64;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_B:
+        if (ch == 'b' || ch == 'B') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_I;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_I:
+        if (ch == 'i' || ch == 'I') {
+            pSession->scannerState = FwdLockConv_ScannerState_Wants_T;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_Wants_T:
+        if (ch == 't' || ch == 'T') {
+            pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsContentTransferEncodingEnd:
+        if (ch == ';') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
+        } else {
+            status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeaderValueEnd:
+        if (ch == ';') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
+        } else if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsCR:
+        if (ch == '\r') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsLF:
+        if (ch == '\n') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsMimeHeadersEnd:
+        if (ch == '\n') {
+            status = FwdLockConv_ApplyDefaults(pSession);
+            if (status == FwdLockConv_Status_OK) {
+                status = FwdLockConv_VerifyContentType(pSession);
+            }
+            if (status == FwdLockConv_Status_OK) {
+                status = FwdLockConv_WriteHeader(pSession, pOutput);
+            }
+            if (status == FwdLockConv_Status_OK) {
+                if (pSession->contentTransferEncoding ==
+                        FwdLockConv_ContentTransferEncoding_Binary) {
+                    pSession->parserState = FwdLockConv_ParserState_WantsBinaryEncodedData;
+                } else {
+                    pSession->parserState = FwdLockConv_ParserState_WantsBase64EncodedData;
+                }
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+            }
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Increments the counter, treated as a 16-byte little-endian number, by one.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ */
+static void FwdLockConv_IncrementCounter(FwdLockConv_Session_t *pSession) {
+    size_t i = 0;
+    while ((++pSession->counter[i] == 0) && (++i < AES_BLOCK_SIZE))
+        ;
+}
+
+/**
+ * Encrypts the given character and writes it to the output buffer.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch The character to encrypt and write.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t *pSession,
+                                                           unsigned char ch,
+                                                           FwdLockConv_Output_t *pOutput) {
+    if (pOutput->fromConvertData.numBytes == pSession->outputBufferSize) {
+        void *pBuffer;
+        pSession->outputBufferSize += OUTPUT_BUFFER_SIZE_INCREMENT;
+        pBuffer = realloc(pOutput->fromConvertData.pBuffer, pSession->outputBufferSize);
+        if (pBuffer == NULL) {
+            return FwdLockConv_Status_OutOfMemory;
+        }
+        pOutput->fromConvertData.pBuffer = pBuffer;
+    }
+    if (++pSession->keyStreamIndex == AES_BLOCK_SIZE) {
+        FwdLockConv_IncrementCounter(pSession);
+        pSession->keyStreamIndex = 0;
+    }
+    if (pSession->keyStreamIndex == 0) {
+        AES_encrypt(pSession->counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+    }
+    ch ^= pSession->keyStream[pSession->keyStreamIndex];
+    ((unsigned char *)pOutput->fromConvertData.pBuffer)[pOutput->fromConvertData.numBytes++] = ch;
+    ++pSession->numDataBytes;
+    return FwdLockConv_Status_OK;
+}
+
+/**
+ * Matches binary-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t *pSession,
+                                                               int ch,
+                                                               FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsByte1:
+        if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+            // The partial match of the delimiter turned out to be spurious. Flush the matched bytes
+            // to the output buffer and start over.
+            size_t i;
+            for (i = 0; i < pSession->delimiterMatchPos; ++i) {
+                status = FwdLockConv_WriteEncryptedChar(pSession, pSession->delimiter[i], pOutput);
+                if (status != FwdLockConv_Status_OK) {
+                    return status;
+                }
+            }
+            pSession->delimiterMatchPos = 0;
+        }
+        if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+            // The current character isn't part of the delimiter. Write it to the output buffer.
+            status = FwdLockConv_WriteEncryptedChar(pSession, ch, pOutput);
+        } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+            // The entire delimiter has been matched. The only valid characters now are the "--"
+            // that complete the close delimiter (no more message parts are expected).
+            pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsFirstDash:
+        if (ch == '-') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsSecondDash:
+        if (ch == '-') {
+            pSession->parserState = FwdLockConv_ParserState_Done;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Checks whether a given character is valid in base64-encoded data.
+ *
+ * @param[in] ch The character to check.
+ *
+ * @return A Boolean value indicating whether the given character is valid in base64-encoded data.
+ */
+static int FwdLockConv_IsBase64Char(int ch) {
+    return 0 <= ch && ch <= 'z' && base64Values[ch] >= 0;
+}
+
+/**
+ * Matches base64-encoded content data and encrypts it, while looking out for the close delimiter.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t *pSession,
+                                                               int ch,
+                                                               FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status = FwdLockConv_Status_OK;
+    switch (pSession->scannerState) {
+    case FwdLockConv_ScannerState_WantsByte1:
+    case FwdLockConv_ScannerState_WantsByte1_AfterCRLF:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch = base64Values[ch] << 2;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsByte2;
+        } else if (ch == '\r') {
+            pSession->savedScannerState = FwdLockConv_ScannerState_WantsByte1_AfterCRLF;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '-') {
+            if (pSession->scannerState == FwdLockConv_ScannerState_WantsByte1_AfterCRLF) {
+                pSession->delimiterMatchPos = 3;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsByte2:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch |= base64Values[ch] >> 4;
+            status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->ch = base64Values[ch] << 4;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte3;
+            }
+        } else if (ch == '\r') {
+            pSession->savedScannerState = pSession->scannerState;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsByte3:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch |= base64Values[ch] >> 2;
+            status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->ch = base64Values[ch] << 6;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte4;
+            }
+        } else if (ch == '\r') {
+            pSession->savedScannerState = pSession->scannerState;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '=') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsPadding;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsByte4:
+        if (FwdLockConv_IsBase64Char(ch)) {
+            pSession->ch |= base64Values[ch];
+            status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
+            if (status == FwdLockConv_Status_OK) {
+                pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
+            }
+        } else if (ch == '\r') {
+            pSession->savedScannerState = pSession->scannerState;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '=') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+        } else if (!FwdLockConv_IsWhitespace(ch)) {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsLF:
+        if (ch == '\n') {
+            pSession->scannerState = pSession->savedScannerState;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsPadding:
+        if (ch == '=') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsWhitespace:
+    case FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF:
+        if (ch == '\r') {
+            pSession->savedScannerState = FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF;
+            pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
+        } else if (ch == '-') {
+            if (pSession->scannerState == FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF) {
+                pSession->delimiterMatchPos = 3;
+                pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
+            } else {
+                status = FwdLockConv_Status_SyntaxError;
+            }
+        } else if (FwdLockConv_IsWhitespace(ch)) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsDelimiter:
+        if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
+            status = FwdLockConv_Status_SyntaxError;
+        } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsFirstDash:
+        if (ch == '-') {
+            pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    case FwdLockConv_ScannerState_WantsSecondDash:
+        if (ch == '-') {
+            pSession->parserState = FwdLockConv_ParserState_Done;
+        } else {
+            status = FwdLockConv_Status_SyntaxError;
+        }
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+/**
+ * Pushes a single character into the converter's state machine.
+ *
+ * @param[in,out] pSession A reference to a converter session.
+ * @param[in] ch A character.
+ * @param[in,out] pOutput The output from the conversion process.
+ *
+ * @return A status code.
+ */
+static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession,
+                                                 int ch,
+                                                 FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    ++pSession->numCharsConsumed;
+    switch (pSession->parserState) {
+    case FwdLockConv_ParserState_WantsOpenDelimiter:
+        status = FwdLockConv_MatchOpenDelimiter(pSession, ch);
+        break;
+    case FwdLockConv_ParserState_WantsMimeHeaders:
+        status = FwdLockConv_MatchMimeHeaders(pSession, ch, pOutput);
+        break;
+    case FwdLockConv_ParserState_WantsBinaryEncodedData:
+        status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput);
+        break;
+    case FwdLockConv_ParserState_WantsBase64EncodedData:
+        status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput);
+        break;
+    case FwdLockConv_ParserState_Done:
+        status = FwdLockConv_Status_OK;
+        break;
+    default:
+        status = FwdLockConv_Status_ProgramError;
+        break;
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (pSessionId == NULL || pOutput == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        *pSessionId = FwdLockConv_AcquireSession();
+        if (*pSessionId < 0) {
+            status = FwdLockConv_Status_TooManySessions;
+        } else {
+            FwdLockConv_Session_t *pSession = sessionPtrs[*pSessionId];
+            pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+            if (pSession->encryptedSessionKeyLength < AES_BLOCK_SIZE) {
+                // The encrypted session key is used as the CTR-mode nonce, so it must be at least
+                // the size of a single AES block.
+                status = FwdLockConv_Status_ProgramError;
+            } else {
+                pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+                if (pSession->pEncryptedSessionKey == NULL) {
+                    status = FwdLockConv_Status_OutOfMemory;
+                } else {
+                    if (!FwdLockGlue_GetRandomNumber(pSession->sessionKey, KEY_SIZE)) {
+                        status = FwdLockConv_Status_RandomNumberGenerationFailed;
+                    } else if (!FwdLockGlue_EncryptKey(pSession->sessionKey, KEY_SIZE,
+                                                       pSession->pEncryptedSessionKey,
+                                                       pSession->encryptedSessionKeyLength)) {
+                        status = FwdLockConv_Status_KeyEncryptionFailed;
+                    } else {
+                        status = FwdLockConv_DeriveKeys(pSession);
+                    }
+                    if (status == FwdLockConv_Status_OK) {
+                        memset(pSession->sessionKey, 0, KEY_SIZE); // Zero out key data.
+                        memcpy(pSession->counter, pSession->pEncryptedSessionKey, AES_BLOCK_SIZE);
+                        pSession->parserState = FwdLockConv_ParserState_WantsOpenDelimiter;
+                        pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
+                        pSession->numCharsConsumed = 0;
+                        pSession->delimiterMatchPos = 0;
+                        pSession->mimeHeaderName = nullString;
+                        pSession->contentType = nullString;
+                        pSession->contentTransferEncoding =
+                                FwdLockConv_ContentTransferEncoding_Undefined;
+                        pSession->keyStreamIndex = -1;
+                        pOutput->fromConvertData.pBuffer = NULL;
+                        pOutput->fromConvertData.errorPos = INVALID_OFFSET;
+                    } else {
+                        free(pSession->pEncryptedSessionKey);
+                    }
+                }
+            }
+            if (status != FwdLockConv_Status_OK) {
+                FwdLockConv_ReleaseSession(*pSessionId);
+                *pSessionId = -1;
+            }
+        }
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+                                             const void *pBuffer,
+                                             size_t numBytes,
+                                             FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (!FwdLockConv_IsValidSession(sessionId) || pBuffer == NULL || pOutput == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        size_t i;
+        FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+        pSession->dataOffset = 0;
+        pSession->numDataBytes = 0;
+        pOutput->fromConvertData.numBytes = 0;
+        status = FwdLockConv_Status_OK;
+
+        for (i = 0; i < numBytes; ++i) {
+            status = FwdLockConv_PushChar(pSession, ((char *)pBuffer)[i], pOutput);
+            if (status != FwdLockConv_Status_OK) {
+                break;
+            }
+        }
+        if (status == FwdLockConv_Status_OK) {
+            // Update the data signature.
+            HMAC_Update(&pSession->signingContext,
+                        &((unsigned char *)pOutput->fromConvertData.pBuffer)[pSession->dataOffset],
+                        pSession->numDataBytes);
+        } else if (status == FwdLockConv_Status_SyntaxError) {
+            pOutput->fromConvertData.errorPos = pSession->numCharsConsumed;
+        }
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput) {
+    FwdLockConv_Status_t status;
+    if (!FwdLockConv_IsValidSession(sessionId) || pOutput == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
+        free(pOutput->fromConvertData.pBuffer);
+        if (pSession->parserState != FwdLockConv_ParserState_Done) {
+            pOutput->fromCloseSession.errorPos = pSession->numCharsConsumed;
+            status = FwdLockConv_Status_SyntaxError;
+        } else {
+            // Finalize the data signature.
+            size_t signatureSize;
+            HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+                       &signatureSize);
+            if (signatureSize != SHA1_HASH_SIZE) {
+                status = FwdLockConv_Status_ProgramError;
+            } else {
+                // Calculate the header signature, which is a signature of the rest of the header
+                // including the data signature.
+                HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+                HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+                HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->contentType.ptr,
+                            pSession->contentType.length);
+                HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+                            pSession->encryptedSessionKeyLength);
+                HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures,
+                            signatureSize);
+                HMAC_Final(&pSession->signingContext, &pOutput->fromCloseSession.
+                           signatures[signatureSize], &signatureSize);
+                if (signatureSize != SHA1_HASH_SIZE) {
+                    status = FwdLockConv_Status_ProgramError;
+                } else {
+                    pOutput->fromCloseSession.fileOffset = TOP_HEADER_SIZE +
+                            pSession->contentType.length + pSession->encryptedSessionKeyLength;
+                    status = FwdLockConv_Status_OK;
+                }
+            }
+            pOutput->fromCloseSession.errorPos = INVALID_OFFSET;
+        }
+        free(pSession->mimeHeaderName.ptr);
+        free(pSession->contentType.ptr);
+        free(pSession->pEncryptedSessionKey);
+        HMAC_CTX_cleanup(&pSession->signingContext);
+        FwdLockConv_ReleaseSession(sessionId);
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+                                                 FwdLockConv_ReadFunc_t *fpReadFunc,
+                                                 int outputFileDesc,
+                                                 FwdLockConv_WriteFunc_t *fpWriteFunc,
+                                                 FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+                                                 off64_t *pErrorPos) {
+    FwdLockConv_Status_t status;
+    if (pErrorPos != NULL) {
+        *pErrorPos = INVALID_OFFSET;
+    }
+    if (fpReadFunc == NULL || fpWriteFunc == NULL || fpLSeekFunc == NULL || inputFileDesc < 0 ||
+        outputFileDesc < 0) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        char *pReadBuffer = malloc(READ_BUFFER_SIZE);
+        if (pReadBuffer == NULL) {
+            status = FwdLockConv_Status_OutOfMemory;
+        } else {
+            int sessionId;
+            FwdLockConv_Output_t output;
+            status = FwdLockConv_OpenSession(&sessionId, &output);
+            if (status == FwdLockConv_Status_OK) {
+                ssize_t numBytesRead;
+                FwdLockConv_Status_t closeStatus;
+                while ((numBytesRead =
+                        fpReadFunc(inputFileDesc, pReadBuffer, READ_BUFFER_SIZE)) > 0) {
+                    status = FwdLockConv_ConvertData(sessionId, pReadBuffer, (size_t)numBytesRead,
+                                                     &output);
+                    if (status == FwdLockConv_Status_OK) {
+                        if (output.fromConvertData.pBuffer != NULL &&
+                            output.fromConvertData.numBytes > 0) {
+                            ssize_t numBytesWritten = fpWriteFunc(outputFileDesc,
+                                                                  output.fromConvertData.pBuffer,
+                                                                  output.fromConvertData.numBytes);
+                            if (numBytesWritten != (ssize_t)output.fromConvertData.numBytes) {
+                                status = FwdLockConv_Status_FileWriteError;
+                                break;
+                            }
+                        }
+                    } else {
+                        if (status == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+                            *pErrorPos = output.fromConvertData.errorPos;
+                        }
+                        break;
+                    }
+                } // end while
+                if (numBytesRead < 0) {
+                    status = FwdLockConv_Status_FileReadError;
+                }
+                closeStatus = FwdLockConv_CloseSession(sessionId, &output);
+                if (status == FwdLockConv_Status_OK) {
+                    if (closeStatus != FwdLockConv_Status_OK) {
+                        if (closeStatus == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
+                            *pErrorPos = output.fromCloseSession.errorPos;
+                        }
+                        status = closeStatus;
+                    } else if (fpLSeekFunc(outputFileDesc, output.fromCloseSession.fileOffset,
+                                           SEEK_SET) < 0) {
+                        status = FwdLockConv_Status_FileSeekError;
+                    } else if (fpWriteFunc(outputFileDesc, output.fromCloseSession.signatures,
+                                           FWD_LOCK_SIGNATURES_SIZE) != FWD_LOCK_SIGNATURES_SIZE) {
+                        status = FwdLockConv_Status_FileWriteError;
+                    }
+                }
+            }
+            free(pReadBuffer);
+        }
+    }
+    return status;
+}
+
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+                                             const char *pOutputFilename,
+                                             off64_t *pErrorPos) {
+    FwdLockConv_Status_t status;
+    if (pErrorPos != NULL) {
+        *pErrorPos = INVALID_OFFSET;
+    }
+    if (pInputFilename == NULL || pOutputFilename == NULL) {
+        status = FwdLockConv_Status_InvalidArgument;
+    } else {
+        int inputFileDesc = open(pInputFilename, O_RDONLY);
+        if (inputFileDesc < 0) {
+            status = FwdLockConv_Status_FileNotFound;
+        } else {
+            int outputFileDesc = open(pOutputFilename, O_CREAT | O_TRUNC | O_WRONLY,
+                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+            if (outputFileDesc < 0) {
+                status = FwdLockConv_Status_FileCreationFailed;
+            } else {
+                status = FwdLockConv_ConvertOpenFile(inputFileDesc, read, outputFileDesc, write,
+                                                     lseek64, pErrorPos);
+                if (close(outputFileDesc) == 0 && status != FwdLockConv_Status_OK) {
+                    remove(pOutputFilename);
+                }
+            }
+            (void)close(inputFileDesc);
+        }
+    }
+    return status;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
new file mode 100644
index 0000000..e20c0c3
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKCONV_H__
+#define __FWDLOCKCONV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * The size of the data and header signatures combined. The signatures are adjacent to each other in
+ * the produced output file.
+ */
+#define FWD_LOCK_SIGNATURES_SIZE (2 * 20)
+
+/**
+ * Data type for the output from FwdLockConv_ConvertData.
+ */
+typedef struct FwdLockConv_ConvertData_Output {
+    /// The converted data.
+    void *pBuffer;
+
+    /// The size of the converted data.
+    size_t numBytes;
+
+    /// The file position where the error occurred, in the case of a syntax error.
+    off64_t errorPos;
+} FwdLockConv_ConvertData_Output_t;
+
+/**
+ * Data type for the output from FwdLockConv_CloseSession.
+ */
+typedef struct FwdLockConv_CloseSession_Output {
+    /// The final set of signatures.
+    unsigned char signatures[FWD_LOCK_SIGNATURES_SIZE];
+
+    /// The offset in the produced output file where the signatures are located.
+    off64_t fileOffset;
+
+    /// The file position where the error occurred, in the case of a syntax error.
+    off64_t errorPos;
+} FwdLockConv_CloseSession_Output_t;
+
+/**
+ * Data type for the output from the conversion process.
+ */
+typedef union FwdLockConv_Output {
+    FwdLockConv_ConvertData_Output_t fromConvertData;
+    FwdLockConv_CloseSession_Output_t fromCloseSession;
+} FwdLockConv_Output_t;
+
+/**
+ * Data type for the Posix-style read function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for reading.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_ReadFunc_t(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style write function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] pBuffer A reference to the buffer containing the data to be written.
+ * @param[in] numBytes The number of bytes to write.
+ *
+ * @return The number of bytes written.
+ * @retval -1 Failure.
+ */
+typedef ssize_t FwdLockConv_WriteFunc_t(int fileDesc, const void *pBuffer, size_t numBytes);
+
+/**
+ * Data type for the Posix-style lseek function used by the converter in pull mode.
+ *
+ * @param[in] fileDesc The file descriptor of a file opened for writing.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+typedef off64_t FwdLockConv_LSeekFunc_t(int fileDesc, off64_t offset, int whence);
+
+/**
+ * The status codes returned by the converter functions.
+ */
+typedef enum FwdLockConv_Status {
+    /// The operation was successful.
+    FwdLockConv_Status_OK = 0,
+
+    /// An actual argument to the function is invalid (a program error on the caller's part).
+    FwdLockConv_Status_InvalidArgument = 1,
+
+    /// There is not enough free dynamic memory to complete the operation.
+    FwdLockConv_Status_OutOfMemory = 2,
+
+    /// An error occurred while opening the input file.
+    FwdLockConv_Status_FileNotFound = 3,
+
+    /// An error occurred while creating the output file.
+    FwdLockConv_Status_FileCreationFailed = 4,
+
+    /// An error occurred while reading from the input file.
+    FwdLockConv_Status_FileReadError = 5,
+
+    /// An error occurred while writing to the output file.
+    FwdLockConv_Status_FileWriteError = 6,
+
+    /// An error occurred while seeking to a new file position within the output file.
+    FwdLockConv_Status_FileSeekError = 7,
+
+    /// The input file is not a syntactically correct OMA DRM v1 Forward Lock file.
+    FwdLockConv_Status_SyntaxError = 8,
+
+    /// Support for this DRM file format has been disabled in the current product configuration.
+    FwdLockConv_Status_UnsupportedFileFormat = 9,
+
+    /// The content transfer encoding is not one of "binary", "base64", "7bit", or "8bit"
+    /// (case-insensitive).
+    FwdLockConv_Status_UnsupportedContentTransferEncoding = 10,
+
+    /// The generation of a random number failed.
+    FwdLockConv_Status_RandomNumberGenerationFailed = 11,
+
+    /// Key encryption failed.
+    FwdLockConv_Status_KeyEncryptionFailed = 12,
+
+    /// The calculation of a keyed hash for integrity protection failed.
+    FwdLockConv_Status_IntegrityProtectionFailed = 13,
+
+    /// There are too many ongoing sessions for another one to be opened.
+    FwdLockConv_Status_TooManySessions = 14,
+
+    /// An unexpected error occurred.
+    FwdLockConv_Status_ProgramError = 15
+} FwdLockConv_Status_t;
+
+/**
+ * Opens a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock file
+ * format.
+ *
+ * @param[out] pSessionId The session ID.
+ * @param[out] pOutput The output from the conversion process (initialized).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Supplies the converter with data to convert. The caller is expected to write the converted data
+ * to file. Can be called an arbitrary number of times.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in] pBuffer A reference to a buffer containing the data to convert.
+ * @param[in] numBytes The number of bytes to convert.
+ * @param[in,out] pOutput The output from the conversion process (allocated/reallocated).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
+                                             const void *pBuffer,
+                                             size_t numBytes,
+                                             FwdLockConv_Output_t *pOutput);
+
+/**
+ * Closes a session for converting an OMA DRM v1 Forward Lock file to the internal Forward Lock
+ * file format. The caller must update the produced output file at the indicated file offset with
+ * the final set of signatures.
+ *
+ * @param[in] sessionId The session ID.
+ * @param[in,out] pOutput The output from the conversion process (deallocated and overwritten).
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ */
+FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput);
+
+/**
+ * Converts an open OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull
+ * mode.
+ *
+ * @param[in] inputFileDesc The file descriptor of the open input file.
+ * @param[in] fpReadFunc A reference to a read function that can operate on the open input file.
+ * @param[in] outputFileDesc The file descriptor of the open output file.
+ * @param[in] fpWriteFunc A reference to a write function that can operate on the open output file.
+ * @param[in] fpLSeekFunc A reference to an lseek function that can operate on the open output file.
+ * @param[out] pErrorPos
+ *   The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
+                                                 FwdLockConv_ReadFunc_t *fpReadFunc,
+                                                 int outputFileDesc,
+                                                 FwdLockConv_WriteFunc_t *fpWriteFunc,
+                                                 FwdLockConv_LSeekFunc_t *fpLSeekFunc,
+                                                 off64_t *pErrorPos);
+
+/**
+ * Converts an OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull mode.
+ *
+ * @param[in] pInputFilename A reference to the input filename.
+ * @param[in] pOutputFilename A reference to the output filename.
+ * @param[out] pErrorPos
+ *   The file position where the error occurred, in the case of a syntax error. May be NULL.
+ *
+ * @return A status code.
+ * @retval FwdLockConv_Status_OK
+ * @retval FwdLockConv_Status_InvalidArgument
+ * @retval FwdLockConv_Status_OutOfMemory
+ * @retval FwdLockConv_Status_FileNotFound
+ * @retval FwdLockConv_Status_FileCreationFailed
+ * @retval FwdLockConv_Status_FileReadError
+ * @retval FwdLockConv_Status_FileWriteError
+ * @retval FwdLockConv_Status_FileSeekError
+ * @retval FwdLockConv_Status_SyntaxError
+ * @retval FwdLockConv_Status_UnsupportedFileFormat
+ * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
+ * @retval FwdLockConv_Status_RandomNumberGenerationFailed
+ * @retval FwdLockConv_Status_KeyEncryptionFailed
+ * @retval FwdLockConv_Status_DataEncryptionFailed
+ * @retval FwdLockConv_Status_IntegrityProtectionFailed
+ * @retval FwdLockConv_Status_TooManySessions
+ */
+FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
+                                             const char *pOutputFilename,
+                                             off64_t *pErrorPos);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKCONV_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
new file mode 100644
index 0000000..b625edf
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2010 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 := \
+    FwdLockFile.c
+
+LOCAL_C_INCLUDES := \
+    frameworks/base/drm/libdrmframework/plugins/forward-lock/internal-format/common \
+    external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := libcrypto
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_STATIC_LIBRARIES := libfwdlock-common
+
+LOCAL_MODULE := libfwdlock-decoder
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
new file mode 100644
index 0000000..98284e7
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+
+#include "FwdLockFile.h"
+#include "FwdLockGlue.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define INVALID_OFFSET ((off64_t)-1)
+
+#define INVALID_BLOCK_INDEX ((uint64_t)-1)
+
+#define MAX_NUM_SESSIONS 128
+
+#define KEY_SIZE AES_BLOCK_SIZE
+#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
+
+#define SHA1_HASH_SIZE 20
+#define SHA1_BLOCK_SIZE 64
+
+#define FWD_LOCK_VERSION 0
+#define FWD_LOCK_SUBFORMAT 0
+#define USAGE_RESTRICTION_FLAGS 0
+#define CONTENT_TYPE_LENGTH_POS 7
+#define TOP_HEADER_SIZE 8
+
+#define SIG_CALC_BUFFER_SIZE (16 * SHA1_BLOCK_SIZE)
+
+/**
+ * Data type for the per-file state information needed by the decoder.
+ */
+typedef struct FwdLockFile_Session {
+    int fileDesc;
+    unsigned char topHeader[TOP_HEADER_SIZE];
+    char *pContentType;
+    size_t contentTypeLength;
+    void *pEncryptedSessionKey;
+    size_t encryptedSessionKeyLength;
+    unsigned char dataSignature[SHA1_HASH_SIZE];
+    unsigned char headerSignature[SHA1_HASH_SIZE];
+    off64_t dataOffset;
+    off64_t filePos;
+    AES_KEY encryptionRoundKeys;
+    HMAC_CTX signingContext;
+    unsigned char keyStream[AES_BLOCK_SIZE];
+    uint64_t blockIndex;
+} FwdLockFile_Session_t;
+
+static FwdLockFile_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
+
+static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const unsigned char topHeaderTemplate[] =
+    { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
+
+/**
+ * Acquires an unused file session for the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_AcquireSession(int fileDesc) {
+    int sessionId = -1;
+    if (fileDesc < 0) {
+        errno = EBADF;
+    } else {
+        int i;
+        pthread_mutex_lock(&sessionAcquisitionMutex);
+        for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+            int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+            if (sessionPtrs[candidateSessionId] == NULL) {
+                sessionPtrs[candidateSessionId] = malloc(sizeof **sessionPtrs);
+                if (sessionPtrs[candidateSessionId] != NULL) {
+                    sessionPtrs[candidateSessionId]->fileDesc = fileDesc;
+                    sessionPtrs[candidateSessionId]->pContentType = NULL;
+                    sessionPtrs[candidateSessionId]->pEncryptedSessionKey = NULL;
+                    sessionId = candidateSessionId;
+                }
+                break;
+            }
+        }
+        pthread_mutex_unlock(&sessionAcquisitionMutex);
+        if (i == MAX_NUM_SESSIONS) {
+            errno = ENFILE;
+        }
+    }
+    return sessionId;
+}
+
+/**
+ * Finds the file session associated to the given file descriptor.
+ *
+ * @param[in] fileDesc A file descriptor.
+ *
+ * @return A session ID.
+ */
+static int FwdLockFile_FindSession(int fileDesc) {
+    int sessionId = -1;
+    if (fileDesc < 0) {
+        errno = EBADF;
+    } else {
+        int i;
+        pthread_mutex_lock(&sessionAcquisitionMutex);
+        for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
+            int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
+            if (sessionPtrs[candidateSessionId] != NULL &&
+                sessionPtrs[candidateSessionId]->fileDesc == fileDesc) {
+                sessionId = candidateSessionId;
+                break;
+            }
+        }
+        pthread_mutex_unlock(&sessionAcquisitionMutex);
+        if (i == MAX_NUM_SESSIONS) {
+            errno = EBADF;
+        }
+    }
+    return sessionId;
+}
+
+/**
+ * Releases a file session.
+ *
+ * @param[in] sessionID A session ID.
+ */
+static void FwdLockFile_ReleaseSession(int sessionId) {
+    pthread_mutex_lock(&sessionAcquisitionMutex);
+    assert(0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL);
+    free(sessionPtrs[sessionId]->pContentType);
+    free(sessionPtrs[sessionId]->pEncryptedSessionKey);
+    memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
+    free(sessionPtrs[sessionId]);
+    sessionPtrs[sessionId] = NULL;
+    pthread_mutex_unlock(&sessionAcquisitionMutex);
+}
+
+/**
+ * Derives keys for encryption and signing from the encrypted session key.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ *
+ * @return A Boolean value indicating whether key derivation was successful.
+ */
+static int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) {
+    int result;
+    struct FwdLockFile_DeriveKeys_Data {
+        AES_KEY sessionRoundKeys;
+        unsigned char value[KEY_SIZE];
+        unsigned char key[KEY_SIZE];
+    } *pData = malloc(sizeof *pData);
+    if (pData == NULL) {
+        result = FALSE;
+    } else {
+        result = FwdLockGlue_DecryptKey(pSession->pEncryptedSessionKey,
+                                        pSession->encryptedSessionKeyLength, pData->key, KEY_SIZE);
+        if (result) {
+            if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, &pData->sessionRoundKeys) != 0) {
+                result = FALSE;
+            } else {
+                // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
+                memset(pData->value, 0, KEY_SIZE);
+                AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+                if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
+                                        &pSession->encryptionRoundKeys) != 0) {
+                    result = FALSE;
+                } else {
+                    // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
+                    ++pData->value[0];
+                    AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
+                    HMAC_CTX_init(&pSession->signingContext);
+                    HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
+                }
+            }
+        }
+        if (!result) {
+            errno = ENOSYS;
+        }
+        memset(pData, 0, sizeof pData); // Zero out key data.
+        free(pData);
+    }
+    return result;
+}
+
+/**
+ * Calculates the counter, treated as a 16-byte little-endian number, used to generate the keystream
+ * for the given block.
+ *
+ * @param[in] pNonce A reference to the nonce.
+ * @param[in] blockIndex The index number of the block.
+ * @param[out] pCounter A reference to the counter.
+ */
+static void FwdLockFile_CalculateCounter(const unsigned char *pNonce,
+                                         uint64_t blockIndex,
+                                         unsigned char *pCounter) {
+    unsigned char carry = 0;
+    size_t i = 0;
+    for (; i < sizeof blockIndex; ++i) {
+        unsigned char part = pNonce[i] + (unsigned char)(blockIndex >> (i * CHAR_BIT));
+        pCounter[i] = part + carry;
+        carry = (part < pNonce[i] || pCounter[i] < part) ? 1 : 0;
+    }
+    for (; i < AES_BLOCK_SIZE; ++i) {
+        pCounter[i] = pNonce[i] + carry;
+        carry = (pCounter[i] < pNonce[i]) ? 1 : 0;
+    }
+}
+
+/**
+ * Decrypts the byte at the current file position using AES-128-CTR. In CTR (or "counter") mode,
+ * encryption and decryption are performed using the same algorithm.
+ *
+ * @param[in,out] pSession A reference to a file session.
+ * @param[in] pByte The byte to decrypt.
+ */
+void FwdLockFile_DecryptByte(FwdLockFile_Session_t * pSession, unsigned char *pByte) {
+    uint64_t blockIndex = pSession->filePos / AES_BLOCK_SIZE;
+    uint64_t blockOffset = pSession->filePos % AES_BLOCK_SIZE;
+    if (blockIndex != pSession->blockIndex) {
+        // The first 16 bytes of the encrypted session key is used as the nonce.
+        unsigned char counter[AES_BLOCK_SIZE];
+        FwdLockFile_CalculateCounter(pSession->pEncryptedSessionKey, blockIndex, counter);
+        AES_encrypt(counter, pSession->keyStream, &pSession->encryptionRoundKeys);
+        pSession->blockIndex = blockIndex;
+    }
+    *pByte ^= pSession->keyStream[blockOffset];
+}
+
+int FwdLockFile_attach(int fileDesc) {
+    int sessionId = FwdLockFile_AcquireSession(fileDesc);
+    if (sessionId >= 0) {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        int isSuccess = FALSE;
+        if (read(fileDesc, pSession->topHeader, TOP_HEADER_SIZE) == TOP_HEADER_SIZE &&
+                memcmp(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate) == 0) {
+            pSession->contentTypeLength = pSession->topHeader[CONTENT_TYPE_LENGTH_POS];
+            assert(pSession->contentTypeLength <= UCHAR_MAX); // Untaint scalar for code checkers.
+            pSession->pContentType = malloc(pSession->contentTypeLength + 1);
+            if (pSession->pContentType != NULL &&
+                    read(fileDesc, pSession->pContentType, pSession->contentTypeLength) ==
+                            (ssize_t)pSession->contentTypeLength) {
+                pSession->pContentType[pSession->contentTypeLength] = '\0';
+                pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
+                pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
+                if (pSession->pEncryptedSessionKey != NULL &&
+                        read(fileDesc, pSession->pEncryptedSessionKey,
+                             pSession->encryptedSessionKeyLength) ==
+                                (ssize_t)pSession->encryptedSessionKeyLength &&
+                        read(fileDesc, pSession->dataSignature, SHA1_HASH_SIZE) ==
+                                SHA1_HASH_SIZE &&
+                        read(fileDesc, pSession->headerSignature, SHA1_HASH_SIZE) ==
+                                SHA1_HASH_SIZE) {
+                    isSuccess = FwdLockFile_DeriveKeys(pSession);
+                }
+            }
+        }
+        if (isSuccess) {
+            pSession->dataOffset = pSession->contentTypeLength +
+                    pSession->encryptedSessionKeyLength + TOP_HEADER_SIZE + 2 * SHA1_HASH_SIZE;
+            pSession->filePos = 0;
+            pSession->blockIndex = INVALID_BLOCK_INDEX;
+        } else {
+            FwdLockFile_ReleaseSession(sessionId);
+            sessionId = -1;
+        }
+    }
+    return (sessionId >= 0) ? 0 : -1;
+}
+
+int FwdLockFile_open(const char *pFilename) {
+    int fileDesc = open(pFilename, O_RDONLY);
+    if (fileDesc >= 0 && FwdLockFile_attach(fileDesc) < 0) {
+        (void)close(fileDesc);
+        fileDesc = -1;
+    }
+    return fileDesc;
+}
+
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes) {
+    ssize_t numBytesRead;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        numBytesRead = -1;
+    } else {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        ssize_t i;
+        numBytesRead = read(pSession->fileDesc, pBuffer, numBytes);
+        for (i = 0; i < numBytesRead; ++i) {
+            FwdLockFile_DecryptByte(pSession, &((unsigned char *)pBuffer)[i]);
+            ++pSession->filePos;
+        }
+    }
+    return numBytesRead;
+}
+
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence) {
+    off64_t newFilePos;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        newFilePos = INVALID_OFFSET;
+    } else {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        switch (whence) {
+        case SEEK_SET:
+            newFilePos = lseek64(pSession->fileDesc, pSession->dataOffset + offset, whence);
+            break;
+        case SEEK_CUR:
+        case SEEK_END:
+            newFilePos = lseek64(pSession->fileDesc, offset, whence);
+            break;
+        default:
+            errno = EINVAL;
+            newFilePos = INVALID_OFFSET;
+            break;
+        }
+        if (newFilePos != INVALID_OFFSET) {
+            if (newFilePos < pSession->dataOffset) {
+                // The new file position is illegal for an internal Forward Lock file. Restore the
+                // original file position.
+                (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+                              SEEK_SET);
+                errno = EINVAL;
+                newFilePos = INVALID_OFFSET;
+            } else {
+                // The return value should be the file position that lseek64() would have returned
+                // for the embedded content file.
+                pSession->filePos = newFilePos - pSession->dataOffset;
+                newFilePos = pSession->filePos;
+            }
+        }
+    }
+    return newFilePos;
+}
+
+int FwdLockFile_detach(int fileDesc) {
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        return -1;
+    }
+    HMAC_CTX_cleanup(&sessionPtrs[sessionId]->signingContext);
+    FwdLockFile_ReleaseSession(sessionId);
+    return 0;
+}
+
+int FwdLockFile_close(int fileDesc) {
+    return (FwdLockFile_detach(fileDesc) == 0) ? close(fileDesc) : -1;
+}
+
+int FwdLockFile_CheckDataIntegrity(int fileDesc) {
+    int result;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        result = FALSE;
+    } else {
+        struct FwdLockFile_CheckDataIntegrity_Data {
+            unsigned char signature[SHA1_HASH_SIZE];
+            unsigned char buffer[SIG_CALC_BUFFER_SIZE];
+        } *pData = malloc(sizeof *pData);
+        if (pData == NULL) {
+            result = FALSE;
+        } else {
+            FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+            if (lseek64(pSession->fileDesc, pSession->dataOffset, SEEK_SET) !=
+                    pSession->dataOffset) {
+                result = FALSE;
+            } else {
+                ssize_t numBytesRead;
+                size_t signatureSize = SHA1_HASH_SIZE;
+                while ((numBytesRead =
+                        read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) {
+                    HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead);
+                }
+                if (numBytesRead < 0) {
+                    result = FALSE;
+                } else {
+                    HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize);
+                    assert(signatureSize == SHA1_HASH_SIZE);
+                    result = memcmp(pData->signature, pSession->dataSignature, signatureSize) == 0;
+                }
+                HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+                (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
+                              SEEK_SET);
+            }
+            free(pData);
+        }
+    }
+    return result;
+}
+
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc) {
+    int result;
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        result = FALSE;
+    } else {
+        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
+        unsigned char signature[SHA1_HASH_SIZE];
+        size_t signatureSize = SHA1_HASH_SIZE;
+        HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
+        HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType,
+                    pSession->contentTypeLength);
+        HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
+                    pSession->encryptedSessionKeyLength);
+        HMAC_Update(&pSession->signingContext, pSession->dataSignature, signatureSize);
+        HMAC_Final(&pSession->signingContext, signature, &signatureSize);
+        assert(signatureSize == SHA1_HASH_SIZE);
+        result = memcmp(signature, pSession->headerSignature, signatureSize) == 0;
+        HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
+    }
+    return result;
+}
+
+int FwdLockFile_CheckIntegrity(int fileDesc) {
+    return FwdLockFile_CheckHeaderIntegrity(fileDesc) && FwdLockFile_CheckDataIntegrity(fileDesc);
+}
+
+const char *FwdLockFile_GetContentType(int fileDesc) {
+    int sessionId = FwdLockFile_FindSession(fileDesc);
+    if (sessionId < 0) {
+        return NULL;
+    }
+    return sessionPtrs[sessionId]->pContentType;
+}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
new file mode 100644
index 0000000..fc64050
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 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 __FWDLOCKFILE_H__
+#define __FWDLOCKFILE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/**
+ * Attaches to an open Forward Lock file. The file position is assumed to be at the beginning of the
+ * file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_attach(int fileDesc);
+
+/**
+ * Opens a Forward Lock file for reading.
+ *
+ * @param[in] pFilename A reference to a filename.
+ *
+ * @return A file descriptor.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_open(const char *pFilename);
+
+/**
+ * Reads the specified number of bytes from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[out] pBuffer A reference to the buffer that should receive the read data.
+ * @param[in] numBytes The number of bytes to read.
+ *
+ * @return The number of bytes read.
+ * @retval -1 Failure.
+ */
+ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes);
+
+/**
+ * Updates the file position within an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ * @param[in] offset The offset with which to update the file position.
+ * @param[in] whence One of SEEK_SET, SEEK_CUR, and SEEK_END.
+ *
+ * @return The new file position.
+ * @retval ((off64_t)-1) Failure.
+ */
+off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence);
+
+/**
+ * Detaches from an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_detach(int fileDesc);
+
+/**
+ * Closes an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A status code.
+ * @retval 0 Success.
+ * @retval -1 Failure.
+ */
+int FwdLockFile_close(int fileDesc);
+
+/**
+ * Checks the data integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckDataIntegrity(int fileDesc);
+
+/**
+ * Checks the header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckHeaderIntegrity(int fileDesc);
+
+/**
+ * Checks both the data and header integrity of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return A Boolean value indicating whether the integrity check was successful.
+ */
+int FwdLockFile_CheckIntegrity(int fileDesc);
+
+/**
+ * Returns the content type of an open Forward Lock file.
+ *
+ * @param[in] fileDesc The file descriptor of an open Forward Lock file.
+ *
+ * @return
+ *   A reference to the content type. The reference remains valid as long as the file is kept open.
+ */
+const char *FwdLockFile_GetContentType(int fileDesc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __FWDLOCKFILE_H__
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
new file mode 100755
index 0000000..8f95cd2
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
@@ -0,0 +1,1039 @@
+<html>
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+<meta name=Generator content="Microsoft Word 12 (filtered)">
+<title>Forward Lock Converter and Decoder</title>
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+	{font-family:SimSun;
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+@font-face
+	{font-family:"Cambria Math";
+	panose-1:2 4 5 3 5 4 6 3 2 4;}
+@font-face
+	{font-family:Tahoma;
+	panose-1:2 11 6 4 3 5 4 4 2 4;}
+@font-face
+	{font-family:"Lucida Console","DejaVu Sans Mono";
+	panose-1:2 11 6 9 4 5 4 2 2 4;}
+@font-face
+	{font-family:"\@SimSun";
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+h1
+	{margin-right:0cm;
+	margin-left:21.6pt;
+	text-indent:-21.6pt;
+	page-break-after:avoid;
+	font-size:16.0pt;
+	font-family:"Arial","sans-serif";}
+h2
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:28.8pt;
+	text-indent:-28.8pt;
+	page-break-after:avoid;
+	font-size:14.0pt;
+	font-family:"Arial","sans-serif";
+	font-style:italic;}
+h3
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:36.0pt;
+	text-indent:-36.0pt;
+	page-break-after:avoid;
+	font-size:13.0pt;
+	font-family:"Arial","sans-serif";}
+h4
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:43.2pt;
+	text-indent:-43.2pt;
+	page-break-after:avoid;
+	font-size:14.0pt;
+	font-family:"Times New Roman","serif";}
+h5
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:50.4pt;
+	text-indent:-50.4pt;
+	font-size:13.0pt;
+	font-family:"Times New Roman","serif";
+	font-style:italic;}
+h6
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:57.6pt;
+	text-indent:-57.6pt;
+	font-size:11.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoHeading7, li.MsoHeading7, div.MsoHeading7
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:64.8pt;
+	text-indent:-64.8pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoHeading8, li.MsoHeading8, div.MsoHeading8
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:72.0pt;
+	text-indent:-72.0pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";
+	font-style:italic;}
+p.MsoHeading9, li.MsoHeading9, div.MsoHeading9
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:3.0pt;
+	margin-left:79.2pt;
+	text-indent:-79.2pt;
+	font-size:11.0pt;
+	font-family:"Arial","sans-serif";}
+p.MsoToc1, li.MsoToc1, div.MsoToc1
+	{margin-top:6.0pt;
+	margin-right:0cm;
+	margin-bottom:6.0pt;
+	margin-left:0cm;
+	line-height:150%;
+	font-size:10.5pt;
+	font-family:"Times New Roman","serif";
+	text-transform:uppercase;
+	font-weight:bold;}
+p.MsoToc2, li.MsoToc2, div.MsoToc2
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:12.0pt;
+	margin-bottom:.0001pt;
+	line-height:150%;
+	font-size:10.5pt;
+	font-family:"Times New Roman","serif";
+	font-variant:small-caps;}
+p.MsoToc3, li.MsoToc3, div.MsoToc3
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:24.0pt;
+	margin-bottom:.0001pt;
+	line-height:150%;
+	font-size:10.5pt;
+	font-family:"Times New Roman","serif";
+	font-style:italic;}
+p.MsoToc4, li.MsoToc4, div.MsoToc4
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:36.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc5, li.MsoToc5, div.MsoToc5
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:48.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc6, li.MsoToc6, div.MsoToc6
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:60.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc7, li.MsoToc7, div.MsoToc7
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:72.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc8, li.MsoToc8, div.MsoToc8
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:84.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoToc9, li.MsoToc9, div.MsoToc9
+	{margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:0cm;
+	margin-left:96.0pt;
+	margin-bottom:.0001pt;
+	font-size:9.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoFootnoteText, li.MsoFootnoteText, div.MsoFootnoteText
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:10.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoHeader, li.MsoHeader, div.MsoHeader
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoFooter, li.MsoFooter, div.MsoFooter
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoCaption, li.MsoCaption, div.MsoCaption
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:11.0pt;
+	font-family:"Times New Roman","serif";
+	font-weight:bold;}
+span.MsoFootnoteReference
+	{vertical-align:super;}
+p.MsoTitle, li.MsoTitle, div.MsoTitle
+	{margin-top:12.0pt;
+	margin-right:0cm;
+	margin-bottom:120.0pt;
+	margin-left:0cm;
+	text-align:center;
+	font-size:16.0pt;
+	font-family:"Arial","sans-serif";
+	font-weight:bold;}
+p.MsoBodyText, li.MsoBodyText, div.MsoBodyText
+	{mso-style-link:"Body Text Char";
+	margin-top:0cm;
+	margin-right:0cm;
+	margin-bottom:6.0pt;
+	margin-left:0cm;
+	font-size:12.0pt;
+	font-family:"Times New Roman","serif";}
+a:link, span.MsoHyperlink
+	{color:blue;
+	text-decoration:underline;}
+a:visited, span.MsoHyperlinkFollowed
+	{color:purple;
+	text-decoration:underline;}
+p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
+	{margin:0cm;
+	margin-bottom:.0001pt;
+	font-size:8.0pt;
+	font-family:"Tahoma","sans-serif";}
+span.BodyTextChar
+	{mso-style-name:"Body Text Char";
+	mso-style-link:"Body Text";}
+ /* Page Definitions */
+ @page WordSection1
+	{size:595.45pt 841.7pt;
+	margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection1
+	{page:WordSection1;}
+@page WordSection2
+	{size:595.45pt 841.7pt;
+	margin:72.0pt 90.0pt 72.0pt 90.0pt;}
+div.WordSection2
+	{page:WordSection2;}
+ /* List Definitions */
+ ol
+	{margin-bottom:0cm;}
+ul
+	{margin-bottom:0cm;}
+-->
+</style>
+
+</head>
+
+<body lang=EN-US link=blue vlink=purple>
+
+<div class=WordSection1>
+
+<p class=MsoTitle>Forward Lock Converter And Decoder</p>
+
+<p class=MsoToc1><span
+class=MsoHyperlink><a href="#_Toc276471422">1<span style='font-size:12.0pt;
+line-height:150%;color:windowtext;text-transform:none;font-weight:normal;
+text-decoration:none'>      </span>Introduction<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471423">2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Overview<span
+style='color:windowtext;display:none;text-decoration:none'>... </span><span
+style='color:windowtext;display:none;text-decoration:none'>3</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471424">3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Use Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471425">3.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Converter<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471426">3.1.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Convert Data (Push-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>4</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471427">3.1.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Convert File (Pull-Mode Conversion)<span
+style='color:windowtext;display:none;text-decoration:none'> </span><span
+style='color:windowtext;display:none;text-decoration:none'>6</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471428">3.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Decoder<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>7</span></a></span></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471429">3.2.1<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Check Integrity<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>8</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471430">3.2.2<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Get Content Type<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>9</span></a></span></p>
+
+<p class=MsoToc3><span class=MsoHyperlink><a href="#_Toc276471431">3.2.3<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;font-style:normal;
+text-decoration:none'>     </span>Decode File<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>10</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471432">4<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Definition of the
+Internal Forward Lock File Format<span style='color:windowtext;display:none;
+text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471433">4.1<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Key Derivation<span style='color:windowtext;display:none;
+text-decoration:none'>.. </span><span
+style='color:windowtext;display:none;text-decoration:none'>11</span></a></span></span></p>
+
+<p class=MsoToc2><span class=MsoHyperlink><span style='font-variant:normal !important;
+text-transform:uppercase'><a href="#_Toc276471434">4.2<span style='font-size:
+12.0pt;line-height:150%;color:windowtext;text-transform:none;text-decoration:
+none'>        </span>Calculation of the Counters<span style='color:windowtext;
+display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471435">5<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>Unit Test Cases<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoToc1><span class=MsoHyperlink><a href="#_Toc276471436">6<span
+style='font-size:12.0pt;line-height:150%;color:windowtext;text-transform:none;
+font-weight:normal;text-decoration:none'>      </span>References<span
+style='color:windowtext;display:none;text-decoration:none'>. </span><span
+style='color:windowtext;display:none;text-decoration:none'>12</span></a></span></p>
+
+<p class=MsoBodyText></p>
+
+</div>
+
+<span style='font-size:12.0pt;font-family:"Times New Roman","serif"'><br
+clear=all style='page-break-before:right'>
+</span>
+
+<div class=WordSection2>
+
+<h1><a name="_Toc276471422"></a><a name="_Ref263085474">1<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Introduction</a></h1>
+
+<p class=MsoBodyText>The internal Forward Lock file format is used for encrypting
+inherently unencrypted OMA DRM version 1 Forward Lock and Combined Delivery
+files so they can be securely stored on externally accessible file system partitions
+such as memory stick.</p>
+
+<p class=MsoBodyText>Our general strategy is to convert such <i>OMA DRM Message</i>
+(‘.dm’) files to internal Forward Lock (‘.fl’) files as soon as they are
+downloaded or otherwise transferred to the phone, and not actually provide any
+decoders for ‘.dm’ files.</p>
+
+<h1><a name="_Toc276471423">2<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Overview</a></h1>
+
+<p class=MsoBodyText>The <i>Forward Lock Converter</i> converts OMA DRM Message
+files to the internal file format. The <i>Forward Lock Decoder</i> provides a
+POSIX-level API for transparent reading and seeking through such a converted
+file as if it were unencrypted. The API also includes functions for checking a
+fileÂ’s integrity and getting the MIME type of its embedded content.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>The converter and decoder are
+built into two separate libraries, which share common code for random number
+generation and key encryption in a third library. For test purposes there is
+also a unit test application. See Figure 1.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=288 height=364
+src="images/image001.gif"></p>
+
+<p class=MsoCaption style='margin-top:12.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref262730885">Figure </a>1. Block diagram illustrating the dependencies between the executable modules.</p>
+
+<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h1><a name="_Toc276471424">3<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Use Cases</a></h1>
+
+<p class=MsoBodyText>This section describes all the use cases for the converter
+and decoder. It shows the sequence of API calls that should be used to solve
+these use cases.</p>
+
+<h2><a name="_Toc276471425">3.1<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Converter</a></h2>
+
+<p class=MsoBodyText>Through the converter API, conversion can be performed in one
+of two ways:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>Push-mode
+conversion</i> is when the client progressively feeds data to the converter as
+it arrives. This is appropriate when data arrives gradually in chunks, with
+idle time in between. Consequently, push mode is used for converting files
+being downloaded through HTTP. See section 3.1.1.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>Pull-mode
+conversion</i> is when the converter drives the process and consumes data from
+the client as it needs it. This is appropriate when the entire file to be
+converted is readily available. Hence, pull mode is used by the unit test application.
+See section 3.1.2.</p>
+
+<p class=MsoBodyText>Internally, pull-mode conversion is implemented in terms
+of the API for push-mode conversion.</p>
+
+<h3><a name="_Toc276471426"></a><a name="_Ref263085478">3.1.1<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Convert Data
+(Push-Mode Conversion)</a></h3>
+
+<p class=MsoBodyText>Push-mode conversion is performed as follows (see also Figure 2):</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_OpenSession</span>
+initializes the output parameter and returns a <i>session ID</i> to be used in
+subsequent calls to the API. The output parameter is a union of return values
+whose correct use at any given moment is determined by the API function last
+called.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_ConvertData</span>
+is called repeatedly until no more input data remains. Each call converts the
+maximum amount of data possible and writes it to the output buffer. The client then
+writes this data to file.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockConv_CloseSession</span>
+cleans up the session and deallocates the output buffer. If all has gone well, a
+two-part cryptographic signature of the output file is calculated. The client
+must go back and rewrite part of the file header with this updated signature
+information.</p>
+
+<p class=MsoBodyText>Every time a file is being converted, the converter calls <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetRandomNumber</span>
+to generate a new, unique session key. No two converted files look alike, even
+if the original files are the same.</p>
+
+<p class=MsoBodyText><b>Note:</b> The random bytes cannot come from any bare-minimum
+implementation of the C-library <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>rand</span>
+function—they must be cryptographically secure. Otherwise, security will be
+compromised.</p>
+
+<p class=MsoBodyText>The session key is encrypted and stored within the
+converted file. Key encryption is performed using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_EncryptKey</span>.
+These two functions, together with the corresponding decryption function (<span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>),
+are the integration points where an OEM manufacturer may implement their own
+key-encryption scheme.</p>
+
+<p class=MsoBodyText><b>Note:</b> The key-encryption key must be unique to each
+device; this is what makes the files forward lock–protected. Ideally, it should
+be derived from secret hardware parameters, but at the very least it should be
+persistent from one master reset to the next.</p>
+
+<div style='margin-bottom:24.0pt;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
+background:#F2F2F2'>
+
+<p class=MsoBodyText style='background:#F2F2F2;border:
+none;padding:0cm'><b>Note:</b> In the open-source implementation of the <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>libfwdlock-common</span>
+library, a random key-encryption key is generated and stored in plaintext in
+the file system, without being obfuscated in any way (doing so would be futile
+since the source code is openly available). This key must be kept secret from
+the user, and shouldnÂ’t be possible to extract through backup-and-restore
+functionality or the like. OEM manufacturers will probably want to implement a
+truly hardware-based device-unique key.</p>
+
+</div>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=563
+src="images/image002.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263085187">Figure </a>2. Converter UC: Convert Data.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471427"></a><a name="_Ref263163082">3.1.2<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Convert File
+(Pull-Mode Conversion)</a></h3>
+
+<p class=MsoBodyText>Pull-mode conversion is performed by calling <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span>
+with the filename, unless there is need for a specialized <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span> function, in
+which case <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>
+should be used directly instead. See Figure 3.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>Internally, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertFile</span>
+calls <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_ConvertOpenFile</span>.
+The latter then proceeds with the conversion using the push-mode API, acting as
+the client in the previous use case; see section 3.1.1.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=531 height=731
+src="images/image003.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263085208">Figure </a>3. Converter UC: Convert File.</p>
+
+<b><i><span style='font-size:14.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></i></b>
+
+<h2><a name="_Toc276471428">3.2<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Decoder</a></h2>
+
+<p class=MsoBodyText>The decoder API allows the client to do the following:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Check
+the integrity of an internal Forward Lock file, i.e., detect whether it has
+been manipulated in any way; see section 3.2.1.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Get
+the MIME type of the embedded content (the “original” MIME type before DRM protection
+was applied); see section 3.2.2.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Decode
+the file by random access, i.e., read and seek through it in an arbitrary
+manner; see section 3.2.3.</p>
+
+<p class=MsoBodyText>All subsequent operations on a file first require it to be
+opened. Opening a file returns a <i>file descriptor</i>—a handle to be used in
+these subsequent operations.</p>
+
+<p class=MsoBodyText>If the filename is known, an internal Forward Lock file
+can be opened using <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>.
+If only the file descriptor of an already open file is available, a decoding
+session can instead be initialized using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>.</p>
+
+<p class=MsoBodyText>Internally, <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> calls <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>. For efficiency
+reasons, <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>
+therefore assumes that the file position is at the beginning of the file when
+the function gets called. A client who calls it directly must make sure that
+this assumption holds.</p>
+
+<p class=MsoBodyText>When a file is being attached, the session key stored in
+the file during conversion is decrypted using <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_GetEncryptedKeyLength</span> and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockGlue_DecryptKey</span>,
+in order to set up for decoding and integrity checking.</p>
+
+<p class=MsoBodyText>For just getting the content type, however, retrieving the
+session key would strictly speaking not be necessary, so there is an
+opportunity here to optimize for that if it proves necessary later.</p>
+
+<p class=MsoBodyText>Symmetrical to <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span> and <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>, there are also functions
+for closing a file or detaching from it:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>If
+it was opened with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>
+it should be closed with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_close</span>.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>If
+it was attached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_attach</span>
+it should be detached with <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_detach</span>.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Ref263163099"></a><a name="_Toc276471429">3.2.1<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Check Integrity</a></h3>
+
+<p class=MsoBodyText>There are three methods for checking the integrity of an
+internal Forward Lock file, in whole or in part (see also Figure 4):</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>,
+which checks the integrity of the encrypted content data.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>,
+which checks the integrity of the file header, including the content type and
+other fields not currently supported but reserved for future use.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>3.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckIntegrity</span>,
+which internally calls first <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span>
+and then <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckHeaderIntegrity</span> is
+generally much faster than <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_CheckDataIntegrity</span>,
+whose running time is directly proportional to the size of the file.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=575
+src="images/image004.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263163308">Figure </a>4. Decoder UC: Check Integrity.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471430"></a><a name="_Ref263163117">3.2.2<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Get Content Type</a></h3>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'><span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_GetContentType</span> returns a
+read-only reference to an ASCII string containing the MIME type of the
+embedded content. This reference is valid as long as the file is kept open.
+Clients who need access to the content type after closing the file should make
+a copy of the string. See Figure 5 below.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=488
+src="images/image005.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263163392">Figure </a>5. Decoder UC: Get Content Type.</p>
+
+<b><span style='font-size:13.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h3><a name="_Toc276471431"></a><a name="_Ref263163137">3.2.3<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp; </span>Decode File</a></h3>
+
+<p class=MsoBodyText>After opening an internal Forward Lock file (or attaching
+to an already open one), it can be transparently read from as if it were
+unencrypted. Any number of calls to read data from the current file position or
+set it to a new one (which is what <span style='font-size:10.0pt;font-family:
+"Lucida Console","DejaVu Sans Mono"'>lseek</span> does) can be made in any order; this is what we
+call <i>random access</i>. See Figure 6.</p>
+
+<p class=MsoBodyText>The Forward Lock Decoder versions of the <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>read</span>, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>lseek</span>, and <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>close</span> functions
+have the exact same signatures as their POSIX counterparts. So, for example,
+the call <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_lseek(fd,
+0, SEEK_END)</span> returns the size of the embedded content data, i.e., the
+size of the original file before DRM protection.</p>
+
+<p class=MsoBodyText style='margin-bottom:24.0pt'>Moreover, <span
+style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>FwdLockFile_open</span>
+is like regular POSIX <span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>open</span>
+except it takes only the filename as a parameter—access is always read-only.</p>
+
+<p class=MsoBodyText style='page-break-after:avoid'><img width=543 height=522
+src="images/image006.gif"></p>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm'><a name="_Ref263166303">Figure </a>6. Decoder UC: Decode File.</p>
+
+<b><span style='font-size:16.0pt;font-family:"Arial","sans-serif"'><br
+clear=all style='page-break-before:always'>
+</span></b>
+
+<h1><a name="_Toc276471432">4<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Definition of the Internal Forward Lock File Format</a></h1>
+
+<p class=MsoBodyText style='margin-bottom:12.0pt'>The inner structure of an internal
+Forward Lock file is defined in Table 1 below.</p>
+
+<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0
+ style='border-collapse:collapse;border:none'>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><b>Offset [bytes]</b></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border:solid windowtext 1.0pt;
+  border-left:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><b>Size [bytes]</b></p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border:solid windowtext 1.0pt;
+  border-left:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><b>Description</b></p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>0</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>4</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The file signature (so-called
+  <i>magic number</i>): a four-character code consisting of the letters
+  F-W-L-K.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>4</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Version number (0 for the
+  first version).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>5</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Indicates the subformat:</p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x00 Forward Lock</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Combined Delivery</i></p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>6</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Usage restriction flags (prohibitions
+  against usage as ringtone or as wallpaper and screen saver). Also indicates
+  if the file is bound to a specific SIM card.</p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x00 No usage
+  restrictions</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x01 Ringtone usage
+  prohibited</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x02 Screen usage
+  prohibited</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'><i>0x80 Bound to SIM</i></p>
+  <p class=MsoNormal style='page-break-after:avoid'>(Any number of these may be
+  OR-ed together.)</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>7</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>1</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Length of the MIME content
+  type (<i>k</i>).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8</p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>k</i></p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The MIME content type
+  (ASCII-encoded without null-character termination).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>l </i>= 0 or 16</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>If the subformat is
+  Combined Delivery, this field contains the auto-generated content ID (16&nbsp;bytes).
+  If not, this field is zero-size.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>m </i>= 0 or 9</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>If the file is bound to a
+  specific SIM card, this field contains the 9-byte packed IMSI number. If not,
+  this field is zero-size.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>n</i> &#8805; 16</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The encrypted session key, the
+  first sixteen bytes of which are also used as the CTR-mode <i>nonce</i> (similar
+  to the CBC-mode <i>initialization vector</i>).</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>8+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>20</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Data signature—the SHA-1
+  HMAC of the encrypted content data.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>28+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>20</p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>Header signature—the SHA-1
+  HMAC of all the fields above, including the encrypted session key and data
+  signature.</p>
+  </td>
+ </tr>
+ <tr>
+  <td width=111 valign=top style='width:83.4pt;border:solid windowtext 1.0pt;
+  border-top:none;padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>48+<i>k</i>+<i>l</i>+<i>m</i>+<i>n</i></p>
+  </td>
+  <td width=96 valign=top style='width:72.0pt;border-top:none;border-left:none;
+  border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'><i>&lt;to the end of the
+  file&gt;</i></p>
+  </td>
+  <td width=361 valign=top style='width:270.85pt;border-top:none;border-left:
+  none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
+  padding:0cm 5.4pt 0cm 5.4pt'>
+  <p class=MsoNormal style='page-break-after:avoid'>The content data encrypted
+  using 128-bit AES in CTR mode.</p>
+  </td>
+ </tr>
+</table>
+
+<p class=MsoCaption style='margin-top:6.0pt;margin-right:0cm;margin-bottom:
+12.0pt;margin-left:0cm;page-break-after:avoid'><a name="_Ref151269206">Table </a>1. Definition of the fields of an internal Forward Lock file.</p>
+
+<p class=MsoBodyText>As of now, neither Combined Delivery nor usage
+restrictions (including SIM binding) are supported. These fields are reserved
+for future use.</p>
+
+<h2><a name="_Toc276471433">4.1<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Key Derivation</a></h2>
+
+<p class=MsoBodyText>The session key consists of sixteen bytes fetched from a
+cryptographically secure random number generator. From the session key, two
+separate keys are derived: one used for encryption, the other for signing.</p>
+
+<p class=MsoBodyText>The encryption key is the output from encrypting the
+16-byte all-zero input block {0, 0, Â…, 0} using 128-bit AES with the random session
+key as the key. The signing key is the output from encrypting the 16-byte input
+block {1, 0, Â…, 0} the same way. The keys so derived will be cryptographically
+independent from each other.</p>
+
+<p class=MsoBodyText>The session key is encrypted using a hardware-dependent
+key-encryption key unique to each device. The encrypted session key is stored
+inside the file, and its first sixteen bytes are also used as the <i>nonce</i>
+for the CTR-mode encryption of the content data.</p>
+
+<h2><a name="_Toc276471434">4.2<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;
+</span>Calculation of the Counters</a></h2>
+
+<p class=MsoBodyText>Using CTR (“counter”) mode, a block cipher such as AES can
+be turned into a stream cipher. The process of encryption and decryption is
+well defined in [1], except for the specifics of the calculation of the
+counters. For the internal Forward Lock file format, the counters are
+calculated as follows:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>The
+nonce is interpreted as a 128-bit unsigned integer in little-endian format.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>The
+zero-based block sequence number (also a little-endian unsigned integer) is
+added modulo 2<sup>128</sup> to the nonce to produce the counter for a given
+block.</p>
+
+<h1><a name="_Toc276471435">5<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>Unit Test Cases</a></h1>
+
+<p class=MsoBodyText>Unit test cases for the converter and decoder come in two
+varieties:</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>1.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>Black-box</i>
+test cases aim to verify that you get sensible results from malformed or
+“tricky” input data.</p>
+
+<p class=MsoBodyText style='margin-left:36.0pt;text-indent:-18.0pt'>2.<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>White-box</i>
+test cases aim to maximize code coverage using knowledge of code internals.</p>
+
+<p class=MsoBodyText>The black-box test cases are dependent on a specifically
+designed set of input files found in the <span style='font-size:10.0pt;
+font-family:"Lucida Console","DejaVu Sans Mono"'>forward-lock/internal-format/test/res</span>
+directory in the repository. For ‘tests’ variants of the software, these input
+files will be automatically installed in the file system image during build.</p>
+
+<p class=MsoBodyText>Run the test cases from the ADB shell command line as
+follows:</p>
+
+<p class=MsoNormal style='margin-top:0cm;margin-right:0cm;margin-bottom:6.0pt;
+margin-left:21.55pt'><span style='font-size:10.0pt;font-family:"Lucida Console","DejaVu Sans Mono"'>#
+gtest_fwdlock</span></p>
+
+<p class=MsoBodyText>If all black-box but no white-box test cases fail, the
+input files probably canÂ’t be found in the working directory.</p>
+
+<h1><a name="_Toc276471436">6<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span>References</a></h1>
+
+<p class=MsoBodyText style='margin-left:28.9pt;text-indent:-28.9pt'>[1]<span
+style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span><a
+href="http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf">Dworkin,
+Morris: “Recommendation for Block Cipher Modes of Operation—Methods and
+Techniques,” NIST Special Publication 800-38A, December 2001.</a><a
+name="_Ref151269073"></a></p>
+
+</div>
+
+</body>
+
+</html>
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
new file mode 100644
index 0000000..ee94513
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image001.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
new file mode 100644
index 0000000..8c12f46
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image002.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
new file mode 100644
index 0000000..9e019ca
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image003.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
new file mode 100644
index 0000000..cae1d01
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image004.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
new file mode 100644
index 0000000..0d87be9
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image005.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
new file mode 100644
index 0000000..9445b6b
--- /dev/null
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/images/image006.gif
Binary files differ
diff --git a/drm/libdrmframework/plugins/passthru/Android.mk b/drm/libdrmframework/plugins/passthru/Android.mk
new file mode 100644
index 0000000..7856d37
--- /dev/null
+++ b/drm/libdrmframework/plugins/passthru/Android.mk
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 2010 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:= \
+    src/DrmPassthruPlugIn.cpp
+
+LOCAL_MODULE := libdrmpassthruplugin
+
+LOCAL_STATIC_LIBRARIES := libdrmframeworkcommon
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_C_INCLUDES += \
+    $(TOP)/frameworks/base/drm/libdrmframework/include \
+    $(TOP)/frameworks/base/drm/libdrmframework/plugins/passthru/include \
+    $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \
+    $(TOP)/frameworks/base/include
+
+# 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/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
new file mode 100644
index 0000000..bbcd9ed
--- /dev/null
+++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 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_PASSTHRU_PLUGIN_H__
+#define __DRM_PASSTHRU_PLUGIN_H__
+
+#include <DrmEngineBase.h>
+
+namespace android {
+
+class DrmPassthruPlugIn : public DrmEngineBase {
+
+public:
+    DrmPassthruPlugIn();
+    virtual ~DrmPassthruPlugIn();
+
+protected:
+    DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action);
+
+    DrmMetadata* onGetMetadata(int uniqueId, const String8* path);
+
+    status_t onInitialize(int uniqueId);
+
+    status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener);
+
+    status_t onTerminate(int uniqueId);
+
+    bool onCanHandle(int uniqueId, const String8& path);
+
+    DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+
+    status_t onSaveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath);
+
+    DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+
+    String8 onGetOriginalMimeType(int uniqueId, const String8& path);
+
+    int onGetDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+
+    int onCheckRightsStatus(int uniqueId, const String8& path, int action);
+
+    status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+
+    status_t onSetPlaybackStatus(
+            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    bool onValidateAction(
+            int uniqueId, const String8& path, int action, const ActionDescription& description);
+
+    status_t onRemoveRights(int uniqueId, const String8& path);
+
+    status_t onRemoveAllRights(int uniqueId);
+
+    status_t onOpenConvertSession(int uniqueId, int convertId);
+
+    DrmConvertedStatus* onConvertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+
+    DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId);
+
+    DrmSupportInfo* onGetSupportInfo(int uniqueId);
+
+    status_t onOpenDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length);
+
+    status_t onOpenDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, const char* uri);
+
+    status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+
+    status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo);
+
+    status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+
+    status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+
+    ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset);
+
+private:
+    DecryptHandle* openDecryptSessionImpl();
+};
+
+};
+
+#endif /* __DRM_PASSTHRU_PLUGIN_H__ */
+
diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
new file mode 100644
index 0000000..dee1fdb
--- /dev/null
+++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2010 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 "DrmPassthruPlugIn"
+#include <utils/Log.h>
+
+#include <drm/DrmRights.h>
+#include <drm/DrmConstraints.h>
+#include <drm/DrmMetadata.h>
+#include <drm/DrmInfo.h>
+#include <drm/DrmInfoEvent.h>
+#include <drm/DrmInfoStatus.h>
+#include <drm/DrmConvertedStatus.h>
+#include <drm/DrmInfoRequest.h>
+#include <drm/DrmSupportInfo.h>
+#include <DrmPassthruPlugIn.h>
+
+using namespace android;
+
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" IDrmEngine* create() {
+    return new DrmPassthruPlugIn();
+}
+
+// This extern "C" is mandatory to be managed by TPlugInManager
+extern "C" void destroy(IDrmEngine* pPlugIn) {
+    delete pPlugIn;
+    pPlugIn = NULL;
+}
+
+DrmPassthruPlugIn::DrmPassthruPlugIn()
+    : DrmEngineBase() {
+
+}
+
+DrmPassthruPlugIn::~DrmPassthruPlugIn() {
+
+}
+
+DrmMetadata* DrmPassthruPlugIn::onGetMetadata(int uniqueId, const String8* path) {
+    return NULL;
+}
+
+DrmConstraints* DrmPassthruPlugIn::onGetConstraints(
+        int uniqueId, const String8* path, int action) {
+    LOGD("DrmPassthruPlugIn::onGetConstraints From Path: %d", uniqueId);
+    DrmConstraints* drmConstraints = new DrmConstraints();
+
+    String8 value("dummy_available_time");
+    char* charValue = NULL;
+    charValue = new char[value.length() + 1];
+    strncpy(charValue, value.string(), value.length());
+
+    //Just add dummy available time for verification
+    drmConstraints->put(&(DrmConstraints::LICENSE_AVAILABLE_TIME), charValue);
+
+    return drmConstraints;
+}
+
+DrmInfoStatus* DrmPassthruPlugIn::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+    LOGD("DrmPassthruPlugIn::onProcessDrmInfo - Enter : %d", uniqueId);
+    DrmInfoStatus* drmInfoStatus = NULL;
+    if (NULL != drmInfo) {
+        switch (drmInfo->getInfoType()) {
+        case DrmInfoRequest::TYPE_REGISTRATION_INFO: {
+            const DrmBuffer* emptyBuffer = new DrmBuffer();
+            drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK,
+                    DrmInfoRequest::TYPE_REGISTRATION_INFO, emptyBuffer, drmInfo->getMimeType());
+            break;
+        }
+        case DrmInfoRequest::TYPE_UNREGISTRATION_INFO: {
+            const DrmBuffer* emptyBuffer = new DrmBuffer();
+            drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK,
+                    DrmInfoRequest::TYPE_UNREGISTRATION_INFO, emptyBuffer, drmInfo->getMimeType());
+            break;
+        }
+        case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO: {
+            String8 licenseString("dummy_license_string");
+            const int bufferSize = licenseString.size();
+            char* data = NULL;
+            data = new char[bufferSize];
+            memcpy(data, licenseString.string(), bufferSize);
+            const DrmBuffer* buffer = new DrmBuffer(data, bufferSize);
+            drmInfoStatus = new DrmInfoStatus(DrmInfoStatus::STATUS_OK,
+                    DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, buffer, drmInfo->getMimeType());
+            break;
+        }
+        }
+    }
+    LOGD("DrmPassthruPlugIn::onProcessDrmInfo - Exit");
+    return drmInfoStatus;
+}
+
+status_t DrmPassthruPlugIn::onSetOnInfoListener(
+            int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
+    LOGD("DrmPassthruPlugIn::onSetOnInfoListener : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onInitialize(int uniqueId) {
+    LOGD("DrmPassthruPlugIn::onInitialize : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onTerminate(int uniqueId) {
+    LOGD("DrmPassthruPlugIn::onTerminate : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+DrmSupportInfo* DrmPassthruPlugIn::onGetSupportInfo(int uniqueId) {
+    LOGD("DrmPassthruPlugIn::onGetSupportInfo : %d", uniqueId);
+    DrmSupportInfo* drmSupportInfo = new DrmSupportInfo();
+    // Add mimetype's
+    drmSupportInfo->addMimeType(String8("application/vnd.passthru.drm"));
+    // Add File Suffixes
+    drmSupportInfo->addFileSuffix(String8(".passthru"));
+    // Add plug-in description
+    drmSupportInfo->setDescription(String8("Passthru plug-in"));
+    return drmSupportInfo;
+}
+
+status_t DrmPassthruPlugIn::onSaveRights(int uniqueId, const DrmRights& drmRights,
+            const String8& rightsPath, const String8& contentPath) {
+    LOGD("DrmPassthruPlugIn::onSaveRights : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+DrmInfo* DrmPassthruPlugIn::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+    LOGD("DrmPassthruPlugIn::onAcquireDrmInfo : %d", uniqueId);
+    DrmInfo* drmInfo = NULL;
+
+    if (NULL != drmInfoRequest) {
+        String8 dataString("dummy_acquistion_string");
+        int length = dataString.length();
+        char* data = NULL;
+        data = new char[length];
+        memcpy(data, dataString.string(), length);
+        drmInfo = new DrmInfo(drmInfoRequest->getInfoType(),
+            DrmBuffer(data, length), drmInfoRequest->getMimeType());
+    }
+    return drmInfo;
+}
+
+bool DrmPassthruPlugIn::onCanHandle(int uniqueId, const String8& path) {
+    LOGD("DrmPassthruPlugIn::canHandle: %s ", path.string());
+    String8 extension = path.getPathExtension();
+    extension.toLower();
+    return (String8(".passthru") == extension);
+}
+
+String8 DrmPassthruPlugIn::onGetOriginalMimeType(int uniqueId, const String8& path) {
+    LOGD("DrmPassthruPlugIn::onGetOriginalMimeType() : %d", uniqueId);
+    return String8("video/passthru");
+}
+
+int DrmPassthruPlugIn::onGetDrmObjectType(
+            int uniqueId, const String8& path, const String8& mimeType) {
+    LOGD("DrmPassthruPlugIn::onGetDrmObjectType() : %d", uniqueId);
+    return DrmObjectType::UNKNOWN;
+}
+
+int DrmPassthruPlugIn::onCheckRightsStatus(int uniqueId, const String8& path, int action) {
+    LOGD("DrmPassthruPlugIn::onCheckRightsStatus() : %d", uniqueId);
+    int rightsStatus = RightsStatus::RIGHTS_VALID;
+    return rightsStatus;
+}
+
+status_t DrmPassthruPlugIn::onConsumeRights(int uniqueId, DecryptHandle* decryptHandle,
+            int action, bool reserve) {
+    LOGD("DrmPassthruPlugIn::onConsumeRights() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+            int playbackStatus, int position) {
+    LOGD("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+bool DrmPassthruPlugIn::onValidateAction(int uniqueId, const String8& path,
+            int action, const ActionDescription& description) {
+    LOGD("DrmPassthruPlugIn::onValidateAction() : %d", uniqueId);
+    return true;
+}
+
+status_t DrmPassthruPlugIn::onRemoveRights(int uniqueId, const String8& path) {
+    LOGD("DrmPassthruPlugIn::onRemoveRights() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onRemoveAllRights(int uniqueId) {
+    LOGD("DrmPassthruPlugIn::onRemoveAllRights() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onOpenConvertSession(int uniqueId, int convertId) {
+    LOGD("DrmPassthruPlugIn::onOpenConvertSession() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+DrmConvertedStatus* DrmPassthruPlugIn::onConvertData(
+            int uniqueId, int convertId, const DrmBuffer* inputData) {
+    LOGD("DrmPassthruPlugIn::onConvertData() : %d", uniqueId);
+    DrmBuffer* convertedData = NULL;
+
+    if (NULL != inputData && 0 < inputData->length) {
+        int length = inputData->length;
+        char* data = NULL;
+        data = new char[length];
+        convertedData = new DrmBuffer(data, length);
+        memcpy(convertedData->data, inputData->data, length);
+    }
+    return new DrmConvertedStatus(DrmConvertedStatus::STATUS_OK, convertedData, 0 /*offset*/);
+}
+
+DrmConvertedStatus* DrmPassthruPlugIn::onCloseConvertSession(int uniqueId, int convertId) {
+    LOGD("DrmPassthruPlugIn::onCloseConvertSession() : %d", uniqueId);
+    return new DrmConvertedStatus(DrmConvertedStatus::STATUS_OK, NULL, 0 /*offset*/);
+}
+
+status_t DrmPassthruPlugIn::onOpenDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, int fd, int offset, int length) {
+    LOGD("DrmPassthruPlugIn::onOpenDecryptSession() : %d", uniqueId);
+
+#ifdef ENABLE_PASSTHRU_DECRYPTION
+    decryptHandle->mimeType = String8("video/passthru");
+    decryptHandle->decryptApiType = DecryptApiType::ELEMENTARY_STREAM_BASED;
+    decryptHandle->status = DRM_NO_ERROR;
+    decryptHandle->decryptInfo = NULL;
+    return DRM_NO_ERROR;
+#endif
+
+    return DRM_ERROR_CANNOT_HANDLE;
+}
+
+status_t DrmPassthruPlugIn::onOpenDecryptSession(
+            int uniqueId, DecryptHandle* decryptHandle, const char* uri) {
+    return DRM_ERROR_CANNOT_HANDLE;
+}
+
+status_t DrmPassthruPlugIn::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+    LOGD("DrmPassthruPlugIn::onCloseDecryptSession() : %d", uniqueId);
+    if (NULL != decryptHandle) {
+        if (NULL != decryptHandle->decryptInfo) {
+            delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
+        }
+        delete decryptHandle; decryptHandle = NULL;
+    }
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* headerInfo) {
+    LOGD("DrmPassthruPlugIn::onInitializeDecryptUnit() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+            int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+    LOGD("DrmPassthruPlugIn::onDecrypt() : %d", uniqueId);
+    /**
+     * As a workaround implementation passthru would copy the given
+     * encrypted buffer as it is to decrypted buffer. Note, decBuffer
+     * memory has to be allocated by the caller.
+     */
+    if (NULL != (*decBuffer) && 0 < (*decBuffer)->length) {
+        memcpy((*decBuffer)->data, encBuffer->data, encBuffer->length);
+        (*decBuffer)->length = encBuffer->length;
+    }
+    return DRM_NO_ERROR;
+}
+
+status_t DrmPassthruPlugIn::onFinalizeDecryptUnit(
+            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    LOGD("DrmPassthruPlugIn::onFinalizeDecryptUnit() : %d", uniqueId);
+    return DRM_NO_ERROR;
+}
+
+ssize_t DrmPassthruPlugIn::onPread(int uniqueId, DecryptHandle* decryptHandle,
+            void* buffer, ssize_t numBytes, off_t offset) {
+    LOGD("DrmPassthruPlugIn::onPread() : %d", uniqueId);
+    return 0;
+}
+
diff --git a/include/drm/DrmConstraints.h b/include/drm/DrmConstraints.h
new file mode 100644
index 0000000..a9ec942
--- /dev/null
+++ b/include/drm/DrmConstraints.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 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_CONSTRAINTS_H__
+#define __DRM_CONSTRAINTS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which contains the constraints information.
+ *
+ * As a result of DrmManagerClient::getConstraints(const String8*, const int)
+ * an instance of DrmConstraints would be returned.
+ *
+ */
+class DrmConstraints {
+public:
+    /**
+     * The following variables are replica of android.drm.DrmStore.ConstraintsColumns
+     * Any changes should also be incorporated with Java Layer as well
+     */
+    /**
+     * The max repeat count
+     */
+    static const String8 MAX_REPEAT_COUNT;
+    /**
+     * The remaining repeat count
+     */
+    static const String8 REMAINING_REPEAT_COUNT;
+
+    /**
+     * The time before which the protected file can not be played/viewed
+     */
+    static const String8 LICENSE_START_TIME;
+
+    /**
+     * The time after which the protected file can not be played/viewed
+     */
+    static const String8 LICENSE_EXPIRY_TIME;
+
+    /**
+     * The available time for license
+     */
+    static const String8 LICENSE_AVAILABLE_TIME;
+
+    /**
+     * The data stream for extended metadata
+     */
+    static const String8 EXTENDED_METADATA;
+
+public:
+    /**
+     * Iterator for key
+     */
+    class KeyIterator {
+        friend class DrmConstraints;
+    private:
+        KeyIterator(DrmConstraints* drmConstraints)
+            : mDrmConstraints(drmConstraints), mIndex(0) {}
+
+    public:
+        KeyIterator(const KeyIterator& keyIterator);
+        KeyIterator& operator=(const KeyIterator& keyIterator);
+        virtual ~KeyIterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmConstraints* mDrmConstraints;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator for constraints
+     */
+    class Iterator {
+        friend class DrmConstraints;
+    private:
+        Iterator(DrmConstraints* drmConstraints)
+            : mDrmConstraints(drmConstraints), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        String8 next();
+
+    private:
+        DrmConstraints* mDrmConstraints;
+        unsigned int mIndex;
+    };
+
+public:
+    DrmConstraints() {}
+    virtual ~DrmConstraints() {
+        DrmConstraints::KeyIterator keyIt = this->keyIterator();
+
+        while (keyIt.hasNext()) {
+            String8 key = keyIt.next();
+                const char* value = this->getAsByteArray(&key);
+                if (NULL != value) {
+                    delete[] value;
+                    value = NULL;
+                }
+        }
+        mConstraintMap.clear();
+    }
+public:
+    /**
+     * Returns the number of constraints contained in this instance
+     *
+     * @return Number of constraints
+     */
+    int getCount(void) const;
+
+    /**
+     * Adds constraint information as <key, value> pair to this instance
+     *
+     * @param[in] key Key to add
+     * @param[in] value Value to add
+     * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t put(const String8* key, const char* value);
+
+    /**
+     * Retrieves the value of given key
+     *
+     * @param key Key whose value to be retrieved
+     * @return The value
+     */
+    String8 get(const String8& key) const;
+
+     /**
+     * Retrieves the value as byte array of given key
+     * @param key Key whose value to be retrieved as byte array
+     * @return The byte array value
+     */
+    const char* getAsByteArray(const String8* key) const;
+
+    /**
+     * Returns KeyIterator object to walk through the keys associated with this instance
+     *
+     * @return KeyIterator object
+     */
+    KeyIterator keyIterator();
+
+    /**
+     * Returns Iterator object to walk through the values associated with this instance
+     *
+     * @return Iterator object
+     */
+    Iterator iterator();
+private:
+    const char* getValue(const String8* key) const;
+private:
+    typedef KeyedVector<String8, const char*> DrmConstraintsMap;
+    DrmConstraintsMap mConstraintMap;
+};
+
+};
+
+#endif /* __DRM_CONSTRAINTS_H__ */
+
diff --git a/include/drm/DrmConvertedStatus.h b/include/drm/DrmConvertedStatus.h
new file mode 100644
index 0000000..679e48d
--- /dev/null
+++ b/include/drm/DrmConvertedStatus.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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_CONVERTED_STATUS_H__
+#define __DRM_CONVERTED_STATUS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the status of the conversion, the converted
+ * data/checksum data and the offset. Offset is going to be used in the case of close
+ * session where the agent will inform where the header and body signature should be added
+ *
+ * As a result of DrmManagerClient::convertData(int, const DrmBuffer*) and
+ * DrmManagerClient::closeConvertSession(int) an instance of DrmConvertedStatus
+ * would be returned.
+ *
+ */
+class DrmConvertedStatus {
+public:
+    // Should be in sync with DrmConvertedStatus.java
+    static const int STATUS_OK = 1;
+    static const int STATUS_INPUTDATA_ERROR = 2;
+    static const int STATUS_ERROR = 3;
+
+public:
+    /**
+     * Constructor for DrmConvertedStatus
+     *
+     * @param[in] _statusCode Status of the conversion
+     * @param[in] _convertedData Converted data/checksum data
+     * @param[in] _offset Offset value
+     */
+    DrmConvertedStatus(int _statusCode, const DrmBuffer* _convertedData, int _offset);
+
+    /**
+     * Destructor for DrmConvertedStatus
+     */
+    virtual ~DrmConvertedStatus() {
+
+    }
+
+public:
+    int statusCode;
+    const DrmBuffer* convertedData;
+    int offset;
+};
+
+};
+
+#endif /* __DRM_CONVERTED_STATUS_H__ */
+
diff --git a/include/drm/DrmInfo.h b/include/drm/DrmInfo.h
new file mode 100644
index 0000000..7b48541
--- /dev/null
+++ b/include/drm/DrmInfo.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010 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_INFO_H__
+#define __DRM_INFO_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class in which necessary information required to transact
+ * between device and online DRM server is described. DRM Framework achieves
+ * server registration, license acquisition and any other server related transaction
+ * by passing an instance of this class to DrmManagerClient::processDrmInfo(const DrmInfo*).
+ *
+ * The Caller can retrieve the DrmInfo instance by using
+ * DrmManagerClient::acquireDrmInfo(const DrmInfoRequest*) by passing DrmInfoRequest instance.
+ *
+ */
+class DrmInfo {
+public:
+    /**
+     * Constructor for DrmInfo
+     *
+     * @param[in] infoType Type of information
+     * @param[in] drmBuffer Trigger data
+     * @param[in] mimeType MIME type
+     */
+    DrmInfo(int infoType, const DrmBuffer& drmBuffer, const String8& mimeType);
+
+    /**
+     * Destructor for DrmInfo
+     */
+    virtual ~DrmInfo() {}
+
+public:
+    /**
+     * Iterator for key
+     */
+    class KeyIterator {
+        friend class DrmInfo;
+
+    private:
+        KeyIterator(const DrmInfo* drmInfo)
+            : mDrmInfo(const_cast <DrmInfo*> (drmInfo)), mIndex(0) {}
+
+    public:
+        KeyIterator(const KeyIterator& keyIterator);
+        KeyIterator& operator=(const KeyIterator& keyIterator);
+        virtual ~KeyIterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmInfo* mDrmInfo;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator
+     */
+    class Iterator {
+        friend class DrmInfo;
+
+    private:
+        Iterator(const DrmInfo* drmInfo)
+            : mDrmInfo(const_cast <DrmInfo*> (drmInfo)), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        String8& next();
+
+    private:
+        DrmInfo* mDrmInfo;
+        unsigned int mIndex;
+    };
+
+public:
+    /**
+     * Returns information type associated with this instance
+     *
+     * @return Information type
+     */
+    int getInfoType(void) const;
+
+    /**
+     * Returns MIME type associated with this instance
+     *
+     * @return MIME type
+     */
+    String8 getMimeType(void) const;
+
+    /**
+     * Returns the trigger data associated with this instance
+     *
+     * @return Trigger data
+     */
+    const DrmBuffer& getData(void) const;
+
+    /**
+     * Returns the number of attributes contained in this instance
+     *
+     * @return Number of attributes
+     */
+    int getCount(void) const;
+
+    /**
+     * Adds optional information as <key, value> pair to this instance
+     *
+     * @param[in] key Key to add
+     * @param[in] value Value to add
+     * @return Returns the error code
+     */
+    status_t put(const String8& key, const String8& value);
+
+    /**
+     * Retrieves the value of given key
+     *
+     * @param key Key whose value to be retrieved
+     * @return The value
+     */
+    String8 get(const String8& key) const;
+
+    /**
+     * Returns KeyIterator object to walk through the keys associated with this instance
+     *
+     * @return KeyIterator object
+     */
+    KeyIterator keyIterator() const;
+
+    /**
+     * Returns Iterator object to walk through the values associated with this instance
+     *
+     * @return Iterator object
+     */
+    Iterator iterator() const;
+
+    /**
+     * Returns index of the given key
+     *
+     * @return index
+     */
+    int indexOfKey(const String8& key) const;
+
+protected:
+    int mInfoType;
+    DrmBuffer mData;
+    String8 mMimeType;
+    KeyedVector<String8, String8> mAttributes;
+};
+
+};
+
+#endif /* __DRM_INFO_H__ */
+
diff --git a/include/drm/DrmInfoEvent.h b/include/drm/DrmInfoEvent.h
new file mode 100644
index 0000000..7b409ff
--- /dev/null
+++ b/include/drm/DrmInfoEvent.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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_INFO_EVENT_H__
+#define __DRM_INFO_EVENT_H__
+
+namespace android {
+
+class String8;
+
+/**
+ * This is an entity class which would be passed to caller in
+ * DrmManagerClient::OnInfoListener::onInfo(const DrmInfoEvent&).
+ */
+class DrmInfoEvent {
+public:
+    /**
+     * The following constant values should be in sync with DrmInfoEvent.java
+     */
+    //! TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT, when registration has been
+    //! already done by another account ID.
+    static const int TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT = 1;
+    //! TYPE_REMOVE_RIGHTS, when the rights needs to be removed completely.
+    static const int TYPE_REMOVE_RIGHTS = 2;
+    //! TYPE_RIGHTS_INSTALLED, when the rights are downloaded and installed ok.
+    static const int TYPE_RIGHTS_INSTALLED = 3;
+    //! TYPE_WAIT_FOR_RIGHTS, rights object is on it's way to phone,
+    //! wait before calling checkRights again
+    static const int TYPE_WAIT_FOR_RIGHTS = 4;
+    //! TYPE_ACCOUNT_ALREADY_REGISTERED, when registration has been
+    //! already done for the given account.
+    static const int TYPE_ACCOUNT_ALREADY_REGISTERED = 5;
+
+    /**
+     * The following constant values should be in sync with DrmErrorEvent.java
+     */
+    //! TYPE_RIGHTS_NOT_INSTALLED, when something went wrong installing the rights
+    static const int TYPE_RIGHTS_NOT_INSTALLED = 2001;
+    //! TYPE_RIGHTS_RENEWAL_NOT_ALLOWED, when the server rejects renewal of rights
+    static const int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 2002;
+    //! TYPE_NOT_SUPPORTED, when answer from server can not be handled by the native agent
+    static const int TYPE_NOT_SUPPORTED = 2003;
+    //! TYPE_OUT_OF_MEMORY, when memory allocation fail during renewal.
+    //! Can in the future perhaps be used to trigger garbage collector
+    static const int TYPE_OUT_OF_MEMORY = 2004;
+    //! TYPE_NO_INTERNET_CONNECTION, when the Internet connection is missing and no attempt
+    //! can be made to renew rights
+    static const int TYPE_NO_INTERNET_CONNECTION = 2005;
+    //! TYPE_PROCESS_DRM_INFO_FAILED, when failed to process DrmInfo.
+    static const int TYPE_PROCESS_DRM_INFO_FAILED = 2006;
+
+public:
+    /**
+     * Constructor for DrmInfoEvent
+     *
+     * @param[in] uniqueId Unique session identifier
+     * @param[in] infoType Type of information
+     * @param[in] message Message description
+     */
+    DrmInfoEvent(int uniqueId, int infoType, const String8& message);
+
+    /**
+     * Destructor for DrmInfoEvent
+     */
+    virtual ~DrmInfoEvent() {}
+
+public:
+    /**
+     * Returns the Unique Id associated with this instance
+     *
+     * @return Unique Id
+     */
+    int getUniqueId() const;
+
+    /**
+     * Returns the Type of information associated with this object
+     *
+     * @return Type of information
+     */
+    int getType() const;
+
+    /**
+     * Returns the message description associated with this object
+     *
+     * @return Message description
+     */
+    const String8& getMessage() const;
+
+private:
+    int mUniqueId;
+    int mInfoType;
+    const String8& mMessage;
+};
+
+};
+
+#endif /* __DRM_INFO_EVENT_H__ */
+
diff --git a/include/drm/DrmInfoRequest.h b/include/drm/DrmInfoRequest.h
new file mode 100644
index 0000000..3e48ecc
--- /dev/null
+++ b/include/drm/DrmInfoRequest.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 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_INFO_REQUEST_H__
+#define __DRM_INFO_REQUEST_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class used to pass required parameters to get
+ * the necessary information to communicate with online DRM server
+ *
+ * An instance of this class is passed to
+ * DrmManagerClient::acquireDrmInfo(const DrmInfoRequest*) to get the
+ * instance of DrmInfo.
+ *
+ */
+class DrmInfoRequest {
+public:
+    // Changes in following constants should be in sync with DrmInfoRequest.java
+    static const int TYPE_REGISTRATION_INFO = 1;
+    static const int TYPE_UNREGISTRATION_INFO = 2;
+    static const int TYPE_RIGHTS_ACQUISITION_INFO = 3;
+    static const int TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4;
+
+    /**
+     * Key to pass the unique id for the account or the user
+     */
+    static const String8 ACCOUNT_ID;
+    /**
+     * Key to pass the subscription id
+     */
+    static const String8 SUBSCRIPTION_ID;
+
+public:
+    /**
+     * Constructor for DrmInfoRequest
+     *
+     * @param[in] infoType Type of information
+     * @param[in] mimeType MIME type
+     */
+    DrmInfoRequest(int infoType, const String8& mimeType);
+
+    /**
+     * Destructor for DrmInfoRequest
+     */
+    virtual ~DrmInfoRequest() {}
+
+public:
+    /**
+     * Iterator for key
+     */
+    class KeyIterator {
+        friend class DrmInfoRequest;
+
+    private:
+        KeyIterator(const DrmInfoRequest* drmInfoRequest)
+            : mDrmInfoRequest(const_cast <DrmInfoRequest*> (drmInfoRequest)), mIndex(0) {}
+
+    public:
+        KeyIterator(const KeyIterator& keyIterator);
+        KeyIterator& operator=(const KeyIterator& keyIterator);
+        virtual ~KeyIterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmInfoRequest* mDrmInfoRequest;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator
+     */
+    class Iterator {
+        friend class DrmInfoRequest;
+
+    private:
+        Iterator(const DrmInfoRequest* drmInfoRequest)
+            : mDrmInfoRequest(const_cast <DrmInfoRequest*> (drmInfoRequest)), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        String8& next();
+
+    private:
+        DrmInfoRequest* mDrmInfoRequest;
+        unsigned int mIndex;
+    };
+
+public:
+    /**
+     * Returns information type associated with this instance
+     *
+     * @return Information type
+     */
+    int getInfoType(void) const;
+
+    /**
+     * Returns MIME type associated with this instance
+     *
+     * @return MIME type
+     */
+    String8 getMimeType(void) const;
+
+    /**
+     * Returns the number of entries in DrmRequestInfoMap
+     *
+     * @return Number of entries
+     */
+    int getCount(void) const;
+
+    /**
+     * Adds optional information as <key, value> pair to this instance
+     *
+     * @param[in] key Key to add
+     * @param[in] value Value to add
+     * @return Returns the error code
+     */
+    status_t put(const String8& key, const String8& value);
+
+    /**
+     * Retrieves the value of given key
+     *
+     * @param key Key whose value to be retrieved
+     * @return The value
+     */
+    String8 get(const String8& key) const;
+
+    /**
+     * Returns KeyIterator object to walk through the keys associated with this instance
+     *
+     * @return KeyIterator object
+     */
+    KeyIterator keyIterator() const;
+
+    /**
+     * Returns Iterator object to walk through the values associated with this instance
+     *
+     * @return Iterator object
+     */
+    Iterator iterator() const;
+
+private:
+    int mInfoType;
+    String8 mMimeType;
+
+    typedef KeyedVector<String8, String8> DrmRequestInfoMap;
+    DrmRequestInfoMap mRequestInformationMap;
+};
+
+};
+
+#endif /* __DRM_INFO_REQUEST_H__ */
+
diff --git a/include/drm/DrmInfoStatus.h b/include/drm/DrmInfoStatus.h
new file mode 100644
index 0000000..88c0f40
--- /dev/null
+++ b/include/drm/DrmInfoStatus.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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_INFO_STATUS_H__
+#define __DRM_INFO_STATUS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the result of communication between device
+ * and online DRM server.
+ *
+ * As a result of DrmManagerClient::processDrmInfo(const DrmInfo*) an instance of
+ * DrmInfoStatus would be returned. This class holds DrmBuffer which could be
+ * used to instantiate DrmRights in license acquisition.
+ *
+ */
+class DrmInfoStatus {
+public:
+    // Should be in sync with DrmInfoStatus.java
+    static const int STATUS_OK = 1;
+    static const int STATUS_ERROR = 2;
+
+public:
+    /**
+     * Constructor for DrmInfoStatus
+     *
+     * @param[in] _statusCode Status of the communication
+     * @param[in] _infoType Type of the DRM information processed
+     * @param[in] _drmBuffer Rights information
+     * @param[in] _mimeType MIME type
+     */
+    DrmInfoStatus(int _statusCode, int _infoType, const DrmBuffer* _drmBuffer, const String8& _mimeType);
+
+    /**
+     * Destructor for DrmInfoStatus
+     */
+    virtual ~DrmInfoStatus() {
+
+    }
+
+public:
+    int statusCode;
+    int infoType;
+    const DrmBuffer* drmBuffer;
+    String8 mimeType;
+};
+
+};
+
+#endif /* __DRM_INFO_STATUS_H__ */
+
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
new file mode 100644
index 0000000..004556f
--- /dev/null
+++ b/include/drm/DrmManagerClient.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2010 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_MANAGER_CLIENT_H__
+#define __DRM_MANAGER_CLIENT_H__
+
+#include <utils/threads.h>
+#include <binder/IInterface.h>
+#include "drm_framework_common.h"
+
+namespace android {
+
+class DrmInfo;
+class DrmRights;
+class DrmMetadata;
+class DrmInfoEvent;
+class DrmInfoStatus;
+class DrmInfoRequest;
+class DrmSupportInfo;
+class DrmConstraints;
+class DrmConvertedStatus;
+class DrmManagerClientImpl;
+
+/**
+ * The Native application will instantiate this class and access DRM Framework
+ * services through this class.
+ *
+ */
+class DrmManagerClient {
+public:
+    DrmManagerClient();
+
+    virtual ~DrmManagerClient();
+
+public:
+    class OnInfoListener: virtual public RefBase {
+
+    public:
+        virtual ~OnInfoListener() {}
+
+    public:
+        virtual void onInfo(const DrmInfoEvent& event) = 0;
+    };
+
+/**
+ * APIs which will be used by native modules (e.g. StageFright)
+ *
+ */
+public:
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] fd File descriptor of the protected content to be decrypted
+     * @param[in] offset Start position of the content
+     * @param[in] length The length of the protected content
+     * @return
+     *     Handle for the decryption session
+     */
+    DecryptHandle* openDecryptSession(int fd, int offset, int length);
+
+    /**
+     * Open the decrypt session to decrypt the given protected content
+     *
+     * @param[in] uri Path of the protected content to be decrypted
+     * @return
+     *     Handle for the decryption session
+     */
+    DecryptHandle* openDecryptSession(const char* uri);
+
+    /**
+     * Close the decrypt session for the given handle
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t closeDecryptSession(DecryptHandle* decryptHandle);
+
+    /**
+     * Consumes the rights for a content.
+     * If the reserve parameter is true the rights is reserved until the same
+     * application calls this api again with the reserve parameter set to false.
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc)
+     * @param[in] reserve True if the rights should be reserved.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure.
+     *     In case license has been expired, DRM_ERROR_LICENSE_EXPIRED will be returned.
+     */
+    status_t consumeRights(DecryptHandle* decryptHandle, int action, bool reserve);
+
+    /**
+     * Informs the DRM engine about the playback actions performed on the DRM files.
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE)
+     * @param[in] position Position in the file (in milliseconds) where the start occurs.
+     *                     Only valid together with Playback::START.
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t setPlaybackStatus(DecryptHandle* decryptHandle, int playbackStatus, int position);
+
+    /**
+     * Initialize decryption for the given unit of the protected content
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @param[in] headerInfo Information for initializing decryption of this decrypUnit
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t initializeDecryptUnit(
+            DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo);
+
+    /**
+     * Decrypt the protected content buffers for the given unit
+     * This method will be called any number of times, based on number of
+     * encrypted streams received from application.
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @param[in] encBuffer Encrypted data block
+     * @param[out] decBuffer Decrypted data block
+     * @param[in] IV Optional buffer
+     * @return status_t
+     *     Returns the error code for this API
+     *     DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED
+     *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
+     *     DRM_ERROR_DECRYPT for failure.
+     */
+    status_t decrypt(
+            DecryptHandle* decryptHandle, int decryptUnitId,
+            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV = NULL);
+
+    /**
+     * Finalize decryption for the given unit of the protected content
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[in] decryptUnitId ID which specifies decryption unit, such as track ID
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t finalizeDecryptUnit(DecryptHandle* decryptHandle, int decryptUnitId);
+
+    /**
+     * Reads the specified number of bytes from an open DRM file.
+     *
+     * @param[in] decryptHandle Handle for the decryption session
+     * @param[out] buffer Reference to the buffer that should receive the read data.
+     * @param[in] numBytes Number of bytes to read.
+     * @param[in] offset Offset with which to update the file position.
+     *
+     * @return Number of bytes read. Returns -1 for Failure.
+     */
+    ssize_t pread(DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off_t offset);
+
+    /**
+     * Validates whether an action on the DRM content is allowed or not.
+     *
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to validate. (Action::DEFAULT, Action::PLAY, etc)
+     * @param[in] description Detailed description of the action
+     * @return true if the action is allowed.
+     */
+    bool validateAction(const String8& path, int action, const ActionDescription& description);
+
+/**
+ * APIs which are just the underlying implementation for the Java API
+ *
+ */
+public:
+    /**
+     * Register a callback to be invoked when the caller required to
+     * receive necessary information
+     *
+     * @param[in] infoListener Listener
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t setOnInfoListener(const sp<DrmManagerClient::OnInfoListener>& infoListener);
+
+    /**
+     * Get constraint information associated with input content
+     *
+     * @param[in] path Path of the protected content
+     * @param[in] action Actions defined such as,
+     *             Action::DEFAULT, Action::PLAY, etc
+     * @return DrmConstraints
+     *     key-value pairs of constraint are embedded in it
+     * @note
+     *     In case of error, return NULL
+     */
+    DrmConstraints* getConstraints(const String8* path, const int action);
+
+    /**
+     * Get metadata information associated with input content
+     *
+     * @param[in] path Path of the protected content
+     * @return DrmMetadata
+     *         key-value pairs of metadata
+     * @note
+     *     In case of error, return NULL
+     */
+    DrmMetadata* getMetadata(const String8* path);
+
+    /**
+     * Check whether the given mimetype or path can be handled
+     *
+     * @param[in] path Path of the content needs to be handled
+     * @param[in] mimetype Mimetype of the content needs to be handled
+     * @return
+     *     True if DrmManager can handle given path or mime type.
+     */
+    bool canHandle(const String8& path, const String8& mimeType);
+
+    /**
+     * Executes given drm information based on its type
+     *
+     * @param[in] drmInfo Information needs to be processed
+     * @return DrmInfoStatus
+     *     instance as a result of processing given input
+     */
+    DrmInfoStatus* processDrmInfo(const DrmInfo* drmInfo);
+
+    /**
+     * Retrieves necessary information for registration, unregistration or rights
+     * acquisition information.
+     *
+     * @param[in] drmInfoRequest Request information to retrieve drmInfo
+     * @return DrmInfo
+     *     instance as a result of processing given input
+     */
+    DrmInfo* acquireDrmInfo(const DrmInfoRequest* drmInfoRequest);
+
+    /**
+     * Save DRM rights to specified rights path
+     * and make association with content path
+     *
+     * @param[in] drmRights DrmRights to be saved
+     * @param[in] rightsPath File path where rights to be saved
+     * @param[in] contentPath File path where content was saved
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t saveRights(
+        const DrmRights& drmRights, const String8& rightsPath, const String8& contentPath);
+
+    /**
+     * Retrieves the mime type embedded inside the original content
+     *
+     * @param[in] path the path of the protected content
+     * @return String8
+     *     Returns mime-type of the original content, such as "video/mpeg"
+     */
+    String8 getOriginalMimeType(const String8& path);
+
+    /**
+     * Retrieves the type of the protected object (content, rights, etc..)
+     * by using specified path or mimetype. At least one parameter should be non null
+     * to retrieve DRM object type
+     *
+     * @param[in] path Path of the content or null.
+     * @param[in] mimeType Mime type of the content or null.
+     * @return type of the DRM content,
+     *     such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
+     */
+    int getDrmObjectType(const String8& path, const String8& mimeType);
+
+    /**
+     * Check whether the given content has valid rights or not
+     *
+     * @param[in] path Path of the protected content
+     * @param[in] action Action to perform
+     * @return the status of the rights for the protected content,
+     *     such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
+     */
+    int checkRightsStatus(const String8& path, int action);
+
+    /**
+     * Removes the rights associated with the given protected content
+     *
+     * @param[in] path Path of the protected content
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t removeRights(const String8& path);
+
+    /**
+     * Removes all the rights information of each plug-in associated with
+     * DRM framework. Will be used in master reset
+     *
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t removeAllRights();
+
+    /**
+     * This API is for Forward Lock DRM.
+     * Each time the application tries to download a new DRM file
+     * which needs to be converted, then the application has to
+     * begin with calling this API.
+     *
+     * @param[in] convertId Handle for the convert session
+     * @param[in] mimeType Description/MIME type of the input data packet
+     * @return Return handle for the convert session
+     */
+    int openConvertSession(const String8& mimeType);
+
+    /**
+     * Passes the input data which need to be converted. The resultant
+     * converted data and the status is returned in the DrmConvertedInfo
+     * object. This method will be called each time there are new block
+     * of data received by the application.
+     *
+     * @param[in] convertId Handle for the convert session
+     * @param[in] inputData Input Data which need to be converted
+     * @return Return object contains the status of the data conversion,
+     *     the output converted data and offset. In this case the
+     *     application will ignore the offset information.
+     */
+    DrmConvertedStatus* convertData(int convertId, const DrmBuffer* inputData);
+
+    /**
+     * When there is no more data which need to be converted or when an
+     * error occurs that time the application has to inform the Drm agent
+     * via this API. Upon successful conversion of the complete data,
+     * the agent will inform that where the header and body signature
+     * should be added. This signature appending is needed to integrity
+     * protect the converted file.
+     *
+     * @param[in] convertId Handle for the convert session
+     * @return Return object contains the status of the data conversion,
+     *     the header and body signature data. It also informs
+     *     the application on which offset these signature data
+     *     should be appended.
+     */
+    DrmConvertedStatus* closeConvertSession(int convertId);
+
+    /**
+     * Retrieves all DrmSupportInfo instance that native DRM framework can handle.
+     * This interface is meant to be used by JNI layer
+     *
+     * @param[out] length Number of elements in drmSupportInfoArray
+     * @param[out] drmSupportInfoArray Array contains all DrmSupportInfo
+     *     that native DRM framework can handle
+     * @return status_t
+     *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t getAllSupportInfo(int* length, DrmSupportInfo** drmSupportInfoArray);
+
+private:
+    int mUniqueId;
+    Mutex mDecryptLock;
+    DrmManagerClientImpl* mDrmManagerClientImpl;
+};
+
+};
+
+#endif /* __DRM_MANAGER_CLIENT_H__ */
+
diff --git a/include/drm/DrmMetadata.h b/include/drm/DrmMetadata.h
new file mode 100644
index 0000000..2c7538a
--- /dev/null
+++ b/include/drm/DrmMetadata.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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_METADATA_H__
+#define __DRM_METADATA_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which contains the constraints information.
+ *
+ * As a result of DrmManagerClient::getMetadata(const String8*)
+ * an instance of DrmMetadata would be returned.
+ */
+class DrmMetadata {
+public:
+    /**
+     * Iterator for key
+     */
+    class KeyIterator {
+        friend class DrmMetadata;
+    private:
+        KeyIterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+    public:
+        KeyIterator(const KeyIterator& keyIterator);
+        KeyIterator& operator=(const KeyIterator& keyIterator);
+        virtual ~KeyIterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmMetadata* mDrmMetadata;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator for constraints
+     */
+    class Iterator {
+        friend class DrmMetadata;
+    private:
+        Iterator(DrmMetadata* drmMetadata) : mDrmMetadata(drmMetadata), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        String8 next();
+
+    private:
+        DrmMetadata* mDrmMetadata;
+        unsigned int mIndex;
+    };
+
+public:
+    DrmMetadata() {}
+    virtual ~DrmMetadata() {
+        DrmMetadata::KeyIterator keyIt = this->keyIterator();
+
+        while (keyIt.hasNext()) {
+            String8 key = keyIt.next();
+            const char* value = this->getAsByteArray(&key);
+            if (NULL != value) {
+                delete[] value;
+                value = NULL;
+            }
+        }
+        mMetadataMap.clear();
+    }
+
+public:
+    int getCount(void) const;
+    status_t put(const String8* key, const char* value);
+    String8 get(const String8& key) const;
+    const char* getAsByteArray(const String8* key) const;
+    KeyIterator keyIterator();
+    Iterator iterator();
+
+private:
+    const char* getValue(const String8* key) const;
+
+private:
+    typedef KeyedVector<String8, const char*> DrmMetadataMap;
+    DrmMetadataMap mMetadataMap;
+};
+
+};
+
+#endif /* __DRM_METADATA_H__ */
+
diff --git a/include/drm/DrmRights.h b/include/drm/DrmRights.h
new file mode 100644
index 0000000..11f8f78
--- /dev/null
+++ b/include/drm/DrmRights.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 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_RIGHTS_H__
+#define __DRM_RIGHTS_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the license information which was
+ * retrieved from the online DRM server.
+ *
+ * Caller can instantiate DrmRights by invoking DrmRights(const DrmBuffer&, String)
+ * constructor by using the result of DrmManagerClient::ProcessDrmInfo(const DrmInfo*) API.
+ * Caller can also instantiate DrmRights using the file path which contains rights information.
+ *
+ */
+class DrmRights {
+public:
+    /**
+     * Constructor for DrmRights
+     *
+     * @param[in] rightsFilePath Path of the file containing rights data
+     * @param[in] mimeType MIME type
+     * @param[in] accountId Account Id of the user
+     * @param[in] subscriptionId Subscription Id of the user
+     */
+    DrmRights(
+            const String8& rightsFilePath, const String8& mimeType,
+            const String8& accountId = String8("_NO_USER"),
+            const String8& subscriptionId = String8(""));
+
+    /**
+     * Constructor for DrmRights
+     *
+     * @param[in] rightsData Rights data
+     * @param[in] mimeType MIME type
+     * @param[in] accountId Account Id of the user
+     * @param[in] subscriptionId Subscription Id of the user
+     */
+    DrmRights(
+            const DrmBuffer& rightsData, const String8& mimeType,
+            const String8& accountId = String8("_NO_USER"),
+            const String8& subscriptionId = String8(""));
+
+    /**
+     * Destructor for DrmRights
+     */
+    virtual ~DrmRights();
+
+public:
+    /**
+     * Returns the rights data associated with this instance
+     *
+     * @return Rights data
+     */
+    const DrmBuffer& getData(void) const;
+
+    /**
+     * Returns MIME type associated with this instance
+     *
+     * @return MIME type
+     */
+    String8 getMimeType(void) const;
+
+    /**
+     * Returns the account-id associated with this instance
+     *
+     * @return Account Id
+     */
+    String8 getAccountId(void) const;
+
+    /**
+     * Returns the subscription-id associated with this object
+     *
+     * @return Subscription Id
+     */
+    String8 getSubscriptionId(void) const;
+
+private:
+    DrmBuffer mData;
+    String8 mMimeType;
+    String8 mAccountId;
+    String8 mSubscriptionId;
+    char* mRightsFromFile;
+};
+
+};
+
+#endif /* __DRM_RIGHTS_H__ */
+
diff --git a/include/drm/DrmSupportInfo.h b/include/drm/DrmSupportInfo.h
new file mode 100644
index 0000000..bf12b0b
--- /dev/null
+++ b/include/drm/DrmSupportInfo.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 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_SUPPORT_INFO_H__
+#define __DRM_SUPPORT_INFO_H__
+
+#include "drm_framework_common.h"
+
+namespace android {
+
+/**
+ * This is an utility class which wraps the capability of each plug-in,
+ * such as mimetype's and file suffixes it could handle.
+ *
+ * Plug-in developer could return the capability of the plugin by passing
+ * DrmSupportInfo instance.
+ *
+ */
+class DrmSupportInfo {
+public:
+    /**
+     * Iterator for mMimeTypeVector
+     */
+    class MimeTypeIterator {
+        friend class DrmSupportInfo;
+    private:
+        MimeTypeIterator(DrmSupportInfo* drmSupportInfo)
+           : mDrmSupportInfo(drmSupportInfo), mIndex(0) {}
+    public:
+        MimeTypeIterator(const MimeTypeIterator& iterator);
+        MimeTypeIterator& operator=(const MimeTypeIterator& iterator);
+        virtual ~MimeTypeIterator() {}
+
+    public:
+        bool hasNext();
+        String8& next();
+
+    private:
+        DrmSupportInfo* mDrmSupportInfo;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator for mFileSuffixVector
+     */
+    class FileSuffixIterator {
+       friend class DrmSupportInfo;
+
+    private:
+        FileSuffixIterator(DrmSupportInfo* drmSupportInfo)
+            : mDrmSupportInfo(drmSupportInfo), mIndex(0) {}
+    public:
+        FileSuffixIterator(const FileSuffixIterator& iterator);
+        FileSuffixIterator& operator=(const FileSuffixIterator& iterator);
+        virtual ~FileSuffixIterator() {}
+
+    public:
+        bool hasNext();
+        String8& next();
+
+    private:
+        DrmSupportInfo* mDrmSupportInfo;
+        unsigned int mIndex;
+    };
+
+public:
+    /**
+     * Constructor for DrmSupportInfo
+     */
+    DrmSupportInfo();
+
+    /**
+     * Copy constructor for DrmSupportInfo
+     */
+    DrmSupportInfo(const DrmSupportInfo& drmSupportInfo);
+
+    /**
+     * Destructor for DrmSupportInfo
+     */
+    virtual ~DrmSupportInfo() {}
+
+    DrmSupportInfo& operator=(const DrmSupportInfo& drmSupportInfo);
+    bool operator<(const DrmSupportInfo& drmSupportInfo) const;
+    bool operator==(const DrmSupportInfo& drmSupportInfo) const;
+
+    /**
+     * Returns FileSuffixIterator object to walk through file suffix values
+     * associated with this instance
+     *
+     * @return FileSuffixIterator object
+     */
+    FileSuffixIterator getFileSuffixIterator();
+
+    /**
+     * Returns MimeTypeIterator object to walk through mimetype values
+     * associated with this instance
+     *
+     * @return MimeTypeIterator object
+     */
+    MimeTypeIterator getMimeTypeIterator();
+
+public:
+    /**
+     * Returns the number of mimetypes supported.
+     *
+     * @return Number of mimetypes supported
+     */
+    int getMimeTypeCount(void) const;
+
+    /**
+     * Returns the number of file types supported.
+     *
+     * @return Number of file types supported
+     */
+    int getFileSuffixCount(void) const;
+
+    /**
+     * Adds the mimetype to the list of supported mimetypes
+     *
+     * @param[in] mimeType mimetype to be added
+     * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t addMimeType(const String8& mimeType);
+
+    /**
+     * Adds the filesuffix to the list of supported file types
+     *
+     * @param[in] filesuffix file suffix to be added
+     * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t addFileSuffix(const String8& fileSuffix);
+
+    /**
+     * Set the unique description about the plugin
+     *
+     * @param[in] description Unique description
+     * @return Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
+     */
+    status_t setDescription(const String8& description);
+
+    /**
+     * Returns the unique description associated with the plugin
+     *
+     * @return Unique description
+     */
+    String8 getDescription() const;
+
+    /**
+     * Returns whether given mimetype is supported or not
+     *
+     * @param[in] mimeType MIME type
+     * @return
+     *        true - if mime-type is supported
+     *        false - if mime-type is not supported
+     */
+    bool isSupportedMimeType(const String8& mimeType) const;
+
+    /**
+     * Returns whether given file type is supported or not
+     *
+     * @param[in] fileType File type
+     * @return
+     *     true if file type is supported
+     *     false if file type is not supported
+     */
+    bool isSupportedFileSuffix(const String8& fileType) const;
+
+private:
+    Vector<String8> mMimeTypeVector;
+    Vector<String8> mFileSuffixVector;
+
+    String8 mDescription;
+};
+
+};
+
+#endif /* __DRM_SUPPORT_INFO_H__ */
+
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
new file mode 100644
index 0000000..c5765a9
--- /dev/null
+++ b/include/drm/drm_framework_common.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2010 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_FRAMEWORK_COMMON_H__
+#define __DRM_FRAMEWORK_COMMON_H__
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+
+#define INVALID_VALUE -1
+
+namespace android {
+
+/**
+ * Error code for DRM Frameowrk
+ */
+enum {
+    DRM_ERROR_BASE = -2000,
+
+    DRM_ERROR_UNKNOWN                       = DRM_ERROR_BASE,
+    DRM_ERROR_LICENSE_EXPIRED               = DRM_ERROR_BASE - 1,
+    DRM_ERROR_SESSION_NOT_OPENED            = DRM_ERROR_BASE - 2,
+    DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED  = DRM_ERROR_BASE - 3,
+    DRM_ERROR_DECRYPT                       = DRM_ERROR_BASE - 4,
+    DRM_ERROR_CANNOT_HANDLE                 = DRM_ERROR_BASE - 5,
+
+    DRM_NO_ERROR                            = NO_ERROR
+};
+
+/**
+ * Defines DRM Buffer
+ */
+class DrmBuffer {
+public:
+    char* data;
+    int length;
+
+    DrmBuffer() :
+        data(NULL),
+        length(0) {
+    }
+
+    DrmBuffer(char* dataBytes, int dataLength) :
+        data(dataBytes),
+        length(dataLength) {
+    }
+
+};
+
+/**
+ * Defines detailed description of the action
+ */
+class ActionDescription {
+public:
+    ActionDescription(int _outputType, int _configuration) :
+        outputType(_outputType),
+        configuration(_configuration) {
+    }
+
+public:
+    int outputType;   /* BLUETOOTH , HDMI*/
+    int configuration; /* RESOLUTION_720_480 , RECORDABLE etc.*/
+};
+
+/**
+ * Defines constants related to DRM types
+ */
+class DrmObjectType {
+private:
+    DrmObjectType();
+
+public:
+    /**
+     * Field specifies the unknown type
+     */
+    static const int UNKNOWN = 0x00;
+    /**
+     * Field specifies the protected content type
+     */
+    static const int CONTENT = 0x01;
+    /**
+     * Field specifies the rights information
+     */
+    static const int RIGHTS_OBJECT = 0x02;
+    /**
+     * Field specifies the trigger information
+     */
+    static const int TRIGGER_OBJECT = 0x03;
+};
+
+/**
+ * Defines constants related to play back
+ */
+class Playback {
+private:
+    Playback();
+
+public:
+    /**
+     * Constant field signifies playback start
+     */
+    static const int START = 0x00;
+    /**
+     * Constant field signifies playback stop
+     */
+    static const int STOP = 0x01;
+    /**
+     * Constant field signifies playback paused
+     */
+    static const int PAUSE = 0x02;
+    /**
+     * Constant field signifies playback resumed
+     */
+    static const int RESUME = 0x03;
+};
+
+/**
+ * Defines actions that can be performed on protected content
+ */
+class Action {
+private:
+    Action();
+
+public:
+    /**
+     * Constant field signifies that the default action
+     */
+    static const int DEFAULT = 0x00;
+    /**
+     * Constant field signifies that the content can be played
+     */
+    static const int PLAY = 0x01;
+    /**
+     * Constant field signifies that the content can be set as ring tone
+     */
+    static const int RINGTONE = 0x02;
+    /**
+     * Constant field signifies that the content can be transfered
+     */
+    static const int TRANSFER = 0x03;
+    /**
+     * Constant field signifies that the content can be set as output
+     */
+    static const int OUTPUT = 0x04;
+    /**
+     * Constant field signifies that preview is allowed
+     */
+    static const int PREVIEW = 0x05;
+    /**
+     * Constant field signifies that the content can be executed
+     */
+    static const int EXECUTE = 0x06;
+    /**
+     * Constant field signifies that the content can displayed
+     */
+    static const int DISPLAY = 0x07;
+};
+
+/**
+ * Defines constants related to status of the rights
+ */
+class RightsStatus {
+private:
+    RightsStatus();
+
+public:
+    /**
+     * Constant field signifies that the rights are valid
+     */
+    static const int RIGHTS_VALID = 0x00;
+    /**
+     * Constant field signifies that the rights are invalid
+     */
+    static const int RIGHTS_INVALID = 0x01;
+    /**
+     * Constant field signifies that the rights are expired for the content
+     */
+    static const int RIGHTS_EXPIRED = 0x02;
+    /**
+     * Constant field signifies that the rights are not acquired for the content
+     */
+    static const int RIGHTS_NOT_ACQUIRED = 0x03;
+};
+
+/**
+ * Defines API set for decryption
+ */
+class DecryptApiType {
+private:
+    DecryptApiType();
+
+public:
+    /**
+     * Decrypt API set for non encrypted content
+     */
+    static const int NON_ENCRYPTED = 0x00;
+    /**
+     * Decrypt API set for ES based DRM
+     */
+    static const int ELEMENTARY_STREAM_BASED = 0x01;
+    /**
+     * POSIX based Decrypt API set for container based DRM
+     */
+    static const int CONTAINER_BASED = 0x02;
+};
+
+/**
+ * Defines decryption information
+ */
+class DecryptInfo {
+public:
+    /**
+     * size of memory to be allocated to get the decrypted content.
+     */
+    int decryptBufferLength;
+    /**
+     * reserved for future purpose
+     */
+};
+
+/**
+ * Defines decryption handle
+ */
+class DecryptHandle {
+public:
+    /**
+     * Decryption session Handle
+     */
+    int decryptId;
+    /**
+     * Mimetype of the content to be used to select the media extractor
+     * For e.g., "video/mpeg" or "audio/mp3"
+     */
+    String8 mimeType;
+    /**
+     * Defines which decryption pattern should be used to decrypt the given content
+     * DrmFramework provides two different set of decryption APIs.
+     *   1. Decrypt APIs for elementary stream based DRM
+     *      (file format is not encrypted but ES is encrypted)
+     *         e.g., Marlin DRM (MP4 file format), WM-DRM (asf file format)
+     *
+     *         DecryptApiType::ELEMENTARY_STREAM_BASED
+     *             Decryption API set for ES based DRM
+     *                 initializeDecryptUnit(), decrypt(), and finalizeDecryptUnit()
+     *   2. Decrypt APIs for container based DRM (file format itself is encrypted)
+     *         e.g., OMA DRM (dcf file format)
+     *
+     *         DecryptApiType::CONTAINER_BASED
+     *             POSIX based Decryption API set for container based DRM
+     *                 pread()
+     */
+    int decryptApiType;
+    /**
+     * Defines the status of the rights like
+     *     RIGHTS_VALID, RIGHTS_INVALID, RIGHTS_EXPIRED or RIGHTS_NOT_ACQUIRED
+     */
+    int status;
+    /**
+     * Information required to decrypt content
+     * e.g. size of memory to be allocated to get the decrypted content.
+     */
+    DecryptInfo* decryptInfo;
+
+public:
+    DecryptHandle():
+            decryptId(INVALID_VALUE),
+            mimeType(""),
+            decryptApiType(INVALID_VALUE),
+            status(INVALID_VALUE) {
+
+    }
+
+    bool operator<(const DecryptHandle& handle) const {
+        return (decryptId < handle.decryptId);
+    }
+
+    bool operator==(const DecryptHandle& handle) const {
+        return (decryptId == handle.decryptId);
+    }
+};
+
+};
+
+#endif /* __DRM_FRAMEWORK_COMMON_H__ */
+
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index a3da3ed..d0b9fcd 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -25,6 +25,7 @@
 #include <utils/List.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
 
 namespace android {
 
@@ -73,6 +74,13 @@
     static void RegisterSniffer(SnifferFunc func);
     static void RegisterDefaultSniffers();
 
+    // for DRM
+    virtual DecryptHandle* DrmInitialization(DrmManagerClient *client) {
+        return NULL;
+    }
+    virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {};
+
+
 protected:
     virtual ~DataSource() {}
 
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index 8a215ea..4307263 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -23,6 +23,7 @@
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
 
 namespace android {
 
@@ -37,15 +38,29 @@
 
     virtual status_t getSize(off_t *size);
 
+    virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
+
+    virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
+
 protected:
     virtual ~FileSource();
 
 private:
     FILE *mFile;
+    int mFd;
     int64_t mOffset;
     int64_t mLength;
     Mutex mLock;
 
+    /*for DRM*/
+    DecryptHandle *mDecryptHandle;
+    DrmManagerClient *mDrmManagerClient;
+    int64_t mDrmBufOffset;
+    int64_t mDrmBufSize;
+    unsigned char *mDrmBuf;
+
+    ssize_t readAtDRM(off_t offset, void *data, size_t size);
+
     FileSource(const FileSource &);
     FileSource &operator=(const FileSource &);
 };
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index e44122d..6df4d86 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -40,6 +40,8 @@
     // Not technically an error.
     INFO_FORMAT_CHANGED    = MEDIA_ERROR_BASE - 12,
     INFO_DISCONTINUITY     = MEDIA_ERROR_BASE - 13,
+
+    ERROR_NO_LICENSE       = MEDIA_ERROR_BASE - 14,
 };
 
 }  // namespace android
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 16b0a4c..a82106e 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -55,6 +55,12 @@
     // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
     virtual uint32_t flags() const;
 
+    // for DRM
+    virtual void setDrmFlag(bool flag) {};
+    virtual char* getDrmTrackInfo(size_t trackID, int *len) {
+        return NULL;
+    }
+
 protected:
     MediaExtractor() {}
     virtual ~MediaExtractor() {}
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index bffb9e0..ea2fa52 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -58,6 +58,8 @@
     kKeyBufferID          = 'bfID',
     kKeyMaxInputSize      = 'inpS',
     kKeyThumbnailTime     = 'thbT',  // int64_t (usecs)
+    kKeyTrackID           = 'trID',
+    kKeyIsDRM             = 'idrm',  // int32_t (bool)
 
     kKeyAlbum             = 'albu',  // cstring
     kKeyArtist            = 'arti',  // cstring
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index 6f581d3..c5112a5 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -81,13 +81,13 @@
 }
 
 static bool fileMatchesExtension(const char* path, const char* extensions) {
-    char* extension = strrchr(path, '.');
+    const char* extension = strrchr(path, '.');
     if (!extension) return false;
     ++extension;    // skip the dot
     if (extension[0] == 0) return false;
 
     while (extensions[0]) {
-        char* comma = strchr(extensions, ',');
+        const char* comma = strchr(extensions, ',');
         size_t length = (comma ? comma - extensions : strlen(extensions));
         if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
         extensions += length;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 615f3e8..d674547 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -11,6 +11,7 @@
         AwesomePlayer.cpp                 \
         CameraSource.cpp                  \
         DataSource.cpp                    \
+        DRMExtractor.cpp                  \
         ESDS.cpp                          \
         FileSource.cpp                    \
         HTTPStream.cpp                    \
@@ -59,7 +60,8 @@
         libsonivox        \
         libvorbisidec     \
         libsurfaceflinger_client \
-        libcamera_client
+        libcamera_client \
+        libdrmframework
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_aacdec \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index d99990b..6dc61c7 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -247,7 +247,8 @@
       mExtractorFlags(0),
       mLastVideoBuffer(NULL),
       mVideoBuffer(NULL),
-      mSuspensionState(NULL) {
+      mSuspensionState(NULL),
+      mDecryptHandle(NULL) {
     CHECK_EQ(mClient.connect(), OK);
 
     DataSource::RegisterDefaultSniffers();
@@ -346,6 +347,12 @@
         return UNKNOWN_ERROR;
     }
 
+    dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
+    if (mDecryptHandle != NULL
+            && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+        notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+    }
+
     return setDataSource_l(extractor);
 }
 
@@ -421,6 +428,13 @@
 }
 
 void AwesomePlayer::reset_l() {
+    if (mDecryptHandle != NULL) {
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::STOP, 0);
+            mDecryptHandle = NULL;
+            mDrmManagerClient = NULL;
+    }
+
     if (mFlags & PREPARING) {
         mFlags |= PREPARE_CANCELLED;
         if (mConnectingDataSource != NULL) {
@@ -761,6 +775,13 @@
 
     bool deferredAudioSeek = false;
 
+    if (mDecryptHandle != NULL) {
+        int64_t position;
+        getPosition(&position);
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::START, position / 1000);
+    }
+
     if (mAudioSource != NULL) {
         if (mAudioPlayer == NULL) {
             if (mAudioSink != NULL) {
@@ -778,6 +799,11 @@
 
                     mFlags &= ~(PLAYING | FIRST_FRAME);
 
+                    if (mDecryptHandle != NULL) {
+                        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                                 Playback::STOP, 0);
+                    }
+
                     return err;
                 }
 
@@ -905,6 +931,11 @@
 
     mFlags &= ~PLAYING;
 
+    if (mDecryptHandle != NULL) {
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::PAUSE, 0);
+    }
+
     return OK;
 }
 
@@ -1022,6 +1053,13 @@
         mWatchForAudioSeekComplete = true;
         mWatchForAudioEOS = true;
         mSeekNotificationSent = false;
+
+        if (mDecryptHandle != NULL) {
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::PAUSE, 0);
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::START, mSeekTimeUs / 1000);
+        }
     }
 }
 
@@ -1254,6 +1292,13 @@
 
     TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
 
+    if (mDecryptHandle != NULL) {
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::PAUSE, 0);
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::START, timeUs / 1000);
+    }
+
     if (mFlags & FIRST_FRAME) {
         mFlags &= ~FIRST_FRAME;
 
@@ -1665,6 +1710,12 @@
         return UNKNOWN_ERROR;
     }
 
+    dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
+    if (mDecryptHandle != NULL
+            && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+        notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+    }
+
     return setDataSource_l(extractor);
 }
 
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
new file mode 100644
index 0000000..aa9ad23
--- /dev/null
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2010 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 "include/DRMExtractor.h"
+#include "include/AMRExtractor.h"
+#include "include/MP3Extractor.h"
+#include "include/MPEG4Extractor.h"
+#include "include/WAVExtractor.h"
+#include "include/OggExtractor.h"
+
+#include <arpa/inet.h>
+#include <utils/String8.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+
+#include <drm/drm_framework_common.h>
+#include <utils/Errors.h>
+
+
+namespace android {
+
+DrmManagerClient* gDrmManagerClient = NULL;
+
+class DRMSource : public MediaSource {
+public:
+    DRMSource(const sp<MediaSource> &mediaSource,
+            DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+    virtual ~DRMSource();
+
+private:
+    sp<MediaSource> mOriginalMediaSource;
+    DecryptHandle* mDecryptHandle;
+    size_t mTrackId;
+    mutable Mutex mDRMLock;
+    size_t mNALLengthSize;
+    bool mWantsNALFragments;
+
+    DRMSource(const DRMSource &);
+    DRMSource &operator=(const DRMSource &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+DRMSource::DRMSource(const sp<MediaSource> &mediaSource,
+        DecryptHandle* decryptHandle, int32_t trackId, DrmBuffer* ipmpBox)
+    : mOriginalMediaSource(mediaSource),
+      mDecryptHandle(decryptHandle),
+      mTrackId(trackId),
+      mNALLengthSize(0),
+      mWantsNALFragments(false) {
+    gDrmManagerClient->initializeDecryptUnit(
+            mDecryptHandle, trackId, ipmpBox);
+
+    const char *mime;
+    bool success = getFormat()->findCString(kKeyMIMEType, &mime);
+    CHECK(success);
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        CHECK(getFormat()->findData(kKeyAVCC, &type, &data, &size));
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size >= 7);
+        CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
+
+        // The number of bytes used to encode the length of a NAL unit.
+        mNALLengthSize = 1 + (ptr[4] & 3);
+    }
+}
+
+DRMSource::~DRMSource() {
+    Mutex::Autolock autoLock(mDRMLock);
+    gDrmManagerClient->finalizeDecryptUnit(mDecryptHandle, mTrackId);
+}
+
+status_t DRMSource::start(MetaData *params) {
+    int32_t val;
+    if (params && params->findInt32(kKeyWantsNALFragments, &val)
+        && val != 0) {
+        mWantsNALFragments = true;
+    } else {
+        mWantsNALFragments = false;
+    }
+
+   return mOriginalMediaSource->start(params);
+}
+
+status_t DRMSource::stop() {
+    return mOriginalMediaSource->stop();
+}
+
+sp<MetaData> DRMSource::getFormat() {
+    return mOriginalMediaSource->getFormat();
+}
+
+status_t DRMSource::read(MediaBuffer **buffer, const ReadOptions *options) {
+    Mutex::Autolock autoLock(mDRMLock);
+    status_t err;
+    if ((err = mOriginalMediaSource->read(buffer, options)) != OK) {
+        return err;
+    }
+
+    size_t len = (*buffer)->range_length();
+
+    char *src = (char *)(*buffer)->data() + (*buffer)->range_offset();
+
+    DrmBuffer encryptedDrmBuffer(src, len);
+    DrmBuffer decryptedDrmBuffer;
+    decryptedDrmBuffer.length = len;
+    decryptedDrmBuffer.data = new char[len];
+    DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer;
+
+    if ((err = gDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
+            &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) {
+
+        if (decryptedDrmBuffer.data) {
+            delete [] decryptedDrmBuffer.data;
+            decryptedDrmBuffer.data = NULL;
+        }
+
+        if (err == DRM_ERROR_LICENSE_EXPIRED) {
+            return ERROR_NO_LICENSE;
+        } else {
+            return ERROR_IO;
+        }
+    }
+    CHECK(pDecryptedDrmBuffer == &decryptedDrmBuffer);
+
+    const char *mime;
+    CHECK(getFormat()->findCString(kKeyMIMEType, &mime));
+
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) && !mWantsNALFragments) {
+        uint8_t *dstData = (uint8_t*)src;
+        size_t srcOffset = 0;
+        size_t dstOffset = 0;
+
+        len = decryptedDrmBuffer.length;
+        while (srcOffset < len) {
+            CHECK(srcOffset + mNALLengthSize <= len);
+            size_t nalLength = 0;
+            const uint8_t* data = (const uint8_t*)(&decryptedDrmBuffer.data[srcOffset]);
+
+            switch (mNALLengthSize) {
+                case 1:
+                    nalLength = *data;
+                    break;
+                case 2:
+                    nalLength = U16_AT(data);
+                    break;
+                case 3:
+                    nalLength = ((size_t)data[0] << 16) | U16_AT(&data[1]);
+                    break;
+                case 4:
+                    nalLength = U32_AT(data);
+                    break;
+                default:
+                    CHECK(!"Should not be here.");
+                    break;
+            }
+
+            srcOffset += mNALLengthSize;
+
+            if (srcOffset + nalLength > len) {
+                if (decryptedDrmBuffer.data) {
+                    delete [] decryptedDrmBuffer.data;
+                    decryptedDrmBuffer.data = NULL;
+                }
+
+                return ERROR_MALFORMED;
+            }
+
+            if (nalLength == 0) {
+                continue;
+            }
+
+            CHECK(dstOffset + 4 <= (*buffer)->size());
+
+            dstData[dstOffset++] = 0;
+            dstData[dstOffset++] = 0;
+            dstData[dstOffset++] = 0;
+            dstData[dstOffset++] = 1;
+            memcpy(&dstData[dstOffset], &decryptedDrmBuffer.data[srcOffset], nalLength);
+            srcOffset += nalLength;
+            dstOffset += nalLength;
+        }
+
+        CHECK_EQ(srcOffset, len);
+        (*buffer)->set_range((*buffer)->range_offset(), dstOffset);
+
+    } else {
+        memcpy(src, decryptedDrmBuffer.data, decryptedDrmBuffer.length);
+        (*buffer)->set_range((*buffer)->range_offset(), decryptedDrmBuffer.length);
+    }
+
+    if (decryptedDrmBuffer.data) {
+        delete [] decryptedDrmBuffer.data;
+        decryptedDrmBuffer.data = NULL;
+    }
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+DRMExtractor::DRMExtractor(const sp<DataSource> &source, const char* mime)
+    : mDataSource(source),
+      mDecryptHandle(NULL) {
+    mOriginalExtractor = MediaExtractor::Create(source, mime);
+    mOriginalExtractor->setDrmFlag(true);
+
+    DrmManagerClient *client;
+    source->getDrmInfo(&mDecryptHandle, &client);
+}
+
+DRMExtractor::~DRMExtractor() {
+}
+
+size_t DRMExtractor::countTracks() {
+    return mOriginalExtractor->countTracks();
+}
+
+sp<MediaSource> DRMExtractor::getTrack(size_t index) {
+    sp<MediaSource> originalMediaSource = mOriginalExtractor->getTrack(index);
+    originalMediaSource->getFormat()->setInt32(kKeyIsDRM, 1);
+
+    int32_t trackID;
+    CHECK(getTrackMetaData(index, 0)->findInt32(kKeyTrackID, &trackID));
+
+    DrmBuffer ipmpBox;
+    ipmpBox.data = mOriginalExtractor->getDrmTrackInfo(trackID, &(ipmpBox.length));
+    CHECK(ipmpBox.length > 0);
+
+    return new DRMSource(originalMediaSource, mDecryptHandle, trackID, &ipmpBox);
+}
+
+sp<MetaData> DRMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+    return mOriginalExtractor->getTrackMetaData(index, flags);
+}
+
+sp<MetaData> DRMExtractor::getMetaData() {
+    return mOriginalExtractor->getMetaData();
+}
+
+static Mutex gDRMSnifferMutex;
+bool SniffDRM(
+    const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
+    {
+        Mutex::Autolock autoLock(gDRMSnifferMutex);
+        if (gDrmManagerClient == NULL) {
+            gDrmManagerClient = new DrmManagerClient();
+        }
+    }
+
+    DecryptHandle *decryptHandle = source->DrmInitialization(gDrmManagerClient);
+
+    if (decryptHandle != NULL) {
+        if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
+            *mimeType = String8("drm+container_based+");
+        } else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) {
+            *mimeType = String8("drm+es_based+");
+        }
+
+        *mimeType += decryptHandle->mimeType;
+        *confidence = 10.0f;
+
+        return true;
+    }
+
+    return false;
+}
+} //namespace android
+
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 49eac62..0b8997c 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -22,6 +22,7 @@
 #include "include/MPEG2TSExtractor.h"
 #include "include/NuCachedSource2.h"
 #include "include/NuHTTPDataSource.h"
+#include "include/DRMExtractor.h"
 
 #include "matroska/MatroskaExtractor.h"
 
@@ -31,6 +32,8 @@
 #include <media/stagefright/MediaErrors.h>
 #include <utils/String8.h>
 
+#include <cutils/properties.h>
+
 namespace android {
 
 bool DataSource::getUInt16(off_t offset, uint16_t *x) {
@@ -104,6 +107,12 @@
     RegisterSniffer(SniffAMR);
     RegisterSniffer(SniffMPEG2TS);
     RegisterSniffer(SniffMP3);
+
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("drm.service.enabled", value, NULL)
+            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+        RegisterSniffer(SniffDRM);
+    }
 }
 
 // static
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index dd2579b..b46d8d0 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -21,14 +21,26 @@
 
 FileSource::FileSource(const char *filename)
     : mFile(fopen(filename, "rb")),
+      mFd(fileno(mFile)),
       mOffset(0),
-      mLength(-1) {
+      mLength(-1),
+      mDecryptHandle(NULL),
+      mDrmManagerClient(NULL),
+      mDrmBufOffset(0),
+      mDrmBufSize(0),
+      mDrmBuf(NULL){
 }
 
 FileSource::FileSource(int fd, int64_t offset, int64_t length)
     : mFile(fdopen(fd, "rb")),
+      mFd(fd),
       mOffset(offset),
-      mLength(length) {
+      mLength(length),
+      mDecryptHandle(NULL),
+      mDrmManagerClient(NULL),
+      mDrmBufOffset(0),
+      mDrmBufSize(0),
+      mDrmBuf(NULL){
     CHECK(offset >= 0);
     CHECK(length >= 0);
 }
@@ -38,6 +50,14 @@
         fclose(mFile);
         mFile = NULL;
     }
+
+    if (mDrmBuf != NULL) {
+        delete[] mDrmBuf;
+        mDrmBuf = NULL;
+    }
+    if (mDecryptHandle != NULL) {
+        mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+    }
 }
 
 status_t FileSource::initCheck() const {
@@ -61,13 +81,18 @@
         }
     }
 
-    int err = fseeko(mFile, offset + mOffset, SEEK_SET);
-    if (err < 0) {
-        LOGE("seek to %lld failed", offset + mOffset);
-        return UNKNOWN_ERROR;
-    }
+    if (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED
+            == mDecryptHandle->decryptApiType) {
+        return readAtDRM(offset, data, size);
+   } else {
+        int err = fseeko(mFile, offset + mOffset, SEEK_SET);
+        if (err < 0) {
+            LOGE("seek to %lld failed", offset + mOffset);
+            return UNKNOWN_ERROR;
+        }
 
-    return fread(data, 1, size, mFile);
+        return fread(data, 1, size, mFile);
+    }
 }
 
 status_t FileSource::getSize(off_t *size) {
@@ -87,4 +112,53 @@
     return OK;
 }
 
+DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) {
+    mDrmManagerClient = client;
+    if (mDecryptHandle == NULL) {
+        mDecryptHandle = mDrmManagerClient->openDecryptSession(
+                mFd, mOffset, mLength);
+    }
+
+    if (mDecryptHandle == NULL) {
+        mDrmManagerClient = NULL;
+    }
+
+    return mDecryptHandle;
+}
+
+void FileSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
+    *handle = mDecryptHandle;
+
+    *client = mDrmManagerClient;
+}
+
+ssize_t FileSource::readAtDRM(off_t offset, void *data, size_t size) {
+    size_t DRM_CACHE_SIZE = 1024;
+    if (mDrmBuf == NULL) {
+        mDrmBuf = new unsigned char[DRM_CACHE_SIZE];
+    }
+
+    if (mDrmBuf != NULL && mDrmBufSize > 0 && (offset + mOffset) >= mDrmBufOffset
+            && (offset + mOffset + size) <= (mDrmBufOffset + mDrmBufSize)) {
+        /* Use buffered data */
+        memcpy(data, (void*)(mDrmBuf+(offset+mOffset-mDrmBufOffset)), size);
+        return size;
+    } else if (size <= DRM_CACHE_SIZE) {
+        /* Buffer new data */
+        mDrmBufOffset =  offset + mOffset;
+        mDrmBufSize = mDrmManagerClient->pread(mDecryptHandle, mDrmBuf,
+                DRM_CACHE_SIZE, offset + mOffset);
+        if (mDrmBufSize > 0) {
+            int64_t dataRead = 0;
+            dataRead = size > mDrmBufSize ? mDrmBufSize : size;
+            memcpy(data, (void*)mDrmBuf, dataRead);
+            return dataRead;
+        } else {
+            return mDrmBufSize;
+        }
+    } else {
+        /* Too big chunk to cache. Call DRM directly */
+        return mDrmManagerClient->pread(mDecryptHandle, data, size, offset + mOffset);
+    }
+}
 }  // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 34064c8..5497322 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -264,7 +264,9 @@
       mHasVideo(false),
       mFirstTrack(NULL),
       mLastTrack(NULL),
-      mFileMetaData(new MetaData) {
+      mFileMetaData(new MetaData),
+      mFirstSINF(NULL),
+      mIsDrm(false) {
 }
 
 MPEG4Extractor::~MPEG4Extractor() {
@@ -276,6 +278,15 @@
         track = next;
     }
     mFirstTrack = mLastTrack = NULL;
+
+    SINF *sinf = mFirstSINF;
+    while (sinf) {
+        SINF *next = sinf->next;
+        delete sinf->IPMPData;
+        delete sinf;
+        sinf = next;
+    }
+    mFirstSINF = NULL;
 }
 
 sp<MetaData> MPEG4Extractor::getMetaData() {
@@ -370,6 +381,178 @@
     return err;
 }
 
+void MPEG4Extractor::setDrmFlag(bool flag) {
+    mIsDrm = flag;
+}
+
+char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
+    if (mFirstSINF == NULL) {
+        return NULL;
+    }
+
+    SINF *sinf = mFirstSINF;
+    while (sinf && (trackID != sinf->trackID)) {
+        sinf = sinf->next;
+    }
+
+    if (sinf == NULL) {
+        return NULL;
+    }
+
+    *len = sinf->len;
+    return sinf->IPMPData;
+}
+
+// Reads an encoded integer 7 bits at a time until it encounters the high bit clear.
+int32_t readSize(off_t offset,
+        const sp<DataSource> DataSource, uint8_t *numOfBytes) {
+    uint32_t size = 0;
+    uint8_t data;
+    bool moreData = true;
+    *numOfBytes = 0;
+
+    while (moreData) {
+        if (DataSource->readAt(offset, &data, 1) < 1) {
+            return -1;
+        }
+        offset ++;
+        moreData = (data >= 128) ? true : false;
+        size = (size << 7) | (data & 0x7f); // Take last 7 bits
+        (*numOfBytes) ++;
+    }
+
+    return size;
+}
+
+status_t MPEG4Extractor::parseDrmSINF(off_t *offset, off_t data_offset) {
+    uint8_t updateIdTag;
+    if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+        return ERROR_IO;
+    }
+    data_offset ++;
+
+    if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t numOfBytes;
+    int32_t size = readSize(data_offset, mDataSource, &numOfBytes);
+    if (size < 0) {
+        return ERROR_IO;
+    }
+    int32_t classSize = size;
+    data_offset += numOfBytes;
+
+    while(size >= 11 ) {
+        uint8_t descriptorTag;
+        if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) {
+            return ERROR_IO;
+        }
+        data_offset ++;
+
+        if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) {
+            return ERROR_MALFORMED;
+        }
+
+        uint8_t buffer[8];
+        //ObjectDescriptorID and ObjectDescriptor url flag
+        if (mDataSource->readAt(data_offset, buffer, 2) < 2) {
+            return ERROR_IO;
+        }
+        data_offset += 2;
+
+        if ((buffer[1] >> 5) & 0x0001) { //url flag is set
+            return ERROR_MALFORMED;
+        }
+
+        if (mDataSource->readAt(data_offset, buffer, 8) < 8) {
+            return ERROR_IO;
+        }
+        data_offset += 8;
+
+        if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1])
+                || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) {
+            return ERROR_MALFORMED;
+        }
+
+        SINF *sinf = new SINF;
+        sinf->trackID = U16_AT(&buffer[3]);
+        sinf->IPMPDescriptorID = buffer[7];
+        sinf->next = mFirstSINF;
+        mFirstSINF = sinf;
+
+        size -= (8 + 2 + 1);
+    }
+
+    if (size != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) {
+        return ERROR_IO;
+    }
+    data_offset ++;
+
+    if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) {
+        return ERROR_MALFORMED;
+    }
+
+    size = readSize(data_offset, mDataSource, &numOfBytes);
+    if (size < 0) {
+        return ERROR_IO;
+    }
+    classSize = size;
+    data_offset += numOfBytes;
+
+    while (size > 0) {
+        uint8_t tag;
+        int32_t dataLen;
+        if (mDataSource->readAt(data_offset, &tag, 1) < 1) {
+            return ERROR_IO;
+        }
+        data_offset ++;
+
+        if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) {
+            uint8_t id;
+            dataLen = readSize(data_offset, mDataSource, &numOfBytes);
+            if (dataLen < 0) {
+                return ERROR_IO;
+            } else if (dataLen < 4) {
+                return ERROR_MALFORMED;
+            }
+            data_offset += numOfBytes;
+
+            if (mDataSource->readAt(data_offset, &id, 1) < 1) {
+                return ERROR_IO;
+            }
+            data_offset ++;
+
+            SINF *sinf = mFirstSINF;
+            while (sinf && (sinf->IPMPDescriptorID != id)) {
+                sinf = sinf->next;
+            }
+            if (sinf == NULL) {
+                return ERROR_MALFORMED;
+            }
+            sinf->len = dataLen - 3;
+            sinf->IPMPData = new char[sinf->len];
+
+            if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
+                return ERROR_IO;
+            }
+            data_offset += sinf->len;
+
+            size -= (dataLen + numOfBytes + 1);
+        }
+    }
+
+    if (size != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    return UNKNOWN_ERROR;  // Return a dummy error.
+}
+
 static void MakeFourCCString(uint32_t x, char *s) {
     s[0] = x >> 24;
     s[1] = (x >> 16) & 0xff;
@@ -572,7 +755,11 @@
             } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                 mHaveMetadata = true;
 
-                return UNKNOWN_ERROR;  // Return a dummy error.
+                if (!mIsDrm) {
+                    return UNKNOWN_ERROR;  // Return a dummy error.
+                } else {
+                    return OK;
+                }
             }
             break;
         }
@@ -1020,6 +1207,20 @@
             break;
         }
 
+        case FOURCC('m', 'd', 'a', 't'):
+        {
+            if (!mIsDrm) {
+                *offset += chunk_size;
+                break;
+            }
+
+            if (chunk_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            return parseDrmSINF(offset, data_offset);
+        }
+
         default:
         {
             *offset += chunk_size;
@@ -1069,6 +1270,8 @@
         duration = U32_AT(&buffer[20]);
     }
 
+    mLastTrack->meta->setInt32(kKeyTrackID, id);
+
     size_t matrixOffset = dynSize + 16;
     int32_t a00 = U32_AT(&buffer[matrixOffset]);
     int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
@@ -1712,9 +1915,15 @@
     } else {
         // Whole NAL units are returned but each fragment is prefixed by
         // the start code (0x00 00 00 01).
-
-        ssize_t num_bytes_read =
-            mDataSource->readAt(offset, mSrcBuffer, size);
+        ssize_t num_bytes_read = 0;
+        int32_t drm = 0;
+        bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
+        if (usesDRM) {
+            num_bytes_read =
+                mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
+        } else {
+            num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
+        }
 
         if (num_bytes_read < (ssize_t)size) {
             mBuffer->release();
@@ -1723,40 +1932,46 @@
             return ERROR_IO;
         }
 
-        uint8_t *dstData = (uint8_t *)mBuffer->data();
-        size_t srcOffset = 0;
-        size_t dstOffset = 0;
+        if (usesDRM) {
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, size);
 
-        while (srcOffset < size) {
-            CHECK(srcOffset + mNALLengthSize <= size);
-            size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
-            srcOffset += mNALLengthSize;
+        } else {
+            uint8_t *dstData = (uint8_t *)mBuffer->data();
+            size_t srcOffset = 0;
+            size_t dstOffset = 0;
 
-            if (srcOffset + nalLength > size) {
-                mBuffer->release();
-                mBuffer = NULL;
+            while (srcOffset < size) {
+                CHECK(srcOffset + mNALLengthSize <= size);
+                size_t nalLength = parseNALSize(&mSrcBuffer[srcOffset]);
+                srcOffset += mNALLengthSize;
 
-                return ERROR_MALFORMED;
+                if (srcOffset + nalLength > size) {
+                    mBuffer->release();
+                    mBuffer = NULL;
+
+                    return ERROR_MALFORMED;
+                }
+
+                if (nalLength == 0) {
+                    continue;
+                }
+
+                CHECK(dstOffset + 4 <= mBuffer->size());
+
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 0;
+                dstData[dstOffset++] = 1;
+                memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+                srcOffset += nalLength;
+                dstOffset += nalLength;
             }
-
-            if (nalLength == 0) {
-                continue;
-            }
-
-            CHECK(dstOffset + 4 <= mBuffer->size());
-
-            dstData[dstOffset++] = 0;
-            dstData[dstOffset++] = 0;
-            dstData[dstOffset++] = 0;
-            dstData[dstOffset++] = 1;
-            memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
-            srcOffset += nalLength;
-            dstOffset += nalLength;
+            CHECK_EQ(srcOffset, size);
+            CHECK(mBuffer != NULL);
+            mBuffer->set_range(0, dstOffset);
         }
-        CHECK_EQ(srcOffset, size);
 
-        CHECK(mBuffer != NULL);
-        mBuffer->set_range(0, dstOffset);
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt64(
                 kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 8a5fb11..965c370 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -24,6 +24,7 @@
 #include "include/WAVExtractor.h"
 #include "include/OggExtractor.h"
 #include "include/MPEG2TSExtractor.h"
+#include "include/DRMExtractor.h"
 
 #include "matroska/MatroskaExtractor.h"
 
@@ -63,6 +64,18 @@
              mime, confidence);
     }
 
+    if (!strncmp(mime, "drm", 3)) {
+        const char *originalMime = strrchr(mime, '+') + 1;
+
+        if (!strncmp(mime, "drm+es_based", 12)) {
+            return new DRMExtractor(source, originalMime);
+        } else if (!strncmp(mime, "drm+container_based", 19)) {
+            mime = originalMime;
+        } else {
+            return NULL;
+        }
+    }
+
     if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
             || !strcasecmp(mime, "audio/mp4")) {
         return new MPEG4Extractor(source);
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 6d4fbcb..af247d5 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -58,7 +58,7 @@
         path->setTo(slashPos);
     }
 
-    char *colonPos = strchr(host->string(), ':');
+    const char *colonPos = strchr(host->string(), ':');
 
     if (colonPos != NULL) {
         unsigned long x;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 10c6e41..7a600d7 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -127,10 +127,11 @@
             || !strcasecmp(extension, ".rtttl")
             || !strcasecmp(extension, ".rtx")
             || !strcasecmp(extension, ".ota")) {
-        return HandleMIDI(path, &client);
-    }
-
-    if (mRetriever->setDataSource(path) == OK) {
+        status_t status = HandleMIDI(path, &client);
+        if (status != OK) {
+            return status;
+        }
+    } else if (mRetriever->setDataSource(path) == OK) {
         const char *value;
         if ((value = mRetriever->extractMetadata(
                         METADATA_KEY_MIMETYPE)) != NULL) {
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index a68c641..90f3d6d 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -96,7 +96,7 @@
         out->setTo(baseURL);
         out->append(url);
     } else {
-        char *slashPos = strrchr(baseURL, '/');
+        const char *slashPos = strrchr(baseURL, '/');
 
         if (slashPos > &baseURL[6]) {
             out->setTo(baseURL, slashPos - baseURL);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index c059e60..e352928 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -26,6 +26,7 @@
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/TimeSource.h>
 #include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
 
 namespace android {
 
@@ -41,6 +42,9 @@
 struct ARTPSession;
 struct UDPPusher;
 
+class DrmManagerClinet;
+class DecryptHandle;
+
 struct AwesomeRenderer : public RefBase {
     AwesomeRenderer() {}
 
@@ -216,6 +220,9 @@
         }
     } *mSuspensionState;
 
+    DrmManagerClient *mDrmManagerClient;
+    DecryptHandle *mDecryptHandle;
+
     status_t setDataSource_l(
             const char *uri,
             const KeyedVector<String8, String8> *headers = NULL);
diff --git a/media/libstagefright/include/DRMExtractor.h b/media/libstagefright/include/DRMExtractor.h
new file mode 100644
index 0000000..cafc812
--- /dev/null
+++ b/media/libstagefright/include/DRMExtractor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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_EXTRACTOR_H_
+
+#define DRM_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+#include <drm/DrmManagerClient.h>
+
+namespace android {
+
+struct AMessage;
+class DataSource;
+class SampleTable;
+class String8;
+class DecryptHandle;
+
+class DRMExtractor : public MediaExtractor {
+public:
+    DRMExtractor(const sp<DataSource> &source, const char *mime);
+
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+    virtual sp<MetaData> getMetaData();
+
+protected:
+    virtual ~DRMExtractor();
+
+private:
+    sp<DataSource> mDataSource;
+
+    sp<MediaExtractor> mOriginalExtractor;
+    DecryptHandle* mDecryptHandle;
+
+    DRMExtractor(const DRMExtractor &);
+    DRMExtractor &operator=(const DRMExtractor &);
+};
+
+bool SniffDRM(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+            sp<AMessage> *);
+
+}  // namespace android
+
+#endif  // DRM_EXTRACTOR_H_
+
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 2610b0e..bc2e4dc 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -39,6 +39,10 @@
 
     virtual sp<MetaData> getMetaData();
 
+    // for DRM
+    virtual void setDrmFlag(bool flag);
+    virtual char* getDrmTrackInfo(size_t trackID, int *len);
+
 protected:
     virtual ~MPEG4Extractor();
 
@@ -71,6 +75,19 @@
 
     static status_t verifyTrack(Track *track);
 
+    struct SINF {
+        SINF *next;
+        uint16_t trackID;
+        uint8_t IPMPDescriptorID;
+        ssize_t len;
+        char *IPMPData;
+    };
+
+    SINF *mFirstSINF;
+
+    bool mIsDrm;
+    status_t parseDrmSINF(off_t *offset, off_t data_offset);
+
     status_t parseTrackHeader(off_t data_offset, off_t data_size);
 
     MPEG4Extractor(const MPEG4Extractor &);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index aa2618e..f03f7a2 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -201,7 +201,7 @@
     AString format;
     getFormat(index, &format);
 
-    char *lastSpacePos = strrchr(format.c_str(), ' ');
+    const char *lastSpacePos = strrchr(format.c_str(), ' ');
     CHECK(lastSpacePos != NULL);
 
     char *end;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 7bf534d..72a2fdb 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -1197,7 +1197,7 @@
             out->setTo(baseURL);
             out->append(url);
         } else {
-            char *slashPos = strrchr(baseURL, '/');
+            const char *slashPos = strrchr(baseURL, '/');
 
             if (slashPos > &baseURL[6]) {
                 out->setTo(baseURL, slashPos - baseURL);
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 8aaa325..433f1f7 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -975,7 +975,6 @@
 {
     int32_t* const outTemp = state->outputTemp;
     const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
-    memset(outTemp, 0, size);
 
     size_t numFrames = state->frameCount;
 
@@ -997,6 +996,7 @@
         }
         e0 &= ~(e1);
         int32_t *out = t1.mainBuffer;
+        memset(outTemp, 0, size);
         while (e1) {
             const int i = 31 - __builtin_clz(e1);
             e1 &= ~(1<<i);