DRM framework support:
- add a sniffer for DRM files
- add DRMSource and DRMExtractor for es_based DRM
- add pread in FileSource.cpp for container_based DRM
- add native DRM framework API calls in the player for
  DRM audio/video playback

Change-Id: I4b9ef19165c9b4f44ff40eeededb9a665e78a90f
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 6f7dc38..cd19eae 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 {
 
@@ -67,6 +68,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 73d0f77..102885f 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -39,6 +39,8 @@
 
     // Not technically an error.
     INFO_FORMAT_CHANGED    = MEDIA_ERROR_BASE - 12,
+
+    ERROR_NO_LICENSE       = MEDIA_ERROR_BASE - 13,
 };
 
 }  // namespace android
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 21338ca..0fbb422 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -54,6 +54,12 @@
     // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | 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 dc2bd50..d1b5f27 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -50,6 +50,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/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 81f995b..cf5c3ca 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -23,6 +23,7 @@
         CachingDataSource.cpp     \
         CameraSource.cpp          \
         DataSource.cpp            \
+        DRMExtractor.cpp          \
         FileSource.cpp            \
         HTTPDataSource.cpp        \
         HTTPStream.cpp            \
@@ -61,7 +62,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 274dad9..7bbc9eb 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -187,7 +187,8 @@
       mExtractorFlags(0),
       mLastVideoBuffer(NULL),
       mVideoBuffer(NULL),
-      mSuspensionState(NULL) {
+      mSuspensionState(NULL),
+      mDecryptHandle(NULL) {
     CHECK_EQ(mClient.connect(), OK);
 
     DataSource::RegisterDefaultSniffers();
@@ -286,6 +287,17 @@
         return UNKNOWN_ERROR;
     }
 
+    dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
+    if (mDecryptHandle != NULL) {
+        if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) {
+            if (DecryptApiType::CONTAINER_BASED == mDecryptHandle->decryptApiType) {
+                mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
+            }
+        } else {
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+        }
+    }
+
     return setDataSource_l(extractor);
 }
 
@@ -316,6 +328,11 @@
     }
 
     mExtractorFlags = extractor->flags();
+    if (mDecryptHandle != NULL) {
+        if (DecryptApiType::ELEMENTARY_STREAM_BASED == mDecryptHandle->decryptApiType) {
+            mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
+        }
+    }
 
     return OK;
 }
@@ -326,6 +343,15 @@
 }
 
 void AwesomePlayer::reset_l() {
+    if (mDecryptHandle != NULL) {
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::STOP, 0);
+            mDrmManagerClient->consumeRights(mDecryptHandle,
+                    Action::PLAY, false);
+            mDecryptHandle = NULL;
+            mDrmManagerClient = NULL;
+    }
+
     if (mFlags & PREPARING) {
         mFlags |= PREPARE_CANCELLED;
         if (mConnectingDataSource != NULL) {
@@ -568,6 +594,13 @@
         seekTo_l(0);
     }
 
+    if (mDecryptHandle != NULL) {
+        int64_t position;
+        getPosition(&position);
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::START, position / 1000);
+    }
+
     return OK;
 }
 
@@ -631,6 +664,11 @@
 
     mFlags &= ~PLAYING;
 
+    if (mDecryptHandle != NULL) {
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::PAUSE, 0);
+    }
+
     return OK;
 }
 
@@ -727,6 +765,13 @@
         mWatchForAudioSeekComplete = true;
         mWatchForAudioEOS = true;
         mSeekNotificationSent = false;
+
+        if (mDecryptHandle != NULL) {
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::PAUSE, 0);
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::START, mSeekTimeUs / 1000);
+        }
     }
 }
 
@@ -919,6 +964,13 @@
         mFlags |= FIRST_FRAME;
         mSeeking = false;
         mSeekNotificationSent = false;
+
+        if (mDecryptHandle != NULL) {
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::PAUSE, 0);
+            mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                    Playback::START, timeUs / 1000);
+        }
     }
 
     if (mFlags & FIRST_FRAME) {
@@ -1137,6 +1189,17 @@
         return UNKNOWN_ERROR;
     }
 
+    dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
+    if (mDecryptHandle != NULL) {
+        if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) {
+            if (DecryptApiType::CONTAINER_BASED == mDecryptHandle->decryptApiType) {
+                mDrmManagerClient->consumeRights(mDecryptHandle, Action::PLAY, true);
+            }
+        } else {
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+        }
+    }
+
     if (dataSource->flags() & DataSource::kWantsPrefetching) {
         mPrefetcher = new Prefetcher;
     }
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
new file mode 100644
index 0000000..8aa5bb4
--- /dev/null
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -0,0 +1,302 @@
+/*
+ * 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) {
+    {
+        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 a66f86b..d7dd4e5 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -19,6 +19,7 @@
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
 #include "include/OggExtractor.h"
+#include "include/DRMExtractor.h"
 
 #include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/DataSource.h>
@@ -94,6 +95,7 @@
     RegisterSniffer(SniffAMR);
     RegisterSniffer(SniffWAV);
     RegisterSniffer(SniffOgg);
+    RegisterSniffer(SniffDRM);
 }
 
 // static
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index b6f1af2..6b85048 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 {
@@ -57,13 +77,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) {
@@ -79,4 +104,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 a41b2f4..d49696a 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;
@@ -563,7 +746,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;
         }
@@ -618,6 +805,7 @@
                 height = U32_AT(&buffer[80]);
             }
 
+            mLastTrack->meta->setInt32(kKeyTrackID, id);
             *offset += chunk_size;
             break;
         }
@@ -1050,6 +1238,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;
@@ -1576,9 +1778,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();
@@ -1587,40 +1795,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 513f49c..a8019ee 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -23,6 +23,7 @@
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
 #include "include/OggExtractor.h"
+#include "include/DRMExtractor.h"
 
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
@@ -57,6 +58,18 @@
              mime, confidence);
     }
 
+    if (!strncmp(mime, "drm", 3)) {
+        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/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 9455743..f0fd0f2 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -25,6 +25,7 @@
 #include <media/stagefright/HTTPDataSource.h>
 #include <media/stagefright/OMXClient.h>
 #include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
 
 namespace android {
 
@@ -35,6 +36,8 @@
 struct MediaSource;
 struct Prefetcher;
 struct TimeSource;
+class DrmManagerClinet;
+class DecryptHandle;
 
 struct AwesomeRenderer : public RefBase {
     AwesomeRenderer() {}
@@ -195,6 +198,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..01e226e
--- /dev/null
+++ b/media/libstagefright/include/DRMExtractor.h
@@ -0,0 +1,59 @@
+/*
+ * 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 {
+
+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);
+
+}  // namespace android
+
+#endif  // DRM_EXTRACTOR_H_
+
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index c8663d5..849bc89 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -38,6 +38,10 @@
 
     virtual sp<MetaData> getMetaData();
 
+    // for DRM
+    virtual void setDrmFlag(bool flag);
+    virtual char* getDrmTrackInfo(size_t trackID, int *len);
+
 protected:
     virtual ~MPEG4Extractor();
 
@@ -70,6 +74,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);
+
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
 };